diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 1037a49acdf32..3a15f05948d37 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1519,6 +1519,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Clause::RegionOutlives(_) | ty::Clause::ConstArgHasType(..) => bug!(), }, ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(_, _, _) diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index e84da2519ae81..c0dd777064515 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -541,6 +541,7 @@ fn trait_predicate_kind<'tcx>( | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) | ty::PredicateKind::Clause(ty::Clause::Projection(_)) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::Subtype(_) diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 7ce48fe1c012f..cc02a55c9719d 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -56,15 +56,20 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) => (), + ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => (), + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => { + bug!( + "`explicit_predicates_of` should only deal with clauses: {predicate:?}" + ) + } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index fb56b7e74cb54..ea3193b1e387f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -670,6 +670,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index b7b11ff8942f9..f68b5adceeb09 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -845,6 +845,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 8a44d5031596f..ada0faa37db52 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -26,6 +26,7 @@ pub fn explicit_outlives_bounds<'tcx>( ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Subtype(..) diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 74a78f38024e0..9644b687bcf45 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -327,14 +327,11 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { .map(|predicate| elaboratable.child(predicate)), ); } - ty::PredicateKind::TypeWellFormedFromEnv(..) => { - // Nothing to elaborate - } - ty::PredicateKind::Ambiguous => {} - ty::PredicateKind::AliasRelate(..) => { - // No - } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { + ty::PredicateKind::TypeWellFormedFromEnv(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { // Nothing to elaborate } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 7b05bff515148..18a5916243676 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1608,6 +1608,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { // Ignore projections, as they can only be global // if the trait bound is global Clause(Clause::Projection(..)) | + NormalizesTo(..) | AliasRelate(..) | // Ignore bounds that a user can't type WellFormed(..) | diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index d64875a9f00e8..2d0c53f454917 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -288,7 +288,11 @@ impl FlagComputation { self.add_ty(ty); } ty::PredicateKind::Ambiguous => {} - ty::PredicateKind::AliasRelate(t1, t2, _) => { + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => { + self.add_alias_ty(alias); + self.add_term(term); + } + ty::PredicateKind::AliasRelate(t1, t2, _direction) => { self.add_term(t1); self.add_term(t2); } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ff5d99794f1e2..99774f7b26aa8 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -537,12 +537,14 @@ impl<'tcx> Predicate<'tcx> { pub fn allow_normalization(self) -> bool { match self.kind().skip_binder() { PredicateKind::WellFormed(_) => false, + // Only used by the new solver where we should not + // normalize any goals. + PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) => false, PredicateKind::Clause(Clause::Trait(_)) | PredicateKind::Clause(Clause::RegionOutlives(_)) | PredicateKind::Clause(Clause::TypeOutlives(_)) | PredicateKind::Clause(Clause::Projection(_)) | PredicateKind::Clause(Clause::ConstArgHasType(..)) - | PredicateKind::AliasRelate(..) | PredicateKind::ObjectSafe(_) | PredicateKind::ClosureKind(_, _, _) | PredicateKind::Subtype(_) @@ -653,10 +655,19 @@ pub enum PredicateKind<'tcx> { /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. Ambiguous, + /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the alias + /// cannot be normalized in the current context. + /// + /// `Projection(::Assoc, ?x)` results in `?x == ::Assoc` while + /// `NormalizesTo(::Assoc, ?x)` results in `NoSolution`. + /// + /// Only used in the new solver. + NormalizesTo(NormalizesTo<'tcx>), + /// Separate from `Clause::Projection` which is used for normalization in new solver. /// This predicate requires two terms to be equal to eachother. /// - /// Only used for new solver + /// Only used in the new solver. AliasRelate(Term<'tcx>, Term<'tcx>, AliasRelationDirection), } @@ -1173,6 +1184,33 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { } } +/// Used by the new solver. Unlike a `ProjectionPredicate` this can only be +/// proven by actually normalizing `alias`. +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +pub struct NormalizesTo<'tcx> { + pub alias: AliasTy<'tcx>, + pub term: Term<'tcx>, +} + +impl<'tcx> NormalizesTo<'tcx> { + pub fn self_ty(self) -> Ty<'tcx> { + self.alias.self_ty() + } + + pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> NormalizesTo<'tcx> { + Self { alias: self.alias.with_self_ty(tcx, self_ty), ..self } + } + + pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + self.alias.trait_def_id(tcx) + } + + pub fn def_id(self) -> DefId { + self.alias.def_id + } +} + pub trait ToPolyTraitRef<'tcx> { fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; } @@ -1295,6 +1333,12 @@ impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { } } +impl<'tcx> ToPredicate<'tcx> for ProjectionPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(self).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(Clause::Projection(p))).to_predicate(tcx) @@ -1313,6 +1357,18 @@ impl<'tcx> ToPredicate<'tcx> for TraitPredicate<'tcx> { } } +impl<'tcx> ToPredicate<'tcx> for NormalizesTo<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateKind::NormalizesTo(self).to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for ty::Binder<'tcx, NormalizesTo<'tcx>> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + self.map_bound(|p| PredicateKind::NormalizesTo(p)).to_predicate(tcx) + } +} + impl<'tcx> Predicate<'tcx> { pub fn to_opt_poly_trait_pred(self) -> Option> { let predicate = self.kind(); @@ -1320,6 +1376,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(Clause::Trait(t)) => Some(predicate.rebind(t)), PredicateKind::Clause(Clause::Projection(..)) | PredicateKind::Clause(Clause::ConstArgHasType(..)) + | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) @@ -1341,6 +1398,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(Clause::Projection(t)) => Some(predicate.rebind(t)), PredicateKind::Clause(Clause::Trait(..)) | PredicateKind::Clause(Clause::ConstArgHasType(..)) + | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) @@ -1363,6 +1421,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(Clause::Trait(..)) | PredicateKind::Clause(Clause::ConstArgHasType(..)) | PredicateKind::Clause(Clause::Projection(..)) + | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) @@ -1381,7 +1440,8 @@ impl<'tcx> Predicate<'tcx> { let predicate = self.kind(); match predicate.skip_binder() { PredicateKind::Clause(clause) => Some(predicate.rebind(clause)), - PredicateKind::AliasRelate(..) + PredicateKind::NormalizesTo(..) + | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) | PredicateKind::WellFormed(..) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6156fdf7eac24..5cc68cd7f5058 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2836,6 +2836,10 @@ define_print_and_forward_display! { p!(print(self.term)) } + ty::NormalizesTo<'tcx> { + p!("`", print(self.alias), "` normalizes to `", print(self.term), "`") + } + ty::Term<'tcx> { match self.unpack() { ty::TermKind::Ty(ty) => p!(print(ty)), @@ -2896,6 +2900,7 @@ define_print_and_forward_display! { p!("the type `", print(ty), "` is found in the environment") } ty::PredicateKind::Ambiguous => p!("ambiguous"), + ty::PredicateKind::NormalizesTo(predicate) => p!(print(predicate)), ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index a965450d27d43..9a7a586690a51 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -165,6 +165,12 @@ impl<'tcx> fmt::Debug for ty::ProjectionPredicate<'tcx> { } } +impl<'tcx> fmt::Debug for ty::NormalizesTo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "NormalizesTo({:?}, {:?})", self.alias, self.term) + } +} + impl<'tcx> fmt::Debug for ty::Predicate<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.kind()) @@ -204,6 +210,7 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { write!(f, "TypeWellFormedFromEnv({:?})", ty) } ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"), + ty::PredicateKind::NormalizesTo(predicate) => predicate.fmt(f), ty::PredicateKind::AliasRelate(t1, t2, dir) => { write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index a51a1c9a8a436..31be1a91603ea 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -192,6 +192,7 @@ where | ty::PredicateKind::ConstEquate(_, _) | ty::PredicateKind::TypeWellFormedFromEnv(_) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) => bug!("unexpected predicate: {:?}", predicate), } } diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 66a4d36a1e5a7..737474c306c67 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -123,7 +123,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { direction: ty::AliasRelationDirection, invert: Invert, ) -> Result<(), NoSolution> { - let other = match direction { + let term = match direction { // This is purely an optimization. ty::AliasRelationDirection::Equate => other, @@ -137,11 +137,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fresh } }; - self.add_goal(Goal::new( - self.tcx(), - param_env, - ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), - )); + self.add_goal(Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term })); Ok(()) } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index e6eddf24844cf..3a64d502973f2 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -87,9 +87,7 @@ pub(super) enum CandidateSource { } /// Methods used to assemble candidates for either trait or projection goals. -pub(super) trait GoalKind<'tcx>: - TypeFoldable> + Copy + Eq + std::fmt::Display -{ +pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq { fn self_ty(self) -> Ty<'tcx>; fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; @@ -112,6 +110,9 @@ pub(super) trait GoalKind<'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. + // + // FIXME: change this function to take an actual `Clause` as assumption instead + // of a predicate. fn consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -332,7 +333,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { let tcx = self.tcx(); - let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { + let &ty::Alias(_, alias) = goal.predicate.self_ty().kind() else { return }; @@ -346,13 +347,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }, |ecx| { let normalized_ty = ecx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); + let normalizes_to_goal = + goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() }); ecx.add_goal(normalizes_to_goal); let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { debug!("self type normalization failed"); diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index a8a0e1ebfb42c..eaab9d765c539 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -95,7 +95,7 @@ pub(super) struct NestedGoals<'tcx> { /// with a fresh inference variable when we evaluate this goal. That can result /// in a trait solver cycle. This would currently result in overflow but can be /// can be unsound with more powerful coinduction in the future. - pub(super) normalizes_to_hack_goal: Option>>, + pub(super) normalizes_to_hack_goal: Option>>, /// The rest of the goals which have not yet processed or remain ambiguous. pub(super) goals: Vec>>, } @@ -332,6 +332,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } + ty::PredicateKind::NormalizesTo(predicate) => { + self.compute_normalizes_to_goal(Goal { param_env, predicate }) + } ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self .compute_alias_relate_goal(Goal { param_env, @@ -364,10 +367,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term); let unconstrained_goal = goal.with( this.tcx(), - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: goal.predicate.projection_ty, - term: unconstrained_rhs, - }), + ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, ); let (_, certainty, instantiate_goals) = @@ -397,9 +397,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // looking at the "has changed" return from evaluate_goal, // because we expect the `unconstrained_rhs` part of the predicate // to have changed -- that means we actually normalized successfully! - if goal.predicate.projection_ty - != this.resolve_vars_if_possible(goal.predicate.projection_ty) - { + if goal.predicate.alias != this.resolve_vars_if_possible(goal.predicate.alias) { has_changed = Ok(()) } @@ -504,7 +502,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// and does not occur in any other part of the predicate. pub(super) fn term_is_fully_unconstrained( &self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> bool { let term_is_infer = match goal.predicate.term.unpack() { ty::TermKind::Ty(ty) => { @@ -568,7 +566,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term }; term_is_infer - && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue() + && goal.predicate.alias.visit_with(&mut visitor).is_continue() && goal.param_env.visit_with(&mut visitor).is_continue() } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 212327448c874..aeb5dc49641b4 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -87,7 +87,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { errors.push(FulfillmentError { obligation: obligation.clone(), code: match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Projection(_)) => { + ty::PredicateKind::Clause(ty::Clause::Projection(_)) + | ty::PredicateKind::NormalizesTo(..) => { FulfillmentErrorCode::CodeProjectionError( // FIXME: This could be a `Sorts` if the term is a type MismatchedProjectionTypes { err: TypeError::Mismatch }, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index f4c29c837b884..08953c167f1b9 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -25,6 +25,7 @@ mod assembly; mod canonicalize; mod eval_ctxt; mod fulfill; +mod normalizes_to; mod opaques; mod project_goals; mod search_graph; @@ -168,7 +169,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] - fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) { + fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { assert!( self.nested_goals.normalizes_to_hack_goal.is_none(), "attempted to set the projection eq hack goal when one already exists" diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to.rs new file mode 100644 index 0000000000000..b986726203731 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to.rs @@ -0,0 +1,581 @@ +use crate::traits::specialization_graph; + +use super::assembly::{self, structural_traits}; +use super::EvalCtxt; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::specialization_graph::LeafDef; +use rustc_infer::traits::Reveal; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; +use rustc_span::{sym, DUMMY_SP}; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_normalizes_to_goal( + &mut self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + let def_id = goal.predicate.def_id(); + match self.tcx().def_kind(def_id) { + DefKind::AssocTy | DefKind::AssocConst => { + // To only compute normalization once for each projection we only + // assemble normalization candidates if the expected term is an + // unconstrained inference variable. + // + // Why: For better cache hits, since if we have an unconstrained RHS then + // there are only as many cache keys as there are (canonicalized) alias + // types in each normalizes-to goal. This also weakens inference in a + // forwards-compatible way so we don't use the value of the RHS term to + // affect candidate assembly for projections. + // + // E.g. for `::Assoc == u32` we recursively compute the goal + // `exists ::Assoc == U` and then take the resulting type for + // `U` and equate it with `u32`. This means that we don't need a separate + // projection cache in the solver, since we're piggybacking off of regular + // goal caching. + if self.term_is_fully_unconstrained(goal) { + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } + ty::AssocItemContainer::ImplContainer => { + bug!("IATs not supported here yet") + } + } + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + DefKind::AnonConst => self.normalize_anon_const(goal), + DefKind::OpaqueTy => self.normalize_opaque_type(goal), + kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn normalize_anon_const( + &mut self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + if let Some(normalized_const) = self.try_const_eval_resolve( + goal.param_env, + ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.substs), + self.tcx() + .type_of(goal.predicate.alias.def_id) + .no_bound_vars() + .expect("const ty should not rely on other generics"), + ) { + self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } +} + +impl<'tcx> assembly::GoalKind<'tcx> for ty::NormalizesTo<'tcx> { + fn self_ty(self) -> Ty<'tcx> { + self.self_ty() + } + + fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + self.alias.trait_ref(tcx) + } + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) + } + + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + self.trait_def_id(tcx) + } + + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Binder<'tcx, ty::Clause<'tcx>>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + if let Some(projection_pred) = assumption.as_projection_clause() + && projection_pred.projection_def_id() == goal.predicate.def_id() + { + ecx.probe(|ecx| { + let assumption_projection_pred = + ecx.instantiate_binder_with_infer(projection_pred); + ecx.eq( + goal.param_env, + goal.predicate.alias, + assumption_projection_pred.projection_ty, + )?; + ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) + .expect("expected goal term to be fully unconstrained"); + then(ecx) + }) + } else { + Err(NoSolution) + } + } + + fn consider_impl_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + impl_def_id: DefId, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + + let goal_trait_ref = goal.predicate.alias.trait_ref(tcx); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + if !drcx.substs_refs_may_unify(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) { + return Err(NoSolution); + } + + ecx.probe(|ecx| { + let impl_substs = ecx.fresh_substs_for_item(impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); + + ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; + + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_substs) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + ecx.add_goals(where_clause_bounds); + + // In case the associated item is hidden due to specialization, we have to + // return ambiguity this would otherwise be incomplete, resulting in + // unsoundness during coherence (#105782). + let Some(assoc_def) = fetch_eligible_assoc_item_def( + ecx, + goal.param_env, + goal_trait_ref, + goal.predicate.def_id(), + impl_def_id + )? else { + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + }; + + if !assoc_def.item.defaultness(tcx).has_value() { + 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.alias.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: + // - a goal ` as Trait>::Assoc` + // - the applicable impl `impl Trait for Vec` + // - and the impl which defines `Assoc` being `impl Trait for Vec` + // + // We first rebase the goal substs onto the impl, going from `[Vec, i32, u64]` + // to `[u32, u64]`. + // + // And then map these substs to the substs of the defining impl of `Assoc`, going + // from `[u32, u64]` to `[u32, i32, u64]`. + let impl_substs_with_gat = + goal.predicate.alias.substs.rebase_onto(tcx, goal_trait_ref.def_id, impl_substs); + let substs = ecx.translate_substs( + goal.param_env, + impl_def_id, + impl_substs_with_gat, + assoc_def.defining_node, + ); + + // Finally we construct the actual value of the associated type. + let term = match assoc_def.item.kind { + ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()), + ty::AssocKind::Const => bug!("associated const projection is not supported yet"), + ty::AssocKind::Fn => unreachable!("we should never project to a fn"), + }; + + ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_auto_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.tcx().sess.delay_span_bug( + ecx.tcx().def_span(goal.predicate.def_id()), + "associated types not allowed on auto traits", + ); + Err(NoSolution) + } + + fn consider_trait_alias_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("trait aliases do not have associated types: {:?}", goal); + } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Sized` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_copy_clone_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_pointer_like_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`PointerLike` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_fn_ptr_trait_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`FnPtr` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + goal_kind: ty::ClosureKind, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + let tupled_inputs_and_output = + match structural_traits::extract_tupled_inputs_and_output_from_callable( + tcx, + goal.predicate.self_ty(), + goal_kind, + )? { + Some(tupled_inputs_and_output) => tupled_inputs_and_output, + None => { + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + }; + let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) + }); + + // We're using `ProjectionPredicate` as a clause here. + // + // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/1 + // for more details. + let pred = tupled_inputs_and_output + .map_bound(|(inputs, output)| ty::ProjectionPredicate { + projection_ty: tcx + .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]), + term: output.into(), + }) + .to_predicate(tcx); + // A built-in `Fn` impl only holds if the output is sized. + // (FIXME: technically we only need to check this if the type is a fn ptr...) + Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) + } + + fn consider_builtin_tuple_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Tuple` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + ecx.probe(|ecx| { + let metadata_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Never + | ty::Foreign(..) => tcx.types.unit, + + ty::Error(e) => tcx.ty_error(*e), + + ty::Str | ty::Slice(_) => tcx.types.usize, + + ty::Dynamic(_, _, _) => { + let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); + tcx.type_of(dyn_metadata) + .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())]) + } + + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. + let sized_predicate = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + DUMMY_SP, + [ty::GenericArg::from(goal.predicate.self_ty())], + ); + ecx.add_goal(goal.with(tcx, sized_predicate)); + tcx.types.unit + } + + ty::Adt(def, substs) if def.is_struct() => { + match def.non_enum_variant().fields.raw.last() { + None => tcx.types.unit, + Some(field_def) => { + let self_ty = field_def.ty(tcx, substs); + ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + } + } + ty::Adt(_, _) => tcx.types.unit, + + ty::Tuple(elements) => match elements.last() { + None => tcx.types.unit, + Some(&self_ty) => { + ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }, + + ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `::Metadata`", + goal.predicate.self_ty() + ), + }; + + ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into()) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_builtin_future_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Generator(def_id, substs, _) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // Generators are not futures unless they come from `async` desugaring + let tcx = ecx.tcx(); + if !tcx.generator_is_async(def_id) { + return Err(NoSolution); + } + + let term = substs.as_generator().return_ty().into(); + + Self::consider_implied_clause( + ecx, + goal, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]), + term, + }).to_predicate(tcx), + // Technically, we need to check that the future type is Sized, + // but that's already proven by the generator being WF. + [], + ) + } + + fn consider_builtin_generator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Generator(def_id, substs, _) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // `async`-desugared generators do not implement the generator trait + let tcx = ecx.tcx(); + if tcx.generator_is_async(def_id) { + return Err(NoSolution); + } + + let generator = substs.as_generator(); + + let name = tcx.associated_item(goal.predicate.def_id()).name; + let term = if name == sym::Return { + generator.return_ty().into() + } else if name == sym::Yield { + generator.yield_ty().into() + } else { + bug!("unexpected associated item `<{self_ty} as Generator>::{name}`") + }; + + Self::consider_implied_clause( + ecx, + goal, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: ecx + .tcx() + .mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]), + term, + }).to_predicate(tcx), + // Technically, we need to check that the future type is Sized, + // but that's already proven by the generator being WF. + [], + ) + } + + fn consider_builtin_unsize_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Unsize` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_dyn_upcast_candidates( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec> { + bug!("`Unsize` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let discriminant_ty = match *self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Never + | ty::Foreign(..) + | ty::Adt(_, _) + | ty::Str + | ty::Slice(_) + | ty::Dynamic(_, _, _) + | ty::Tuple(_) + | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()), + + // We do not call `Ty::discriminant_ty` on alias, param, or placeholder + // types, which return `::Discriminant` + // (or ICE in the case of placeholders). Projecting a type to itself + // is never really productive. + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + return Err(NoSolution); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `::Discriminant`", + goal.predicate.self_ty() + ), + }; + + ecx.probe(|ecx| { + ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into()) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_builtin_destruct_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Destruct` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_transmute_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) + } +} + +/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. +/// +/// FIXME: We should merge these 3 implementations as it's likely that they otherwise +/// diverge. +#[instrument(level = "debug", skip(ecx, param_env), ret)] +fn fetch_eligible_assoc_item_def<'tcx>( + ecx: &EvalCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + goal_trait_ref: ty::TraitRef<'tcx>, + trait_assoc_def_id: DefId, + impl_def_id: DefId, +) -> Result, NoSolution> { + let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id) + .map_err(|ErrorGuaranteed { .. }| NoSolution)?; + + let eligible = if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref); + !poly_trait_ref.still_further_specializable() + } else { + debug!(?node_item.item.def_id, "not eligible due to default"); + false + } + }; + + if eligible { Ok(Some(node_item)) } else { Ok(None) } +} diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 538c16c8ce2cd..5df3664b2c3d7 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -9,10 +9,10 @@ use super::{EvalCtxt, SolverMode}; impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn normalize_opaque_type( &mut self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); - let opaque_ty = goal.predicate.projection_ty; + let opaque_ty = goal.predicate.alias; let expected = goal.predicate.term.ty().expect("no such thing as an opaque const"); match (goal.param_env.reveal(), self.solver_mode()) { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 8303bab4c36f3..a45c739673937 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,20 +1,6 @@ -use crate::traits::specialization_graph; - -use super::assembly::{self, structural_traits}; use super::EvalCtxt; -use rustc_errors::ErrorGuaranteed; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_hir::LangItem; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::specialization_graph::LeafDef; -use rustc_infer::traits::Reveal; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty::ProjectionPredicate; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; -use rustc_span::{sym, DUMMY_SP}; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -22,571 +8,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let def_id = goal.predicate.def_id(); - match self.tcx().def_kind(def_id) { - DefKind::AssocTy | DefKind::AssocConst => { - // To only compute normalization once for each projection we only - // assemble normalization candidates if the expected term is an - // unconstrained inference variable. - // - // Why: For better cache hits, since if we have an unconstrained RHS then - // there are only as many cache keys as there are (canonicalized) alias - // types in each normalizes-to goal. This also weakens inference in a - // forwards-compatible way so we don't use the value of the RHS term to - // affect candidate assembly for projections. - // - // E.g. for `::Assoc == u32` we recursively compute the goal - // `exists ::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver, since we're piggybacking off of regular - // goal caching. - if self.term_is_fully_unconstrained(goal) { - match self.tcx().associated_item(def_id).container { - ty::AssocItemContainer::TraitContainer => { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) - } - ty::AssocItemContainer::ImplContainer => { - bug!("IATs not supported here yet") - } - } - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - } - DefKind::AnonConst => self.normalize_anon_const(goal), - DefKind::OpaqueTy => self.normalize_opaque_type(goal), - kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), - } - } - - #[instrument(level = "debug", skip(self), ret)] - fn normalize_anon_const( - &mut self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, - ) -> QueryResult<'tcx> { - if let Some(normalized_const) = self.try_const_eval_resolve( - goal.param_env, - ty::UnevaluatedConst::new( - goal.predicate.projection_ty.def_id, - goal.predicate.projection_ty.substs, - ), - self.tcx() - .type_of(goal.predicate.projection_ty.def_id) - .no_bound_vars() - .expect("const ty should not rely on other generics"), - ) { - self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - } -} - -impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { - fn self_ty(self) -> Ty<'tcx> { - self.self_ty() - } - - fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { - self.projection_ty.trait_ref(tcx) - } - - fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { - self.with_self_ty(tcx, self_ty) - } - - fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { - self.trait_def_id(tcx) - } - - fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Binder<'tcx, ty::Clause<'tcx>>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { - if let Some(projection_pred) = assumption.as_projection_clause() - && projection_pred.projection_def_id() == goal.predicate.def_id() - { - ecx.probe(|ecx| { - let assumption_projection_pred = - ecx.instantiate_binder_with_infer(projection_pred); - ecx.eq( - goal.param_env, - goal.predicate.projection_ty, - assumption_projection_pred.projection_ty, - )?; - ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) - .expect("expected goal term to be fully unconstrained"); - then(ecx) - }) - } else { - Err(NoSolution) - } + let alias = goal.predicate.projection_ty.to_ty(self.tcx()).into(); + self.eq(goal.param_env, alias, goal.predicate.term)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - - fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, - impl_def_id: DefId, - ) -> QueryResult<'tcx> { - let tcx = ecx.tcx(); - - let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); - let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; - if !drcx.substs_refs_may_unify(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) { - return Err(NoSolution); - } - - ecx.probe(|ecx| { - let impl_substs = ecx.fresh_substs_for_item(impl_def_id); - let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - - ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; - - let where_clause_bounds = tcx - .predicates_of(impl_def_id) - .instantiate(tcx, impl_substs) - .predicates - .into_iter() - .map(|pred| goal.with(tcx, pred)); - ecx.add_goals(where_clause_bounds); - - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). - let Some(assoc_def) = fetch_eligible_assoc_item_def( - ecx, - goal.param_env, - goal_trait_ref, - goal.predicate.def_id(), - impl_def_id - )? else { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - }; - - if !assoc_def.item.defaultness(tcx).has_value() { - 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: - // - a goal ` as Trait>::Assoc` - // - the applicable impl `impl Trait for Vec` - // - and the impl which defines `Assoc` being `impl Trait for Vec` - // - // We first rebase the goal substs onto the impl, going from `[Vec, i32, u64]` - // to `[u32, u64]`. - // - // And then map these substs to the substs of the defining impl of `Assoc`, going - // from `[u32, u64]` to `[u32, i32, u64]`. - let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto( - tcx, - goal_trait_ref.def_id, - impl_substs, - ); - let substs = ecx.translate_substs( - goal.param_env, - impl_def_id, - impl_substs_with_gat, - assoc_def.defining_node, - ); - - // Finally we construct the actual value of the associated type. - let term = match assoc_def.item.kind { - ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()), - ty::AssocKind::Const => bug!("associated const projection is not supported yet"), - ty::AssocKind::Fn => unreachable!("we should never project to a fn"), - }; - - ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - - fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - ecx.tcx().sess.delay_span_bug( - ecx.tcx().def_span(goal.predicate.def_id()), - "associated types not allowed on auto traits", - ); - Err(NoSolution) - } - - fn consider_trait_alias_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("trait aliases do not have associated types: {:?}", goal); - } - - fn consider_builtin_sized_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Sized` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_copy_clone_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_pointer_like_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`PointerLike` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_fn_ptr_trait_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`FnPtr` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - goal_kind: ty::ClosureKind, - ) -> QueryResult<'tcx> { - let tcx = ecx.tcx(); - let tupled_inputs_and_output = - match structural_traits::extract_tupled_inputs_and_output_from_callable( - tcx, - goal.predicate.self_ty(), - goal_kind, - )? { - Some(tupled_inputs_and_output) => tupled_inputs_and_output, - None => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - } - }; - let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) - }); - - let pred = tupled_inputs_and_output - .map_bound(|(inputs, output)| ty::ProjectionPredicate { - projection_ty: tcx - .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]), - term: output.into(), - }) - .to_predicate(tcx); - // A built-in `Fn` impl only holds if the output is sized. - // (FIXME: technically we only need to check this if the type is a fn ptr...) - Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) - } - - fn consider_builtin_tuple_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Tuple` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - let tcx = ecx.tcx(); - ecx.probe(|ecx| { - let metadata_ty = match goal.predicate.self_ty().kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Array(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) - | ty::Never - | ty::Foreign(..) => tcx.types.unit, - - ty::Error(e) => tcx.ty_error(*e), - - ty::Str | ty::Slice(_) => tcx.types.usize, - - ty::Dynamic(_, _, _) => { - let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); - tcx.type_of(dyn_metadata) - .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())]) - } - - ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. - let sized_predicate = ty::TraitRef::from_lang_item( - tcx, - LangItem::Sized, - DUMMY_SP, - [ty::GenericArg::from(goal.predicate.self_ty())], - ); - ecx.add_goal(goal.with(tcx, sized_predicate)); - tcx.types.unit - } - - ty::Adt(def, substs) if def.is_struct() => { - match def.non_enum_variant().fields.raw.last() { - None => tcx.types.unit, - Some(field_def) => { - let self_ty = field_def.ty(tcx, substs); - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - )); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); - } - } - } - ty::Adt(_, _) => tcx.types.unit, - - ty::Tuple(elements) => match elements.last() { - None => tcx.types.unit, - Some(&self_ty) => { - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - )); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); - } - }, - - ty::Infer( - ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), - ) - | ty::Bound(..) => bug!( - "unexpected self ty `{:?}` when normalizing `::Metadata`", - goal.predicate.self_ty() - ), - }; - - ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into()) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - - fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - let self_ty = goal.predicate.self_ty(); - let ty::Generator(def_id, substs, _) = *self_ty.kind() else { - return Err(NoSolution); - }; - - // Generators are not futures unless they come from `async` desugaring - let tcx = ecx.tcx(); - if !tcx.generator_is_async(def_id) { - return Err(NoSolution); - } - - let term = substs.as_generator().return_ty().into(); - - Self::consider_implied_clause( - ecx, - goal, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]), - term, - }) - .to_predicate(tcx), - // Technically, we need to check that the future type is Sized, - // but that's already proven by the generator being WF. - [], - ) - } - - fn consider_builtin_generator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - let self_ty = goal.predicate.self_ty(); - let ty::Generator(def_id, substs, _) = *self_ty.kind() else { - return Err(NoSolution); - }; - - // `async`-desugared generators do not implement the generator trait - let tcx = ecx.tcx(); - if tcx.generator_is_async(def_id) { - return Err(NoSolution); - } - - let generator = substs.as_generator(); - - let name = tcx.associated_item(goal.predicate.def_id()).name; - let term = if name == sym::Return { - generator.return_ty().into() - } else if name == sym::Yield { - generator.yield_ty().into() - } else { - bug!("unexpected associated item `<{self_ty} as Generator>::{name}`") - }; - - Self::consider_implied_clause( - ecx, - goal, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: ecx - .tcx() - .mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]), - term, - }) - .to_predicate(tcx), - // Technically, we need to check that the future type is Sized, - // but that's already proven by the generator being WF. - [], - ) - } - - fn consider_builtin_unsize_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Unsize` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_dyn_upcast_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec> { - bug!("`Unsize` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - let self_ty = goal.predicate.self_ty(); - let discriminant_ty = match *self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Array(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) - | ty::Never - | ty::Foreign(..) - | ty::Adt(_, _) - | ty::Str - | ty::Slice(_) - | ty::Dynamic(_, _, _) - | ty::Tuple(_) - | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()), - - // We do not call `Ty::discriminant_ty` on alias, param, or placeholder - // types, which return `::Discriminant` - // (or ICE in the case of placeholders). Projecting a type to itself - // is never really productive. - ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return Err(NoSolution); - } - - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Bound(..) => bug!( - "unexpected self ty `{:?}` when normalizing `::Discriminant`", - goal.predicate.self_ty() - ), - }; - - ecx.probe(|ecx| { - ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into()) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - - fn consider_builtin_destruct_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Destruct` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_transmute_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) - } -} - -/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. -/// -/// FIXME: We should merge these 3 implementations as it's likely that they otherwise -/// diverge. -#[instrument(level = "debug", skip(ecx, param_env), ret)] -fn fetch_eligible_assoc_item_def<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - goal_trait_ref: ty::TraitRef<'tcx>, - trait_assoc_def_id: DefId, - impl_def_id: DefId, -) -> Result, NoSolution> { - let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id) - .map_err(|ErrorGuaranteed { .. }| NoSolution)?; - - let eligible = if node_item.is_final() { - // Non-specializable items are always projectable. - true - } else { - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - if param_env.reveal() == Reveal::All { - let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref); - !poly_trait_ref.still_further_specializable() - } else { - debug!(?node_item.item.def_id, "not eligible due to default"); - false - } - }; - - if eligible { Ok(Some(node_item)) } else { Ok(None) } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 60cf386a289f9..197e731f6490f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -702,12 +702,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() - .map(|ty| { - goal.with( - ecx.tcx(), - ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)), - ) - }) + .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))) .collect::>(), ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 62d2aad5277f6..81dab948eca8d 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -828,6 +828,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // the `ParamEnv`. ty::PredicateKind::WellFormed(..) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) 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 1bc4599377aac..c4ba00a499452 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1098,6 +1098,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "TypeWellFormedFromEnv predicate should only exist in the environment" ), + ty::PredicateKind::NormalizesTo(..) => span_bug!( + span, + "NormalizesTo predicate should never be the predicate cause of a SelectionError" + ), + ty::PredicateKind::AliasRelate(..) => span_bug!( span, "AliasRelate predicate should never be the predicate cause of a SelectionError" diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 88d2091de0f52..be604b96b7697 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -369,8 +369,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } - ty::PredicateKind::AliasRelate(..) => { - bug!("AliasRelate is only used for new solver") + ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) => { + bug!("predicate only used by new solver: {:?}", obligation.predicate) } }, Some(pred) => match pred { @@ -637,8 +637,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } - ty::PredicateKind::AliasRelate(..) => { - bug!("AliasRelate is only used for new solver") + ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) => { + bug!("predicate only used by new solver: {:?}", obligation.predicate) } ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index b2771915eef8e..65253be04e629 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -308,20 +308,23 @@ fn predicate_references_self<'tcx>( has_self_ty(&ty.into()).then_some(sp) } - ty::PredicateKind::AliasRelate(..) => bug!("`AliasRelate` not allowed as assumption"), - ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + // FIXME(generic_const_exprs): this can mention `Self` + | ty::PredicateKind::ConstEvaluatable(..) => None, + + ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - // FIXME(generic_const_exprs): this can mention `Self` - | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => { + bug!("should only check where-clauses for object safety: {predicate:?}") + } } } @@ -349,7 +352,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { return false; /* No Sized trait, can't require it! */ }; - // Search for a predicate like `Self : Sized` amongst the trait bounds. + // Search for a predicate like `Self: Sized` amongst the trait bounds. let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() { @@ -367,6 +370,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 9989fc9c479a3..0c82d45f6610a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -133,6 +133,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5acd7b573dd9a..30eef0685a4e4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -983,8 +983,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for chalk") } - ty::PredicateKind::AliasRelate(..) => { - bug!("AliasRelate is only used for new solver") + ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) => { + bug!("predicate only used by new solver: {bound_predicate:?}") } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 84746eba3ecf8..68b48d6748136 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -22,7 +22,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); if self.infcx.next_trait_solver() { - while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() { + while let ty::Alias(ty::Projection, alias) = *ty.kind() { let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::NormalizeProjectionType, span: self.cause.span, @@ -31,10 +31,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { self.infcx.tcx, self.cause.clone(), self.param_env, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: new_infer_ty.into(), - }), + ty::NormalizesTo { alias, term: new_infer_ty.into() }, ); if self.infcx.predicate_may_hold(&obligation) { fulfill_cx.register_predicate_obligation(self.infcx, obligation); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 086ab32b52007..cbb7087106dff 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -174,6 +174,7 @@ pub fn predicate_obligations<'tcx>( | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}") @@ -971,6 +972,7 @@ pub(crate) fn required_region_bounds<'tcx>( | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index e6c6e0f13df31..bdd0bd33bc7b9 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -133,6 +133,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi // some of these in terms of chalk operations. ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) @@ -675,6 +677,7 @@ impl<'tcx> LowerInto<'tcx, Option None, ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) @@ -810,6 +813,7 @@ impl<'tcx> LowerInto<'tcx, Option None, ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 94c33efaeff7a..df1ef51a9113e 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -37,7 +37,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable> + Par // This has been seen to fail in RL, so making it a non-debug assertion to better catch // those cases. assert_eq!( - normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)), + normalized_obligations.iter().find(|p| !is_outlives_predicate(p.predicate)), None, ); @@ -54,13 +54,14 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable> + Par } } -fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { +fn is_outlives_predicate(p: ty::Predicate<'_>) -> bool { match p.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => false, + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => true, ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) @@ -70,6 +71,6 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => true, + | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0c7950bfd3ddd..22c4f9bd8bf74 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -349,6 +349,7 @@ pub(crate) fn clean_predicate<'tcx>( ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None, ty::PredicateKind::Subtype(..) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ObjectSafe(..) diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index c0d2c835d63d4..ddedee288417b 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -34,15 +34,18 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) | ty::Clause::ConstArgHasType(..), ) | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEvaluatable(..) => continue, + ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, - ty::PredicateKind::AliasRelate(..) => panic!("alias relate predicate on function: {predicate:#?}"), - ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), - ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), - ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), - ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"), - ty::PredicateKind::Ambiguous => panic!("ambiguous predicate on function: {predicate:#?}"), + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => { + panic!("We should only wf check where clauses, unexpected predicate: {predicate:?}") + } } } match predicates.parent { diff --git a/tests/ui/traits/new-solver/projection/param-env-trait-candidate-1.rs b/tests/ui/traits/new-solver/projection/param-env-trait-candidate-1.rs new file mode 100644 index 0000000000000..e36d574efe264 --- /dev/null +++ b/tests/ui/traits/new-solver/projection/param-env-trait-candidate-1.rs @@ -0,0 +1,14 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +// See https://github.com/rust-lang/trait-system-refactor-initiative/issues/1 +// a minimization of a pattern in core. +fn next, U>(t: &mut T) -> Option { + t.next() +} + +fn foo(t: &mut T) { + let _: Option = next(t); +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/projection/param-env-trait-candidate-2.rs b/tests/ui/traits/new-solver/projection/param-env-trait-candidate-2.rs new file mode 100644 index 0000000000000..c8050997a1d2a --- /dev/null +++ b/tests/ui/traits/new-solver/projection/param-env-trait-candidate-2.rs @@ -0,0 +1,29 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +// See https://github.com/rust-lang/trait-system-refactor-initiative/issues/1, +// a minimization of a pattern in core. + +trait Iterator { + type Item; +} + +struct Flatten(I); + +impl Iterator for Flatten +where + I: Iterator, +{ + type Item = U; +} + +fn needs_iterator() {} + +fn environment() +where + J: Iterator, +{ + needs_iterator::>(); +} + +fn main() {}