@@ -7,7 +7,7 @@ use rustc_infer::infer::{
7
7
BoundRegionConversionTime , DefineOpaqueTypes , InferCtxt , InferOk , TyCtxtInferExt ,
8
8
} ;
9
9
use rustc_infer:: traits:: query:: NoSolution ;
10
- use rustc_infer:: traits:: solve:: MaybeCause ;
10
+ use rustc_infer:: traits:: solve:: { MaybeCause , NestedNormalizationGoals } ;
11
11
use rustc_infer:: traits:: ObligationCause ;
12
12
use rustc_middle:: infer:: canonical:: CanonicalVarInfos ;
13
13
use rustc_middle:: infer:: unify_key:: { ConstVariableOrigin , ConstVariableOriginKind } ;
@@ -61,6 +61,14 @@ pub struct EvalCtxt<'a, 'tcx> {
61
61
/// The variable info for the `var_values`, only used to make an ambiguous response
62
62
/// with no constraints.
63
63
variables : CanonicalVarInfos < ' tcx > ,
64
+ /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals,
65
+ /// `NormalizesTo` goals act like functions with the expected term always being
66
+ /// fully unconstrained. This would weaken inference however, as the nested goals
67
+ /// never get the inference constraints from the actual normalized-to type. Because
68
+ /// of this we return any ambiguous nested goals from `NormalizesTo` to the caller
69
+ /// when then adds these to its own context. The caller is always an `AliasRelate`
70
+ /// goal so this never leaks out of the solver.
71
+ is_normalizes_to_goal : bool ,
64
72
pub ( super ) var_values : CanonicalVarValues < ' tcx > ,
65
73
66
74
predefined_opaques_in_body : PredefinedOpaques < ' tcx > ,
@@ -91,7 +99,7 @@ pub struct EvalCtxt<'a, 'tcx> {
91
99
pub ( super ) inspect : ProofTreeBuilder < ' tcx > ,
92
100
}
93
101
94
- #[ derive( Debug , Clone ) ]
102
+ #[ derive( Default , Debug , Clone ) ]
95
103
pub ( super ) struct NestedGoals < ' tcx > {
96
104
/// These normalizes-to goals are treated specially during the evaluation
97
105
/// loop. In each iteration we take the RHS of the projection, replace it with
@@ -153,6 +161,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
153
161
self . search_graph . solver_mode ( )
154
162
}
155
163
164
+ pub ( super ) fn set_is_normalizes_to_goal ( & mut self ) {
165
+ self . is_normalizes_to_goal = true ;
166
+ }
167
+
156
168
/// Creates a root evaluation context and search graph. This should only be
157
169
/// used from outside of any evaluation, and other methods should be preferred
158
170
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
@@ -165,8 +177,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
165
177
let mut search_graph = search_graph:: SearchGraph :: new ( mode) ;
166
178
167
179
let mut ecx = EvalCtxt {
168
- search_graph : & mut search_graph,
169
180
infcx,
181
+ search_graph : & mut search_graph,
170
182
nested_goals : NestedGoals :: new ( ) ,
171
183
inspect : ProofTreeBuilder :: new_maybe_root ( infcx. tcx , generate_proof_tree) ,
172
184
@@ -178,6 +190,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
178
190
max_input_universe : ty:: UniverseIndex :: ROOT ,
179
191
variables : ty:: List :: empty ( ) ,
180
192
var_values : CanonicalVarValues :: dummy ( ) ,
193
+ is_normalizes_to_goal : false ,
181
194
tainted : Ok ( ( ) ) ,
182
195
} ;
183
196
let result = f ( & mut ecx) ;
@@ -231,6 +244,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
231
244
infcx,
232
245
variables : canonical_input. variables ,
233
246
var_values,
247
+ is_normalizes_to_goal : false ,
234
248
predefined_opaques_in_body : input. predefined_opaques_in_body ,
235
249
max_input_universe : canonical_input. max_universe ,
236
250
search_graph,
@@ -317,6 +331,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
317
331
source : GoalSource ,
318
332
goal : Goal < ' tcx , ty:: Predicate < ' tcx > > ,
319
333
) -> Result < ( bool , Certainty ) , NoSolution > {
334
+ let ( normalization_nested_goals, has_changed, certainty) =
335
+ self . evaluate_goal_raw ( goal_evaluation_kind, source, goal) ?;
336
+ assert ! ( normalization_nested_goals. is_empty( ) ) ;
337
+ Ok ( ( has_changed, certainty) )
338
+ }
339
+
340
+ /// FIXME(-Znext-solver=coinduction): `_source` is currently unused but will
341
+ /// be necessary once we implement the new coinduction approach.
342
+ fn evaluate_goal_raw (
343
+ & mut self ,
344
+ goal_evaluation_kind : GoalEvaluationKind ,
345
+ _source : GoalSource ,
346
+ goal : Goal < ' tcx , ty:: Predicate < ' tcx > > ,
347
+ ) -> Result < ( NestedNormalizationGoals < ' tcx > , bool , Certainty ) , NoSolution > {
320
348
let ( orig_values, canonical_goal) = self . canonicalize_goal ( goal) ;
321
349
let mut goal_evaluation =
322
350
self . inspect . new_goal_evaluation ( goal, & orig_values, goal_evaluation_kind) ;
@@ -334,12 +362,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
334
362
Ok ( response) => response,
335
363
} ;
336
364
337
- let ( certainty, has_changed) = self . instantiate_response_discarding_overflow (
338
- goal . param_env ,
339
- source ,
340
- orig_values,
341
- canonical_response,
342
- ) ;
365
+ let ( normalization_nested_goals , certainty, has_changed) = self
366
+ . instantiate_response_discarding_overflow (
367
+ goal . param_env ,
368
+ orig_values,
369
+ canonical_response,
370
+ ) ;
343
371
self . inspect . goal_evaluation ( goal_evaluation) ;
344
372
// FIXME: We previously had an assert here that checked that recomputing
345
373
// a goal after applying its constraints did not change its response.
@@ -351,47 +379,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
351
379
// Once we have decided on how to handle trait-system-refactor-initiative#75,
352
380
// we should re-add an assert here.
353
381
354
- Ok ( ( has_changed, certainty) )
382
+ Ok ( ( normalization_nested_goals , has_changed, certainty) )
355
383
}
356
384
357
385
fn instantiate_response_discarding_overflow (
358
386
& mut self ,
359
387
param_env : ty:: ParamEnv < ' tcx > ,
360
- source : GoalSource ,
361
388
original_values : Vec < ty:: GenericArg < ' tcx > > ,
362
389
response : CanonicalResponse < ' tcx > ,
363
- ) -> ( Certainty , bool ) {
364
- // The old solver did not evaluate nested goals when normalizing.
365
- // It returned the selection constraints allowing a `Projection`
366
- // obligation to not hold in coherence while avoiding the fatal error
367
- // from overflow.
368
- //
369
- // We match this behavior here by considering all constraints
370
- // from nested goals which are not from where-bounds. We will already
371
- // need to track which nested goals are required by impl where-bounds
372
- // for coinductive cycles, so we simply reuse that here.
373
- //
374
- // While we could consider overflow constraints in more cases, this should
375
- // not be necessary for backcompat and results in better perf. It also
376
- // avoids a potential inconsistency which would otherwise require some
377
- // tracking for root goals as well. See #119071 for an example.
378
- let keep_overflow_constraints = || {
379
- self . search_graph . current_goal_is_normalizes_to ( )
380
- && source != GoalSource :: ImplWhereBound
381
- } ;
382
-
383
- if let Certainty :: Maybe ( MaybeCause :: Overflow { .. } ) = response. value . certainty
384
- && !keep_overflow_constraints ( )
385
- {
386
- return ( response. value . certainty , false ) ;
390
+ ) -> ( NestedNormalizationGoals < ' tcx > , Certainty , bool ) {
391
+ if let Certainty :: Maybe ( MaybeCause :: Overflow { .. } ) = response. value . certainty {
392
+ return ( NestedNormalizationGoals :: empty ( ) , response. value . certainty , false ) ;
387
393
}
388
394
389
395
let has_changed = !response. value . var_values . is_identity_modulo_regions ( )
390
396
|| !response. value . external_constraints . opaque_types . is_empty ( ) ;
391
397
392
- let certainty =
398
+ let ( normalization_nested_goals , certainty) =
393
399
self . instantiate_and_apply_query_response ( param_env, original_values, response) ;
394
- ( certainty, has_changed)
400
+ ( normalization_nested_goals , certainty, has_changed)
395
401
}
396
402
397
403
fn compute_goal ( & mut self , goal : Goal < ' tcx , ty:: Predicate < ' tcx > > ) -> QueryResult < ' tcx > {
@@ -494,7 +500,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
494
500
/// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
495
501
fn evaluate_added_goals_step ( & mut self ) -> Result < Option < Certainty > , NoSolution > {
496
502
let tcx = self . tcx ( ) ;
497
- let mut goals = core:: mem:: replace ( & mut self . nested_goals , NestedGoals :: new ( ) ) ;
503
+ let mut goals = core:: mem:: take ( & mut self . nested_goals ) ;
498
504
499
505
self . inspect . evaluate_added_goals_loop_start ( ) ;
500
506
@@ -515,11 +521,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
515
521
ty:: NormalizesTo { alias : goal. predicate . alias , term : unconstrained_rhs } ,
516
522
) ;
517
523
518
- let ( _, certainty) = self . evaluate_goal (
524
+ let ( NestedNormalizationGoals ( nested_goals ) , _, certainty) = self . evaluate_goal_raw (
519
525
GoalEvaluationKind :: Nested { is_normalizes_to_hack : IsNormalizesToHack :: Yes } ,
520
526
GoalSource :: Misc ,
521
527
unconstrained_goal,
522
528
) ?;
529
+ // Add the nested goals from normalization to our own nested goals.
530
+ goals. goals . extend ( nested_goals) ;
523
531
524
532
// Finally, equate the goal's RHS with the unconstrained var.
525
533
// We put the nested goals from this into goals instead of
0 commit comments