-
Notifications
You must be signed in to change notification settings - Fork 13.3k
small refactor to new projection code #107348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,8 +45,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | |
projection_ty: goal.predicate.projection_ty, | ||
term: unconstrained_rhs, | ||
}); | ||
let (_has_changed, normalize_certainty) = | ||
self.evaluate_goal(goal.with(self.tcx(), unconstrained_predicate))?; | ||
let (_has_changed, normalize_certainty) = self.in_projection_eq_hack(|this| { | ||
this.evaluate_goal(goal.with(this.tcx(), unconstrained_predicate)) | ||
})?; | ||
|
||
let nested_eq_goals = | ||
self.infcx.eq(goal.param_env, unconstrained_rhs, predicate.term)?; | ||
|
@@ -55,6 +56,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | |
} | ||
} | ||
|
||
/// This sets a flag used by a debug assert in [`EvalCtxt::evaluate_goal`], | ||
/// see the comment in that method for more details. | ||
fn in_projection_eq_hack<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { | ||
self.in_projection_eq_hack = true; | ||
let result = f(self); | ||
self.in_projection_eq_hack = false; | ||
result | ||
} | ||
|
||
/// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`. | ||
/// | ||
/// This is the case if the `term` is an inference variable in the innermost universe | ||
|
@@ -122,6 +132,28 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | |
&& goal.param_env.visit_with(&mut visitor).is_continue() | ||
} | ||
|
||
/// After normalizing the projection to `normalized_alias` with the given | ||
/// `normalization_certainty`, constrain the inference variable `term` to it | ||
/// and return a query response. | ||
fn eq_term_and_make_canonical_response( | ||
&mut self, | ||
goal: Goal<'tcx, ProjectionPredicate<'tcx>>, | ||
normalization_certainty: Certainty, | ||
normalized_alias: impl Into<ty::Term<'tcx>>, | ||
) -> QueryResult<'tcx> { | ||
// The term of our goal should be fully unconstrained, so this should never fail. | ||
// | ||
// It can however be ambiguous when the `normalized_alias` contains a projection. | ||
let nested_goals = self | ||
.infcx | ||
.eq(goal.param_env, goal.predicate.term, normalized_alias.into()) | ||
.expect("failed to unify with unconstrained term"); | ||
let rhs_certainty = | ||
self.evaluate_all(nested_goals).expect("failed to unify with unconstrained term"); | ||
|
||
self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty)) | ||
} | ||
|
||
fn merge_project_candidates( | ||
&mut self, | ||
mut candidates: Vec<Candidate<'tcx>>, | ||
|
@@ -218,7 +250,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
.map(|pred| goal.with(tcx, pred)); | ||
|
||
nested_goals.extend(where_clause_bounds); | ||
let trait_ref_certainty = ecx.evaluate_all(nested_goals)?; | ||
let match_impl_certainty = ecx.evaluate_all(nested_goals)?; | ||
|
||
// In case the associated item is hidden due to specialization, we have to | ||
// return ambiguity this would otherwise be incomplete, resulting in | ||
|
@@ -230,7 +262,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
goal.predicate.def_id(), | ||
impl_def_id | ||
)? else { | ||
return ecx.make_canonical_response(trait_ref_certainty.unify_and(Certainty::AMBIGUOUS)); | ||
return ecx.make_canonical_response(match_impl_certainty.unify_and(Certainty::AMBIGUOUS)); | ||
}; | ||
|
||
if !assoc_def.item.defaultness(tcx).has_value() { | ||
|
@@ -277,17 +309,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
ty.map_bound(|ty| ty.into()) | ||
}; | ||
|
||
// The term of our goal should be fully unconstrained, so this should never fail. | ||
// | ||
// It can however be ambiguous when the resolved type is a projection. | ||
let nested_goals = ecx | ||
.infcx | ||
.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) | ||
.expect("failed to unify with unconstrained term"); | ||
let rhs_certainty = | ||
ecx.evaluate_all(nested_goals).expect("failed to unify with unconstrained term"); | ||
|
||
ecx.make_canonical_response(trait_ref_certainty.unify_and(rhs_certainty)) | ||
ecx.eq_term_and_make_canonical_response(goal, match_impl_certainty, term.subst(tcx, substs)) | ||
}) | ||
} | ||
|
||
|
@@ -307,18 +329,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
)?; | ||
let subst_certainty = ecx.evaluate_all(nested_goals)?; | ||
|
||
// The term of our goal should be fully unconstrained, so this should never fail. | ||
// | ||
// It can however be ambiguous when the resolved type is a projection. | ||
let nested_goals = ecx | ||
.infcx | ||
.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) | ||
.expect("failed to unify with unconstrained term"); | ||
let rhs_certainty = ecx | ||
.evaluate_all(nested_goals) | ||
.expect("failed to unify with unconstrained term"); | ||
|
||
ecx.make_canonical_response(subst_certainty.unify_and(rhs_certainty)) | ||
ecx.eq_term_and_make_canonical_response( | ||
goal, | ||
subst_certainty, | ||
assumption_projection_pred.term, | ||
) | ||
}) | ||
} else { | ||
Err(NoSolution) | ||
|
@@ -434,14 +449,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
[ty::GenericArg::from(goal.predicate.self_ty())], | ||
)); | ||
|
||
let mut nested_goals = ecx.infcx.eq( | ||
goal.param_env, | ||
goal.predicate.term.ty().unwrap(), | ||
let is_sized_certainty = ecx.evaluate_goal(goal.with(tcx, sized_predicate))?.1; | ||
return ecx.eq_term_and_make_canonical_response( | ||
goal, | ||
is_sized_certainty, | ||
tcx.types.unit, | ||
)?; | ||
nested_goals.push(goal.with(tcx, sized_predicate)); | ||
|
||
return ecx.evaluate_all_and_make_canonical_response(nested_goals); | ||
); | ||
} | ||
|
||
ty::Adt(def, substs) if def.is_struct() => { | ||
|
@@ -453,7 +466,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
tcx, | ||
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), | ||
); | ||
return ecx.evaluate_all_and_make_canonical_response(vec![new_goal]); | ||
let (_, certainty) = ecx.evaluate_goal(new_goal)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we throw away There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because added a second commit which checks that the result of Will have to change the |
||
return ecx.make_canonical_response(certainty); | ||
} | ||
} | ||
} | ||
|
@@ -466,7 +480,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
tcx, | ||
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), | ||
); | ||
return ecx.evaluate_all_and_make_canonical_response(vec![new_goal]); | ||
let (_, certainty) = ecx.evaluate_goal(new_goal)?; | ||
return ecx.make_canonical_response(certainty); | ||
} | ||
}, | ||
|
||
|
@@ -479,9 +494,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
), | ||
}; | ||
|
||
let nested_goals = | ||
ecx.infcx.eq(goal.param_env, goal.predicate.term.ty().unwrap(), metadata_ty)?; | ||
ecx.evaluate_all_and_make_canonical_response(nested_goals) | ||
ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, metadata_ty) | ||
}) | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thiis hack would be also useful for other
let (_, certainty) = evaluate_goal()
calls in candidate assembly, maybe generalize the name and put it in a helper likeevaluate_goal_expect_stable
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would it be useful there? All other
evaluate_goal
shouldn't result in cycles when rerunning with substitutions applied (that's at least what I believe and why I've added the debug assert in the first place)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you meant for
evaluate_goal
added in the future? We only really hit this issue if we branch on the inference state where we actually drop info, I don't think this will happen for anything apart from hacks for better caching