@@ -2,7 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
2
2
3
3
use super :: assembly:: { self , Candidate , CandidateSource } ;
4
4
use super :: infcx_ext:: InferCtxtExt ;
5
- use super :: { Certainty , EvalCtxt , Goal , QueryResult } ;
5
+ use super :: { Certainty , EvalCtxt , Goal , MaybeCause , QueryResult } ;
6
6
use rustc_errors:: ErrorGuaranteed ;
7
7
use rustc_hir:: def:: DefKind ;
8
8
use rustc_hir:: def_id:: DefId ;
@@ -11,19 +11,112 @@ use rustc_infer::traits::query::NoSolution;
11
11
use rustc_infer:: traits:: specialization_graph:: LeafDef ;
12
12
use rustc_infer:: traits:: Reveal ;
13
13
use rustc_middle:: ty:: fast_reject:: { DeepRejectCtxt , TreatParams } ;
14
- use rustc_middle:: ty:: ProjectionPredicate ;
15
14
use rustc_middle:: ty:: TypeVisitable ;
16
15
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
16
+ use rustc_middle:: ty:: { ProjectionPredicate , TypeSuperVisitable , TypeVisitor } ;
17
17
use rustc_span:: DUMMY_SP ;
18
18
use std:: iter;
19
+ use std:: ops:: ControlFlow ;
19
20
20
21
impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
21
22
pub ( super ) fn compute_projection_goal (
22
23
& mut self ,
23
24
goal : Goal < ' tcx , ProjectionPredicate < ' tcx > > ,
24
25
) -> 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 ( )
27
120
}
28
121
29
122
fn merge_project_candidates (
@@ -124,14 +217,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
124
217
nested_goals. extend ( where_clause_bounds) ;
125
218
let trait_ref_certainty = ecx. evaluate_all ( nested_goals) ?;
126
219
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).
127
223
let Some ( assoc_def) = fetch_eligible_assoc_item_def (
128
224
ecx. infcx ,
129
225
goal. param_env ,
130
226
goal_trait_ref,
131
227
goal. predicate . def_id ( ) ,
132
228
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) ) ;
135
232
} ;
136
233
137
234
if !assoc_def. item . defaultness ( tcx) . has_value ( ) {
@@ -178,9 +275,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
178
275
ty. map_bound ( |ty| ty. into ( ) )
179
276
} ;
180
277
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" ) ;
184
287
185
288
Ok ( trait_ref_certainty. unify_and ( rhs_certainty) )
186
289
} )
@@ -217,10 +320,9 @@ fn fetch_eligible_assoc_item_def<'tcx>(
217
320
goal_trait_ref : ty:: TraitRef < ' tcx > ,
218
321
trait_assoc_def_id : DefId ,
219
322
impl_def_id : DefId ,
220
- ) -> Option < LeafDef > {
323
+ ) -> Result < Option < LeafDef > , NoSolution > {
221
324
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 ) ?;
224
326
225
327
let eligible = if node_item. is_final ( ) {
226
328
// Non-specializable items are always projectable.
@@ -239,5 +341,5 @@ fn fetch_eligible_assoc_item_def<'tcx>(
239
341
}
240
342
} ;
241
343
242
- if eligible { Some ( node_item) } else { None }
344
+ if eligible { Ok ( Some ( node_item) ) } else { Ok ( None ) }
243
345
}
0 commit comments