@@ -2,7 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
22
33use super :: assembly:: { self , Candidate , CandidateSource } ;
44use super :: infcx_ext:: InferCtxtExt ;
5- use super :: { Certainty , EvalCtxt , Goal , QueryResult } ;
5+ use super :: { Certainty , EvalCtxt , Goal , MaybeCause , QueryResult } ;
66use rustc_errors:: ErrorGuaranteed ;
77use rustc_hir:: def:: DefKind ;
88use rustc_hir:: def_id:: DefId ;
@@ -11,19 +11,112 @@ use rustc_infer::traits::query::NoSolution;
1111use rustc_infer:: traits:: specialization_graph:: LeafDef ;
1212use rustc_infer:: traits:: Reveal ;
1313use rustc_middle:: ty:: fast_reject:: { DeepRejectCtxt , TreatParams } ;
14- use rustc_middle:: ty:: ProjectionPredicate ;
1514use rustc_middle:: ty:: TypeVisitable ;
1615use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
16+ use rustc_middle:: ty:: { ProjectionPredicate , TypeSuperVisitable , TypeVisitor } ;
1717use rustc_span:: DUMMY_SP ;
1818use std:: iter;
19+ use std:: ops:: ControlFlow ;
1920
2021impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
2122 pub ( super ) fn compute_projection_goal (
2223 & mut self ,
2324 goal : Goal < ' tcx , ProjectionPredicate < ' tcx > > ,
2425 ) -> QueryResult < ' tcx > {
25- let candidates = self . assemble_and_evaluate_candidates ( goal) ;
26- self . merge_project_candidates ( candidates)
26+ // To only compute normalization ones for each projection we only
27+ // normalize if the expected term is an unconstrained inference variable.
28+ //
29+ // E.g. for `<T as Trait>::Assoc = u32` we recursively compute the goal
30+ // `exists<U> <T as Trait>::Assoc = U` and then take the resulting type for
31+ // `U` and equate it with `u32`. This means that we don't need a separate
32+ // projection cache in the solver.
33+ if self . term_is_fully_unconstrained ( goal) {
34+ let candidates = self . assemble_and_evaluate_candidates ( goal) ;
35+ self . merge_project_candidates ( candidates)
36+ } else {
37+ let predicate = goal. predicate ;
38+ let unconstrained_rhs = match predicate. term . unpack ( ) {
39+ ty:: TermKind :: Ty ( _) => self . infcx . next_ty_infer ( ) . into ( ) ,
40+ ty:: TermKind :: Const ( ct) => self . infcx . next_const_infer ( ct. ty ( ) ) . into ( ) ,
41+ } ;
42+ let unconstrained_predicate = ty:: Clause :: Projection ( ProjectionPredicate {
43+ projection_ty : goal. predicate . projection_ty ,
44+ term : unconstrained_rhs,
45+ } ) ;
46+ let ( _has_changed, normalize_certainty) =
47+ self . evaluate_goal ( goal. with ( self . tcx ( ) , unconstrained_predicate) ) ?;
48+
49+ let nested_eq_goals =
50+ self . infcx . eq ( goal. param_env , unconstrained_rhs, predicate. term ) ?;
51+ let eval_certainty = self . evaluate_all ( nested_eq_goals) ?;
52+ self . make_canonical_response ( normalize_certainty. unify_and ( eval_certainty) )
53+ }
54+ }
55+
56+ /// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
57+ ///
58+ /// This is the case if the `term` is an inference variable in the innermost universe
59+ /// and does not occur in any other part of the predicate.
60+ fn term_is_fully_unconstrained ( & self , goal : Goal < ' tcx , ProjectionPredicate < ' tcx > > ) -> bool {
61+ let infcx = self . infcx ;
62+ let term_is_infer = match goal. predicate . term . unpack ( ) {
63+ ty:: TermKind :: Ty ( ty) => {
64+ if let & ty:: Infer ( ty:: TyVar ( vid) ) = ty. kind ( ) {
65+ match infcx. probe_ty_var ( vid) {
66+ Ok ( value) => bug ! ( "resolved var in query: {goal:?} {value:?}" ) ,
67+ Err ( universe) => universe == infcx. universe ( ) ,
68+ }
69+ } else {
70+ false
71+ }
72+ }
73+ ty:: TermKind :: Const ( ct) => {
74+ if let ty:: ConstKind :: Infer ( ty:: InferConst :: Var ( vid) ) = ct. kind ( ) {
75+ match self . infcx . probe_const_var ( vid) {
76+ Ok ( value) => bug ! ( "resolved var in query: {goal:?} {value:?}" ) ,
77+ Err ( universe) => universe == infcx. universe ( ) ,
78+ }
79+ } else {
80+ false
81+ }
82+ }
83+ } ;
84+
85+ struct ContainsTerm < ' tcx > {
86+ term : ty:: Term < ' tcx > ,
87+ }
88+ impl < ' tcx > TypeVisitor < ' tcx > for ContainsTerm < ' tcx > {
89+ type BreakTy = ( ) ;
90+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
91+ if t. needs_infer ( ) {
92+ if ty:: Term :: from ( t) == self . term {
93+ ControlFlow :: BREAK
94+ } else {
95+ t. super_visit_with ( self )
96+ }
97+ } else {
98+ ControlFlow :: CONTINUE
99+ }
100+ }
101+
102+ fn visit_const ( & mut self , c : ty:: Const < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
103+ if c. needs_infer ( ) {
104+ if ty:: Term :: from ( c) == self . term {
105+ ControlFlow :: BREAK
106+ } else {
107+ c. super_visit_with ( self )
108+ }
109+ } else {
110+ ControlFlow :: CONTINUE
111+ }
112+ }
113+ }
114+
115+ let mut visitor = ContainsTerm { term : goal. predicate . term } ;
116+
117+ term_is_infer
118+ && goal. predicate . projection_ty . visit_with ( & mut visitor) . is_continue ( )
119+ && goal. param_env . visit_with ( & mut visitor) . is_continue ( )
27120 }
28121
29122 fn merge_project_candidates (
@@ -124,14 +217,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
124217 nested_goals. extend ( where_clause_bounds) ;
125218 let trait_ref_certainty = ecx. evaluate_all ( nested_goals) ?;
126219
220+ // In case the associated item is hidden due to specialization, we have to
221+ // return ambiguity this would otherwise be incomplete, resulting in
222+ // unsoundness during coherence (#105782).
127223 let Some ( assoc_def) = fetch_eligible_assoc_item_def (
128224 ecx. infcx ,
129225 goal. param_env ,
130226 goal_trait_ref,
131227 goal. predicate . def_id ( ) ,
132228 impl_def_id
133- ) else {
134- return Err ( NoSolution ) ;
229+ ) ? else {
230+ let certainty = Certainty :: Maybe ( MaybeCause :: Ambiguity ) ;
231+ return Ok ( trait_ref_certainty. unify_and ( certainty) ) ;
135232 } ;
136233
137234 if !assoc_def. item . defaultness ( tcx) . has_value ( ) {
@@ -178,9 +275,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
178275 ty. map_bound ( |ty| ty. into ( ) )
179276 } ;
180277
181- let nested_goals =
182- ecx. infcx . eq ( goal. param_env , goal. predicate . term , term. subst ( tcx, substs) ) ?;
183- let rhs_certainty = ecx. evaluate_all ( nested_goals) ?;
278+ // The term of our goal should be fully unconstrained, so this should never fail.
279+ //
280+ // It can however be ambiguous when the resolved type is a projection.
281+ let nested_goals = ecx
282+ . infcx
283+ . eq ( goal. param_env , goal. predicate . term , term. subst ( tcx, substs) )
284+ . expect ( "failed to unify with unconstrained term" ) ;
285+ let rhs_certainty =
286+ ecx. evaluate_all ( nested_goals) . expect ( "failed to unify with unconstrained term" ) ;
184287
185288 Ok ( trait_ref_certainty. unify_and ( rhs_certainty) )
186289 } )
@@ -217,10 +320,9 @@ fn fetch_eligible_assoc_item_def<'tcx>(
217320 goal_trait_ref : ty:: TraitRef < ' tcx > ,
218321 trait_assoc_def_id : DefId ,
219322 impl_def_id : DefId ,
220- ) -> Option < LeafDef > {
323+ ) -> Result < Option < LeafDef > , NoSolution > {
221324 let node_item = specialization_graph:: assoc_def ( infcx. tcx , impl_def_id, trait_assoc_def_id)
222- . map_err ( |ErrorGuaranteed { .. } | ( ) )
223- . ok ( ) ?;
325+ . map_err ( |ErrorGuaranteed { .. } | NoSolution ) ?;
224326
225327 let eligible = if node_item. is_final ( ) {
226328 // Non-specializable items are always projectable.
@@ -239,5 +341,5 @@ fn fetch_eligible_assoc_item_def<'tcx>(
239341 }
240342 } ;
241343
242- if eligible { Some ( node_item) } else { None }
344+ if eligible { Ok ( Some ( node_item) ) } else { Ok ( None ) }
243345}
0 commit comments