diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
22 files changed, 569 insertions, 239 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index df7c4df1868..54e22cc3d7f 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -89,3 +89,11 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { diag } } + +#[derive(Diagnostic)] +#[diag(trait_selection_inherent_projection_normalization_overflow)] +pub struct InherentProjectionNormalizationOverflow { + #[primary_span] + pub span: Span, + pub ty: String, +} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index bacb0e32efc..25cc82f01d5 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate; +use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::TypeFoldable; @@ -87,7 +88,9 @@ pub(super) enum CandidateSource { } /// Methods used to assemble candidates for either trait or projection goals. -pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { +pub(super) trait GoalKind<'tcx>: + TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display +{ fn self_ty(self) -> Ty<'tcx>; fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; @@ -96,6 +99,17 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + // Try equating an assumption predicate against a goal's predicate. If it + // holds, then execute the `then` callback, which should do any additional + // work, then produce a response (typically by executing + // [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx>; + // Consider a clause, which consists of a "assumption" and some "requirements", // to satisfy a goal. If the requirements hold, then attempt to satisfy our // goal by equating it with the assumption. @@ -104,7 +118,26 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, - ) -> QueryResult<'tcx>; + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + ecx.add_goals(requirements); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + /// Consider a bound originating from the item bounds of an alias. For this we + /// require that the well-formed requirements of the self type of the goal + /// are "satisfied from the param-env". + /// See [`EvalCtxt::validate_alias_bound_self_from_param_env`]. + fn consider_alias_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + ecx.validate_alias_bound_self_from_param_env(goal) + }) + } // Consider a clause specifically for a `dyn Trait` self type. This requires // additionally checking all of the supertraits and object bounds to hold, @@ -113,7 +146,25 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx>; + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + let tcx = ecx.tcx(); + let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { + bug!("expected object type in `consider_object_bound_candidate`"); + }; + ecx.add_goals( + structural_traits::predicates_for_object_candidate( + &ecx, + goal.param_env, + goal.predicate.trait_ref(tcx), + bounds, + ) + .into_iter() + .map(|pred| goal.with(tcx, pred)), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, @@ -463,7 +514,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs) { - match G::consider_implied_clause(self, goal, assumption, []) { + match G::consider_alias_bound_candidate(self, goal, assumption) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::AliasBound, result }) } @@ -472,6 +523,105 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + /// Check that we are allowed to use an alias bound originating from the self + /// type of this goal. This means something different depending on the self type's + /// alias kind. + /// + /// * Projection: Given a goal with a self type such as `<Ty as Trait>::Assoc`, + /// we require that the bound `Ty: Trait` can be proven using either a nested alias + /// bound candidate, or a param-env candidate. + /// + /// * Opaque: The param-env must be in `Reveal::UserFacing` mode. Otherwise, + /// the goal should be proven by using the hidden type instead. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn validate_alias_bound_self_from_param_env<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + ) -> QueryResult<'tcx> { + match *goal.predicate.self_ty().kind() { + ty::Alias(ty::Projection, projection_ty) => { + let mut param_env_candidates = vec![]; + let self_trait_ref = projection_ty.trait_ref(self.tcx()); + + if self_trait_ref.self_ty().is_ty_var() { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + + let trait_goal: Goal<'_, ty::TraitPredicate<'tcx>> = goal.with( + self.tcx(), + ty::TraitPredicate { + trait_ref: self_trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }, + ); + + self.assemble_param_env_candidates(trait_goal, &mut param_env_candidates); + // FIXME: We probably need some sort of recursion depth check here. + // Can't come up with an example yet, though, and the worst case + // we can have is a compiler stack overflow... + self.assemble_alias_bound_candidates(trait_goal, &mut param_env_candidates); + + // FIXME: We must also consider alias-bound candidates for a peculiar + // class of built-in candidates that I'll call "defaulted" built-ins. + // + // For example, we always know that `T: Pointee` is implemented, but + // we do not always know what `<T as Pointee>::Metadata` actually is, + // similar to if we had a user-defined impl with a `default type ...`. + // For these traits, since we're not able to always normalize their + // associated types to a concrete type, we must consider their alias bounds + // instead, so we can prove bounds such as `<T as Pointee>::Metadata: Copy`. + self.assemble_alias_bound_candidates_for_builtin_impl_default_items( + trait_goal, + &mut param_env_candidates, + ); + + self.merge_candidates(param_env_candidates) + } + ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() { + Reveal::UserFacing => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + Reveal::All => return Err(NoSolution), + }, + _ => bug!("only expected to be called on alias tys"), + } + } + + /// Assemble a subset of builtin impl candidates for a class of candidates called + /// "defaulted" built-in traits. + /// + /// For example, we always know that `T: Pointee` is implemented, but we do not + /// always know what `<T as Pointee>::Metadata` actually is! See the comment in + /// [`EvalCtxt::validate_alias_bound_self_from_param_env`] for more detail. + #[instrument(level = "debug", skip_all)] + fn assemble_alias_bound_candidates_for_builtin_impl_default_items<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let lang_items = self.tcx().lang_items(); + let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + + // You probably shouldn't add anything to this list unless you + // know what you're doing. + let result = if lang_items.pointee_trait() == Some(trait_def_id) { + G::consider_builtin_pointee_candidate(self, goal) + } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + G::consider_builtin_discriminant_kind_candidate(self, goal) + } else { + Err(NoSolution) + }; + + match result { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) + } + Err(NoSolution) => (), + } + } + #[instrument(level = "debug", skip_all)] fn assemble_object_bound_candidates<G: GoalKind<'tcx>>( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 996dc329dcb..0ede32c753c 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -33,7 +33,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e5d51064c8d..d3228074421 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -56,11 +56,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { self.trait_def_id(tcx) } - fn consider_implied_clause( + fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() && poly_projection_pred.projection_def_id() == goal.predicate.def_id() @@ -75,49 +75,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { )?; ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) .expect("expected goal term to be fully unconstrained"); - ecx.add_goals(requirements); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } else { - Err(NoSolution) - } - } - - fn consider_object_bound_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx> { - if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() - && poly_projection_pred.projection_def_id() == goal.predicate.def_id() - { - ecx.probe(|ecx| { - let tcx = ecx.tcx(); - - let assumption_projection_pred = - ecx.instantiate_binder_with_infer(poly_projection_pred); - ecx.eq( - goal.param_env, - goal.predicate.projection_ty, - assumption_projection_pred.projection_ty, - )?; - - let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { - bug!("expected object type in `consider_object_bound_candidate`"); - }; - ecx.add_goals( - structural_traits::predicates_for_object_candidate( - &ecx, - goal.param_env, - goal.predicate.projection_ty.trait_ref(tcx), - bounds, - ) - .into_iter() - .map(|pred| goal.with(tcx, pred)), - ); - ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + then(ecx) }) } else { Err(NoSolution) @@ -166,10 +124,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }; if !assoc_def.item.defaultness(tcx).has_value() { - tcx.sess.delay_span_bug( + let guar = tcx.sess.delay_span_bug( tcx.def_span(assoc_def.item.def_id), "missing value for assoc item in impl", ); + let error_term = match assoc_def.item.kind { + ty::AssocKind::Const => tcx + .const_error( + tcx.type_of(goal.predicate.def_id()) + .subst(tcx, goal.predicate.projection_ty.substs), + guar, + ) + .into(), + ty::AssocKind::Type => tcx.ty_error(guar).into(), + ty::AssocKind::Fn => unreachable!(), + }; + ecx.eq(goal.param_env, goal.predicate.term, error_term) + .expect("expected goal term to be fully unconstrained"); + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } // Getting the right substitutions here is complex, e.g. given: diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 6c98fadd148..dcfa33ae842 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -78,11 +78,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_implied_clause( + fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() && poly_trait_pred.def_id() == goal.predicate.def_id() @@ -97,48 +97,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; - ecx.add_goals(requirements); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } else { - Err(NoSolution) - } - } - - fn consider_object_bound_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx> { - if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() - && poly_trait_pred.def_id() == goal.predicate.def_id() - && poly_trait_pred.polarity() == goal.predicate.polarity - { - // FIXME: Constness and polarity - ecx.probe(|ecx| { - let assumption_trait_pred = - ecx.instantiate_binder_with_infer(poly_trait_pred); - ecx.eq( - goal.param_env, - goal.predicate.trait_ref, - assumption_trait_pred.trait_ref, - )?; - - let tcx = ecx.tcx(); - let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { - bug!("expected object type in `consider_object_bound_candidate`"); - }; - ecx.add_goals( - structural_traits::predicates_for_object_candidate( - &ecx, - goal.param_env, - goal.predicate.trait_ref, - bounds, - ) - .into_iter() - .map(|pred| goal.with(tcx, pred)), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + then(ecx) }) } else { Err(NoSolution) @@ -655,7 +614,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 6b080a132f3..183c2401fc3 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -801,7 +801,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { span: tcx.def_span(unevaluated.def), unevaluated: unevaluated, }); - Err(ErrorHandled::Reported(reported)) + Err(ErrorHandled::Reported(reported.into())) } Err(err) => Err(err), } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index b7690f79933..969e5fa64b0 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -322,7 +322,9 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b let selcx = &mut SelectionContext::new(&infcx); let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); let (subject2, obligations) = - impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); + impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| { + ObligationCause::dummy() + }); !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) } @@ -673,7 +675,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> { | ty::RawPtr(..) | ty::Never | ty::Tuple(..) - | ty::Alias(ty::Projection, ..) => self.found_non_local_ty(ty), + | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty), ty::Param(..) => self.found_param_ty(ty), diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 9d99d30d45c..bd1ea43a78e 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -79,7 +79,7 @@ pub fn is_const_evaluatable<'tcx>( "Missing value for constant, but no error reported?", ))) } - Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e.into())), Ok(_) => Ok(()), } } @@ -147,7 +147,7 @@ pub fn is_const_evaluatable<'tcx>( Err(err) } - Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e.into())), Ok(_) => Ok(()), } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index afb64da8b61..f5f2fe54217 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -28,6 +28,7 @@ use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; +use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; @@ -796,9 +797,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_label(span, explanation); } - if let ObligationCauseCode::ObjectCastObligation(concrete_ty, obj_ty) = obligation.cause.code().peel_derives() && - Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { - self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty); + if let ObligationCauseCode::Coercion { source, target } = + *obligation.cause.code().peel_derives() + { + if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + self.suggest_borrowing_for_object_cast( + &mut err, + &root_obligation, + source, + target, + ); + } } let UnsatisfiedConst(unsatisfied_const) = self @@ -1087,17 +1096,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - OutputTypeParameterMismatch( + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { found_trait_ref, expected_trait_ref, - terr @ TypeError::CyclicTy(_), - ) => self.report_type_parameter_mismatch_cyclic_type_error( + terr: terr @ TypeError::CyclicTy(_), + }) => self.report_type_parameter_mismatch_cyclic_type_error( &obligation, found_trait_ref, expected_trait_ref, terr, ), - OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => { + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + found_trait_ref, + expected_trait_ref, + terr: _, + }) => { match self.report_type_parameter_mismatch_error( &obligation, span, @@ -1505,7 +1518,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::BindingObligation(_, _) | ObligationCauseCode::ExprItemObligation(..) | ObligationCauseCode::ExprBindingObligation(..) - | ObligationCauseCode::ObjectCastObligation(..) + | ObligationCauseCode::Coercion { .. } | ObligationCauseCode::OpaqueType ); @@ -1687,13 +1700,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::Tuple(..) => Some(10), ty::Param(..) => Some(11), ty::Alias(ty::Projection, ..) => Some(12), - ty::Alias(ty::Opaque, ..) => Some(13), - ty::Never => Some(14), - ty::Adt(..) => Some(15), - ty::Generator(..) => Some(16), - ty::Foreign(..) => Some(17), - ty::GeneratorWitness(..) => Some(18), - ty::GeneratorWitnessMIR(..) => Some(19), + ty::Alias(ty::Inherent, ..) => Some(13), + ty::Alias(ty::Opaque, ..) => Some(14), + ty::Never => Some(15), + ty::Adt(..) => Some(16), + ty::Generator(..) => Some(17), + ty::Foreign(..) => Some(18), + ty::GeneratorWitness(..) => Some(19), + ty::GeneratorWitnessMIR(..) => Some(20), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 53bf38c0a34..ea17f23434b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1442,8 +1442,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>, self_ty: Ty<'tcx>, - object_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, ) { + let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; }; let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; }; let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty); @@ -1458,7 +1459,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_suggestion( obligation.cause.span.shrink_to_lo(), format!( - "consider borrowing the value, since `&{self_ty}` can be coerced into `{object_ty}`" + "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`" ), "&", Applicability::MaybeIncorrect, @@ -2446,10 +2447,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && generator_did.is_local() // Try to avoid cycles. && !generator_within_in_progress_typeck + && let Some(generator_info) = self.tcx.mir_generator_witnesses(generator_did) { - let generator_info = &self.tcx.mir_generator_witnesses(generator_did); debug!(?generator_info); - 'find_source: for (variant, source_info) in generator_info.variant_fields.iter().zip(&generator_info.variant_source_info) { @@ -2851,30 +2851,27 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_note(tcx.def_span(item_def_id), descr); } } - ObligationCauseCode::ObjectCastObligation(concrete_ty, object_ty) => { - let (concrete_ty, concrete_file) = - self.tcx.short_ty_string(self.resolve_vars_if_possible(concrete_ty)); - let (object_ty, object_file) = - self.tcx.short_ty_string(self.resolve_vars_if_possible(object_ty)); + ObligationCauseCode::Coercion { source, target } => { + let (source, source_file) = + self.tcx.short_ty_string(self.resolve_vars_if_possible(source)); + let (target, target_file) = + self.tcx.short_ty_string(self.resolve_vars_if_possible(target)); err.note(with_forced_trimmed_paths!(format!( - "required for the cast from `{concrete_ty}` to the object type `{object_ty}`", + "required for the cast from `{source}` to `{target}`", ))); - if let Some(file) = concrete_file { + if let Some(file) = source_file { err.note(format!( - "the full name for the casted type has been written to '{}'", + "the full name for the source type has been written to '{}'", file.display(), )); } - if let Some(file) = object_file { + if let Some(file) = target_file { err.note(format!( - "the full name for the object type has been written to '{}'", + "the full name for the target type has been written to '{}'", file.display(), )); } } - ObligationCauseCode::Coercion { source: _, target } => { - err.note(format!("required by cast to type `{}`", self.ty_to_string(target))); - } ObligationCauseCode::RepeatElementCopy { is_const_fn } => { err.note( "the `Copy` trait is required because this value will be copied for each element of the array", diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 43196d1e629..2f85c32b575 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -615,7 +615,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { (Err(ErrorHandled::Reported(reported)), _) | (_, Err(ErrorHandled::Reported(reported))) => ProcessResult::Error( CodeSelectionError(SelectionError::NotConstEvaluatable( - NotConstEvaluatable::Error(reported), + NotConstEvaluatable::Error(reported.into()), )), ), (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index d8e5725d3ca..223cdc48f0b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -26,6 +26,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; +use rustc_middle::query::Providers; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; @@ -49,12 +50,15 @@ pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::ObjectSafetyViolation; -pub use self::project::{normalize_projection_type, NormalizeExt}; +pub use self::project::NormalizeExt; +pub use self::project::{normalize_inherent_projection, normalize_projection_type}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; -pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; +pub use self::specialize::{ + specialization_graph, translate_substs, translate_substs_with_cause, OverlapError, +}; pub use self::structural_match::{ search_for_adt_const_param_violation, search_for_structural_match_violation, }; @@ -495,10 +499,10 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI false } -pub fn provide(providers: &mut ty::query::Providers) { +pub fn provide(providers: &mut Providers) { object_safety::provide(providers); vtable::provide(providers); - *providers = ty::query::Providers { + *providers = Providers { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, subst_and_check_impossible_predicates, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 06d9c10386e..c81bf6ebc2e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -16,6 +16,7 @@ use crate::traits::{self, Obligation, ObligationCause}; use rustc_errors::{DelayDm, FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::query::Providers; use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, @@ -947,7 +948,6 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>( }) } -pub fn provide(providers: &mut ty::query::Providers) { - *providers = - ty::query::Providers { object_safety_violations, check_is_object_safe, ..*providers }; +pub fn provide(providers: &mut Providers) { + *providers = Providers { object_safety_violations, check_is_object_safe, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8c74860cdf3..8e684b7ac23 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -16,6 +16,7 @@ use super::{ }; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::TypeErrCtxtExt as _; @@ -370,10 +371,14 @@ pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( reveal: Reveal, ) -> bool { match reveal { - Reveal::UserFacing => value - .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION), + Reveal::UserFacing => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION, + ), Reveal::All => value.has_type_flags( ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT | ty::TypeFlags::HAS_TY_OPAQUE | ty::TypeFlags::HAS_CT_PROJECTION, ), @@ -616,6 +621,51 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx ); normalized_ty } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` + // here like `ty::Projection`? + normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } } } @@ -1204,6 +1254,115 @@ fn normalize_to_error<'a, 'tcx>( Normalized { value: new_value, obligations: vec![trait_obligation] } } +/// Confirm and normalize the given inherent projection. +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] +pub fn normalize_inherent_projection<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> Ty<'tcx> { + let tcx = selcx.tcx(); + + if !tcx.recursion_limit().value_within_limit(depth) { + // Halt compilation because it is important that overflows never be masked. + tcx.sess.emit_fatal(InherentProjectionNormalizationOverflow { + span: cause.span, + ty: alias_ty.to_string(), + }); + } + + let substs = compute_inherent_assoc_ty_substs( + selcx, + param_env, + alias_ty, + cause.clone(), + depth, + obligations, + ); + + // Register the obligations arising from the impl and from the associated type itself. + let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, substs); + for (predicate, span) in predicates { + let predicate = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + predicate, + obligations, + ); + + let nested_cause = ObligationCause::new( + cause.span, + cause.body_id, + // FIXME(inherent_associated_types): Since we can't pass along the self type to the + // cause code, inherent projections will be printed with identity substitutions in + // diagnostics which is not ideal. + // Consider creating separate cause codes for this specific situation. + if span.is_dummy() { + super::ItemObligation(alias_ty.def_id) + } else { + super::BindingObligation(alias_ty.def_id, span) + }, + ); + + obligations.push(Obligation::with_depth( + tcx, + nested_cause, + depth + 1, + param_env, + predicate, + )); + } + + let ty = tcx.type_of(alias_ty.def_id).subst(tcx, substs); + + let mut ty = selcx.infcx.resolve_vars_if_possible(ty); + if ty.has_projections() { + ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); + } + + ty +} + +pub fn compute_inherent_assoc_ty_substs<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> ty::SubstsRef<'tcx> { + let tcx = selcx.tcx(); + + let impl_def_id = tcx.parent(alias_ty.def_id); + let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id); + + let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); + let impl_ty = + normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations); + + // Infer the generic parameters of the impl by unifying the + // impl type with the self type of the projection. + let self_ty = alias_ty.self_ty(); + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { + Ok(mut ok) => obligations.append(&mut ok.obligations), + Err(_) => { + tcx.sess.delay_span_bug( + cause.span, + format!( + "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" + ), + ); + } + } + + alias_ty.rebase_substs_onto_impl(impl_substs, tcx) +} + enum Projected<'tcx> { Progress(Progress<'tcx>), NoProgress(ty::Term<'tcx>), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a986a9b6a71..8bf934cb78a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -257,11 +257,11 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> ty::Opaque => ty.try_super_fold_with(self)?, - ty::Projection => { + ty::Projection | ty::Inherent => { // See note in `rustc_trait_selection::traits::project` - let tcx = self.infcx.tcx; let infcx = self.infcx; + let tcx = infcx.tcx; // Just an optimization: When we don't have escaping bound vars, // we don't need to replace them with placeholders. let (data, maps) = if data.has_escaping_bound_vars() { @@ -276,12 +276,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> let mut orig_values = OriginalQueryValues::default(); // HACK(matthewjasper) `'static` is special-cased in selection, // so we cannot canonicalize it. - let c_data = self - .infcx + let c_data = infcx .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values); debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); - let result = tcx.normalize_projection_ty(c_data)?; + let result = match kind { + ty::Projection => tcx.normalize_projection_ty(c_data), + ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), + _ => unreachable!(), + }?; // We don't expect ambiguity. if result.is_ambiguous() { // Rustdoc normalizes possibly not well-formed types, so only @@ -294,8 +297,8 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> } return Err(NoSolution); } - let InferOk { value: result, obligations } = - self.infcx.instantiate_query_response_and_region_obligations( + let InferOk { value: result, obligations } = infcx + .instantiate_query_response_and_region_obligations( self.cause, self.param_env, &orig_values, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 33f502f8182..a8fb55df2d3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // this trait and type. } ty::Param(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) | ty::Bound(..) => { // In these cases, we don't know what the actual diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 422285d9474..6a648294efd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -10,6 +10,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; +use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::ty::{ self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, @@ -28,9 +29,9 @@ use crate::traits::{ ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, ImplSourceConstDestructData, ImplSourceFnPointerData, ImplSourceFutureData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData, - ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation, - Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, - SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, + ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, Obligation, + ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, SelectionError, + TraitNotObjectSafe, TraitObligation, Unimplemented, }; use super::BuiltinImplConditions; @@ -811,7 +812,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_poly_trait_refs( &mut self, obligation: &TraitObligation<'tcx>, - expected_trait_ref: ty::PolyTraitRef<'tcx>, + self_ty_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let obligation_trait_ref = obligation.predicate.to_poly_trait_ref(); // Normalize the obligation and expected trait refs together, because why not @@ -822,7 +823,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - (obligation_trait_ref, expected_trait_ref), + (obligation_trait_ref, self_ty_trait_ref), ) }); @@ -834,7 +835,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.extend(nested); obligations }) - .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) + .map_err(|terr| { + OutputTypeParameterMismatch(Box::new(SelectionOutputTypeParameterMismatch { + expected_trait_ref: obligation_trait_ref, + found_trait_ref: expected_trait_ref, + terr, + })) + }) } fn confirm_trait_upcasting_unsize_candidate( @@ -898,16 +905,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|_| Unimplemented)?; nested.extend(obligations); - // Register one obligation for 'a: 'b. - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(source, target), - ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( tcx, - cause, + obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, obligation.predicate.rebind(outlives), @@ -998,15 +999,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested.extend(obligations); // Register one obligation for 'a: 'b. - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(source, target), - ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( tcx, - cause, + obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, obligation.predicate.rebind(outlives), @@ -1020,16 +1016,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(TraitNotObjectSafe(did)); } - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(source, target), - ); - let predicate_to_obligation = |predicate| { Obligation::with_depth( tcx, - cause.clone(), + obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, predicate, @@ -1049,7 +1039,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); // We can only make objects from sized types. - let tr = ty::TraitRef::from_lang_item(tcx, LangItem::Sized, cause.span, [source]); + let tr = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + obligation.cause.span, + [source], + ); nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx))); // If the type is `Foo + 'a`, ensure that the type @@ -1268,7 +1263,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If we have a projection type, make sure to normalize it so we replace it // with a fresh infer variable - ty::Alias(ty::Projection, ..) => { + ty::Alias(ty::Projection | ty::Inherent, ..) => { let predicate = normalize_with_depth_to( self, obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 246d3ea2ef2..b72ff5b78e4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2315,7 +2315,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); @@ -2647,14 +2647,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let predicates = predicates.instantiate_own(tcx, substs); let mut obligations = Vec::with_capacity(predicates.len()); for (index, (predicate, span)) in predicates.into_iter().enumerate() { - let cause = cause.clone().derived_cause(parent_trait_pred, |derived| { - ImplDerivedObligation(Box::new(ImplDerivedObligationCause { - derived, - impl_or_alias_def_id: def_id, - impl_def_predicate_index: Some(index), - span, - })) - }); + let cause = + if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() { + cause.clone() + } else { + cause.clone().derived_cause(parent_trait_pred, |derived| { + ImplDerivedObligation(Box::new(ImplDerivedObligationCause { + derived, + impl_or_alias_def_id: def_id, + impl_def_predicate_index: Some(index), + span, + })) + }) + }; let predicate = normalize_with_depth_to( self, param_env, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 233d35aed38..9a4b72013b8 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -83,6 +83,30 @@ pub fn translate_substs<'tcx>( source_substs: SubstsRef<'tcx>, target_node: specialization_graph::Node, ) -> SubstsRef<'tcx> { + translate_substs_with_cause( + infcx, + param_env, + source_impl, + source_substs, + target_node, + |_, _| ObligationCause::dummy(), + ) +} + +/// Like [translate_substs], but obligations from the parent implementation +/// are registered with the provided `ObligationCause`. +/// +/// This is for reporting *region* errors from those bounds. Type errors should +/// not happen because the specialization graph already checks for those, and +/// will result in an ICE. +pub fn translate_substs_with_cause<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_substs: SubstsRef<'tcx>, + target_node: specialization_graph::Node, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, +) -> SubstsRef<'tcx> { debug!( "translate_substs({:?}, {:?}, {:?}, {:?})", param_env, source_impl, source_substs, target_node @@ -99,14 +123,13 @@ pub fn translate_substs<'tcx>( return source_substs; } - fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( - |()| { + fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause) + .unwrap_or_else(|()| { bug!( "When translating substitutions from {source_impl:?} to {target_impl:?}, \ the expected specialization failed to hold" ) - }, - ) + }) } specialization_graph::Node::Trait(..) => source_trait_ref.substs, }; @@ -153,20 +176,12 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, // Create an infcx, taking the predicates of impl1 as assumptions: let infcx = tcx.infer_ctxt().build(); - let impl1_trait_ref = - match traits::fully_normalize(&infcx, ObligationCause::dummy(), penv, impl1_trait_ref) { - Ok(impl1_trait_ref) => impl1_trait_ref, - Err(_errors) => { - tcx.sess.delay_span_bug( - tcx.def_span(impl1_def_id), - format!("failed to fully normalize {impl1_trait_ref}"), - ); - impl1_trait_ref - } - }; // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() + fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id, |_, _| { + ObligationCause::dummy() + }) + .is_ok() } /// Attempt to fulfill all obligations of `target_impl` after unification with @@ -178,23 +193,41 @@ fn fulfill_implication<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, source_trait_ref: ty::TraitRef<'tcx>, + source_impl: DefId, target_impl: DefId, + error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, ) -> Result<SubstsRef<'tcx>, ()> { debug!( "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", param_env, source_trait_ref, target_impl ); + let source_trait_ref = match traits::fully_normalize( + &infcx, + ObligationCause::dummy(), + param_env, + source_trait_ref, + ) { + Ok(source_trait_ref) => source_trait_ref, + Err(_errors) => { + infcx.tcx.sess.delay_span_bug( + infcx.tcx.def_span(source_impl), + format!("failed to fully normalize {source_trait_ref}"), + ); + source_trait_ref + } + }; + let source_trait = ImplSubject::Trait(source_trait_ref); let selcx = &mut SelectionContext::new(&infcx); let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); let (target_trait, obligations) = - util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs); + util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs, error_cause); // do the impls unify? If not, no specialization. let Ok(InferOk { obligations: more_obligations, .. }) = - infcx.at(&ObligationCause::dummy(), param_env, ).eq(DefineOpaqueTypes::No,source_trait, target_trait) + infcx.at(&ObligationCause::dummy(), param_env).eq(DefineOpaqueTypes::No, source_trait, target_trait) else { debug!( "fulfill_implication: {:?} does not unify with {:?}", diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 7b7e297c64b..82f3df40198 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -197,6 +197,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, impl_substs: SubstsRef<'tcx>, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, ) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { let subject = selcx.tcx().impl_subject(impl_def_id); let subject = subject.subst(selcx.tcx(), impl_substs); @@ -208,8 +209,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( let predicates = predicates.instantiate(selcx.tcx(), impl_substs); let InferOk { value: predicates, obligations: normalization_obligations2 } = selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates); - let impl_obligations = - super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates); + let impl_obligations = super::predicates_for_generics(cause, param_env, predicates); let impl_obligations = impl_obligations .chain(normalization_obligations1.into_iter()) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index f7a3126b4aa..cc674ceee3d 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -4,6 +4,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::traits::util::PredicateSet; use rustc_infer::traits::ImplSource; +use rustc_middle::query::Providers; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; @@ -379,8 +380,8 @@ pub(crate) fn count_own_vtable_entries<'tcx>( tcx.own_existential_vtable_entries(trait_ref.def_id()).len() } -pub(super) fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { +pub(super) fn provide(providers: &mut Providers) { + *providers = Providers { own_existential_vtable_entries, vtable_entries, vtable_trait_upcasting_coercion_new_vptr_slot, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 22710c7c059..086ab32b520 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -58,15 +58,8 @@ pub fn obligations<'tcx>( GenericArgKind::Lifetime(..) => return Some(Vec::new()), }; - let mut wf = WfPredicates { - tcx: infcx.tcx, - param_env, - body_id, - span, - out: vec![], - recursion_depth, - item: None, - }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; wf.compute(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); @@ -91,7 +84,7 @@ pub fn unnormalized_obligations<'tcx>( debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id: CRATE_DEF_ID, span: DUMMY_SP, @@ -116,7 +109,7 @@ pub fn trait_obligations<'tcx>( item: &'tcx hir::Item<'tcx>, ) -> Vec<traits::PredicateObligation<'tcx>> { let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id, span, @@ -138,7 +131,7 @@ pub fn predicate_obligations<'tcx>( span: Span, ) -> Vec<traits::PredicateObligation<'tcx>> { let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id, span, @@ -190,8 +183,8 @@ pub fn predicate_obligations<'tcx>( wf.normalize(infcx) } -struct WfPredicates<'tcx> { - tcx: TyCtxt<'tcx>, +struct WfPredicates<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, span: Span, @@ -290,9 +283,9 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( } } -impl<'tcx> WfPredicates<'tcx> { +impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { @@ -325,7 +318,7 @@ impl<'tcx> WfPredicates<'tcx> { /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) { - let tcx = self.tcx; + let tcx = self.tcx(); let trait_ref = &trait_pred.trait_ref; // Negative trait predicates don't require supertraits to hold, just @@ -369,7 +362,6 @@ impl<'tcx> WfPredicates<'tcx> { self.out.extend(obligations); } - let tcx = self.tcx(); self.out.extend( trait_ref .substs @@ -436,13 +428,45 @@ impl<'tcx> WfPredicates<'tcx> { let obligations = self.nominal_obligations_without_const(data.def_id, data.substs); self.out.extend(obligations); + self.compute_projection_substs(data.substs); + } + + fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + // An inherent projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its substs are wf + // + // (*) The predicates of an inherent associated type include the + // predicates of the impl that it's contained in. + + if !data.self_ty().has_escaping_bound_vars() { + // FIXME(inherent_associated_types): Should this happen inside of a snapshot? + // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm! + let substs = traits::project::compute_inherent_assoc_ty_substs( + &mut traits::SelectionContext::new(self.infcx), + self.param_env, + data, + self.cause(traits::WellFormed(None)), + self.recursion_depth, + &mut self.out, + ); + // Inherent projection types do not require const predicates. + let obligations = self.nominal_obligations_without_const(data.def_id, substs); + self.out.extend(obligations); + } + + self.compute_projection_substs(data.substs); + } + + fn compute_projection_substs(&mut self, substs: SubstsRef<'tcx>) { let tcx = self.tcx(); let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let depth = self.recursion_depth; self.out.extend( - data.substs + substs .iter() .filter(|arg| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) @@ -464,9 +488,9 @@ impl<'tcx> WfPredicates<'tcx> { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); let trait_ref = - ty::TraitRef::from_lang_item(self.tcx, LangItem::Sized, cause.span, [subty]); + ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [subty]); self.out.push(traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -605,6 +629,10 @@ impl<'tcx> WfPredicates<'tcx> { walker.skip_current_subtree(); // Subtree handled by compute_projection. self.compute_projection(data); } + ty::Alias(ty::Inherent, data) => { + walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection. + self.compute_inherent_projection(data); + } ty::Adt(def, substs) => { // WfNominalType @@ -697,7 +725,7 @@ impl<'tcx> WfPredicates<'tcx> { // All of the requirements on type parameters // have already been checked for `impl Trait` in // return position. We do need to check type-alias-impl-trait though. - if self.tcx.is_type_alias_impl_trait(def_id) { + if self.tcx().is_type_alias_impl_trait(def_id) { let obligations = self.nominal_obligations(def_id, substs); self.out.extend(obligations); } @@ -767,15 +795,15 @@ impl<'tcx> WfPredicates<'tcx> { substs: SubstsRef<'tcx>, remap_constness: bool, ) -> Vec<traits::PredicateObligation<'tcx>> { - let predicates = self.tcx.predicates_of(def_id); + let predicates = self.tcx().predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; let mut head = predicates; while let Some(parent) = head.parent { - head = self.tcx.predicates_of(parent); + head = self.tcx().predicates_of(parent); origins.extend(iter::repeat(parent).take(head.predicates.len())); } - let predicates = predicates.instantiate(self.tcx, substs); + let predicates = predicates.instantiate(self.tcx(), substs); trace!("{:#?}", predicates); debug_assert_eq!(predicates.predicates.len(), origins.len()); @@ -788,10 +816,10 @@ impl<'tcx> WfPredicates<'tcx> { }; let cause = self.cause(code); if remap_constness { - pred = pred.without_const(self.tcx); + pred = pred.without_const(self.tcx()); } traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -856,7 +884,7 @@ impl<'tcx> WfPredicates<'tcx> { // Note: in fact we only permit builtin traits, not `Bar<'d>`, I // am looking forward to the future here. if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.tcx, data); + let implicit_bounds = object_region_bounds(self.tcx(), data); let explicit_bound = region; @@ -866,7 +894,7 @@ impl<'tcx> WfPredicates<'tcx> { let outlives = ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); self.out.push(traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, |