-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
implement intercrate_ambiguity_causes
in the new solver
#115996
Conversation
Some changes occurred to the core trait solver cc @rust-lang/initiative-trait-system-refactor |
This comment has been minimized.
This comment has been minimized.
052b3bc
to
609a6fb
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
a6b3582
to
30f314b
Compare
@@ -568,7 +569,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { | |||
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }, | |||
unconstrained_goal, | |||
)?; | |||
self.add_goals(instantiate_goals); | |||
self.nested_goals.goals.extend(instantiate_goals); |
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 stopped using add_goals
here because the added goals aren't really nested goals and it ended up breaking the proof tree builder
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.
Can you be more specific how it breaks things? Like I understand how we probably don't want to include these goals, and I especially understand not wanting to add ambiguous goal repeatedly over and over below, but does this affect tests detrimentally?
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.
the issue was that we add goals while the state is DebugBuilder::AddedGoalsEvaluation
.
For us to sensibly handle these nested goals we'd have to either add AddGoal
as an action to AddedGoalsEvaluation
or somehow take the added goals during the evaluation and "move them back" into the WipProbe
once the added goals evaluation is done. Both of these feel somewhat ugly and slightly annoying to implement
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
9e7cc9e
to
f0bffce
Compare
This comment has been minimized.
This comment has been minimized.
5fee5f7
to
ceb890a
Compare
This comment has been minimized.
This comment has been minimized.
ceb890a
to
ed7f5ce
Compare
WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> | ||
WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> | ||
WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> | ||
WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> |
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.
this happens because I only changed structurally_normalize
to silently fail on overflow, added a fixme there to correctly handle it.
2c1ea63
to
4bc1228
Compare
@@ -568,7 +569,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { | |||
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }, | |||
unconstrained_goal, | |||
)?; | |||
self.add_goals(instantiate_goals); | |||
self.nested_goals.goals.extend(instantiate_goals); |
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.
Can you be more specific how it breaks things? Like I understand how we probably don't want to include these goals, and I especially understand not wanting to add ambiguous goal repeatedly over and over below, but does this affect tests detrimentally?
@@ -29,6 +33,22 @@ use rustc_span::DUMMY_SP; | |||
use std::iter; | |||
use std::ops::Deref; | |||
|
|||
trait ResponseT<'tcx> { |
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.
This name is kinda bad, but I can change it in a follow-up.
@@ -206,21 +228,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | |||
/// This returns the substitutions to instantiate the bound variables of | |||
/// the canonical response. This depends on the `original_values` for the | |||
/// bound variables. | |||
fn compute_query_response_substitution( | |||
&self, | |||
fn compute_query_response_substitution<T: ResponseT<'tcx>>( |
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 don't know why this is a method on EvalCtxt
anymore in this case. Should probably just be free and live in some public submodule, or even just in solve
. You don't have to change this though.
@@ -287,9 +309,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | |||
CanonicalVarValues { var_values } | |||
} | |||
|
|||
#[instrument(level = "debug", skip(self, param_env), ret)] | |||
#[instrument(level = "debug", skip(infcx, param_env), ret)] | |||
fn unify_query_var_values( |
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.
Same as above.
/// need it. | ||
pub fn visit_nested<V: ProofTreeVisitor<'tcx>>( | ||
&self, | ||
visitor: &mut V, |
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.
Like type folders, I feel like it makes more sense for the visitor to provide a fn infcx(&self) -> &InferCtxt<'tcx>
rather than carrying it in the candidate itself. You don't have to change this though.
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.
hmm, I had the InferCtxt
as part of the context instead of the visitor as the context is responsible for calling infcx.probe
which in some sense requires "ownership" of the infcx
to me 🤷
self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation); | ||
|
||
if candidates.is_empty() { | ||
candidates.push(InspectCandidate { |
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.
Hm, does this matter? If we fail to assemble any candidates, do the nested obligations that we accumulate in probes actually matter?
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.
added a comment and slightly restructured the code. It is slightly broken rn and I am still a bit unsure how to best handle this 🤷
}, | ||
); | ||
} | ||
if !overlap_mode.use_implicit_negative() { |
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.
What's up with the control flow changes here? Doesn't this mean we're no longer doing leak check below?
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.
yeah, wanted to avoid computing intercrate ambiguity causes in case we didn't actually prove the obligations as we now don't collect them while proving them. Changed this back.
infcx: &InferCtxt<'tcx>, | ||
obligations: &[PredicateObligation<'tcx>], | ||
) -> FxIndexSet<IntercrateAmbiguityCause> { | ||
let mut causes: FxIndexSet<IntercrateAmbiguityCause> = Default::default(); |
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.
nit: inference should guide this? You don't need to fix this.
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.
not sure what you mean with that
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 did you need to ascribe the type here lol
|
||
let Goal { param_env, predicate } = goal.goal(); | ||
|
||
let trait_ref = match predicate.kind().no_bound_vars() { |
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.
Do we ever have bound preds? I'm not exactly sure how we track that extra step of instantiation with placeholders in the proof tree. Is it just a goal with 1 nested goal?
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.
yeah, for these goals we instantiate the bound vars with placeholders in the ProbeKind::Root
and then prove it as a nested goal. Added commenmt
// FIXME: this isn't quite right. Changing a goal from YES with | ||
// inference contraints to AMBIGUOUS can also cause a goal to not | ||
// fail. | ||
Ok(Certainty::Yes) => { |
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.
Changing a goal from YES with inference contraints to AMBIGUOUS can also cause a goal to not fail.
Not certain what this means.
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.
This should be checking that the goal is YES + no inference constraints, right? We essentially don't want to report ambiguity if we would've just disregarded that branch of the tree because of a better one?
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 think it really only cares about "if the coherence ambiguity candidate did not exist, would this goal be an error", changed it and improved the FIXME
This should be checking that the goal is YES + no inference constraints, right?
this is already handled in line 929
r=me after addressing this one comment: #115996 (comment) The rest can be clarified asynchronously I guess, since they're just miscellaneous thoughts. |
4bc1228
to
8024c69
Compare
This comment has been minimized.
This comment has been minimized.
c1ff61a
to
614760f
Compare
@bors r=compiler-errors rollup (new solver) |
☀️ Test successful - checks-actions |
Finished benchmarking commit (e4a361a): comparison URL. Overall result: no relevant changes - no action needed@rustbot label: -perf-regression Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)This benchmark run did not return any relevant results for this metric. CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 630.991s -> 633.931s (0.47%) |
I added some comments but this is still somewhat of a mess. I think we should for the most part be able to treat all of this as a black box, so I can accept that this code isn't too great.
I also believe that some of the weirdness here is unavoidable, as proof trees - and their visitor - hide semantically relevant information, so they cannot perfectly represent the actual solver behavior.
There are some known bugs here when testing with
./x.py test tests/ui --bless -- --target-rustcflags -Ztrait-solver=next-coherence
. While I haven't diagnosed them all in detail I believe we are able to handle them all separatelystructurally_normalize
currently does not normalize opaque types, resulting in divergence between the solver internaltrait_ref_is_knowable
and the one when computing intercrate ambiguity causes.intercrate_ambiguity_cause
for reserved implsdeeply_normalize
the trait ref before printing it, that requires a "best effort" version ofdeeply_normalize
r? @compiler-errors