@@ -7,7 +7,7 @@ use rustc_infer::infer::{
77 BoundRegionConversionTime , DefineOpaqueTypes , InferCtxt , InferOk , TyCtxtInferExt ,
88} ;
99use rustc_infer:: traits:: query:: NoSolution ;
10- use rustc_infer:: traits:: solve:: MaybeCause ;
10+ use rustc_infer:: traits:: solve:: { MaybeCause , NestedNormalizationGoals } ;
1111use rustc_infer:: traits:: ObligationCause ;
1212use rustc_middle:: infer:: canonical:: CanonicalVarInfos ;
1313use rustc_middle:: infer:: unify_key:: { ConstVariableOrigin , ConstVariableOriginKind } ;
@@ -61,6 +61,14 @@ pub struct EvalCtxt<'a, 'tcx> {
6161 /// The variable info for the `var_values`, only used to make an ambiguous response
6262 /// with no constraints.
6363 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 ,
6472 pub ( super ) var_values : CanonicalVarValues < ' tcx > ,
6573
6674 predefined_opaques_in_body : PredefinedOpaques < ' tcx > ,
@@ -91,7 +99,7 @@ pub struct EvalCtxt<'a, 'tcx> {
9199 pub ( super ) inspect : ProofTreeBuilder < ' tcx > ,
92100}
93101
94- #[ derive( Debug , Clone ) ]
102+ #[ derive( Default , Debug , Clone ) ]
95103pub ( super ) struct NestedGoals < ' tcx > {
96104 /// These normalizes-to goals are treated specially during the evaluation
97105 /// loop. In each iteration we take the RHS of the projection, replace it with
@@ -153,6 +161,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
153161 self . search_graph . solver_mode ( )
154162 }
155163
164+ pub ( super ) fn set_is_normalizes_to_goal ( & mut self ) {
165+ self . is_normalizes_to_goal = true ;
166+ }
167+
156168 /// Creates a root evaluation context and search graph. This should only be
157169 /// used from outside of any evaluation, and other methods should be preferred
158170 /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
@@ -165,8 +177,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
165177 let mut search_graph = search_graph:: SearchGraph :: new ( mode) ;
166178
167179 let mut ecx = EvalCtxt {
168- search_graph : & mut search_graph,
169180 infcx,
181+ search_graph : & mut search_graph,
170182 nested_goals : NestedGoals :: new ( ) ,
171183 inspect : ProofTreeBuilder :: new_maybe_root ( infcx. tcx , generate_proof_tree) ,
172184
@@ -178,6 +190,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
178190 max_input_universe : ty:: UniverseIndex :: ROOT ,
179191 variables : ty:: List :: empty ( ) ,
180192 var_values : CanonicalVarValues :: dummy ( ) ,
193+ is_normalizes_to_goal : false ,
181194 tainted : Ok ( ( ) ) ,
182195 } ;
183196 let result = f ( & mut ecx) ;
@@ -231,6 +244,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
231244 infcx,
232245 variables : canonical_input. variables ,
233246 var_values,
247+ is_normalizes_to_goal : false ,
234248 predefined_opaques_in_body : input. predefined_opaques_in_body ,
235249 max_input_universe : canonical_input. max_universe ,
236250 search_graph,
@@ -317,6 +331,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
317331 source : GoalSource ,
318332 goal : Goal < ' tcx , ty:: Predicate < ' tcx > > ,
319333 ) -> 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 > {
320348 let ( orig_values, canonical_goal) = self . canonicalize_goal ( goal) ;
321349 let mut goal_evaluation =
322350 self . inspect . new_goal_evaluation ( goal, & orig_values, goal_evaluation_kind) ;
@@ -334,12 +362,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
334362 Ok ( response) => response,
335363 } ;
336364
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+ ) ;
343371 self . inspect . goal_evaluation ( goal_evaluation) ;
344372 // FIXME: We previously had an assert here that checked that recomputing
345373 // a goal after applying its constraints did not change its response.
@@ -351,47 +379,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
351379 // Once we have decided on how to handle trait-system-refactor-initiative#75,
352380 // we should re-add an assert here.
353381
354- Ok ( ( has_changed, certainty) )
382+ Ok ( ( normalization_nested_goals , has_changed, certainty) )
355383 }
356384
357385 fn instantiate_response_discarding_overflow (
358386 & mut self ,
359387 param_env : ty:: ParamEnv < ' tcx > ,
360- source : GoalSource ,
361388 original_values : Vec < ty:: GenericArg < ' tcx > > ,
362389 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 ) ;
387393 }
388394
389395 let has_changed = !response. value . var_values . is_identity_modulo_regions ( )
390396 || !response. value . external_constraints . opaque_types . is_empty ( ) ;
391397
392- let certainty =
398+ let ( normalization_nested_goals , certainty) =
393399 self . instantiate_and_apply_query_response ( param_env, original_values, response) ;
394- ( certainty, has_changed)
400+ ( normalization_nested_goals , certainty, has_changed)
395401 }
396402
397403 fn compute_goal ( & mut self , goal : Goal < ' tcx , ty:: Predicate < ' tcx > > ) -> QueryResult < ' tcx > {
@@ -494,7 +500,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
494500 /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
495501 fn evaluate_added_goals_step ( & mut self ) -> Result < Option < Certainty > , NoSolution > {
496502 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 ) ;
498504
499505 self . inspect . evaluate_added_goals_loop_start ( ) ;
500506
@@ -515,11 +521,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
515521 ty:: NormalizesTo { alias : goal. predicate . alias , term : unconstrained_rhs } ,
516522 ) ;
517523
518- let ( _, certainty) = self . evaluate_goal (
524+ let ( NestedNormalizationGoals ( nested_goals ) , _, certainty) = self . evaluate_goal_raw (
519525 GoalEvaluationKind :: Nested { is_normalizes_to_hack : IsNormalizesToHack :: Yes } ,
520526 GoalSource :: Misc ,
521527 unconstrained_goal,
522528 ) ?;
529+ // Add the nested goals from normalization to our own nested goals.
530+ goals. goals . extend ( nested_goals) ;
523531
524532 // Finally, equate the goal's RHS with the unconstrained var.
525533 // We put the nested goals from this into goals instead of
0 commit comments