Skip to content

Commit ca718ff

Browse files
committed
track the source of nested goals
1 parent 321b656 commit ca718ff

File tree

14 files changed

+173
-81
lines changed

14 files changed

+173
-81
lines changed

compiler/rustc_middle/src/traits/solve.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,24 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
233233
}
234234
}
235235

236+
/// Why a specific goal has to be proven.
237+
///
238+
/// This is necessary as we treat nested goals different depending on
239+
/// their source.
240+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
241+
pub enum GoalSource {
242+
Misc,
243+
/// We're proving a where-bound of an impl.
244+
///
245+
/// FIXME(-Znext-solver=coinductive): Explain how and why this
246+
/// changes whether cycles are coinductive.
247+
///
248+
/// This also impacts whether we erase constraints on overflow.
249+
/// Erasing constraints is generally very useful for perf and also
250+
/// results in better error messages by avoiding spurious errors.
251+
ImplWhereBound,
252+
}
253+
236254
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
237255
pub enum IsNormalizesToHack {
238256
Yes,

compiler/rustc_middle/src/traits/solve/inspect.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
//! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
2020
2121
use super::{
22-
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution,
23-
QueryInput, QueryResult,
22+
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack,
23+
NoSolution, QueryInput, QueryResult,
2424
};
2525
use crate::{infer::canonical::CanonicalVarValues, ty};
2626
use format::ProofTreeFormatter;
@@ -115,7 +115,7 @@ impl Debug for Probe<'_> {
115115
pub enum ProbeStep<'tcx> {
116116
/// We added a goal to the `EvalCtxt` which will get proven
117117
/// the next time `EvalCtxt::try_evaluate_added_goals` is called.
118-
AddGoal(CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
118+
AddGoal(GoalSource, CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
119119
/// The inside of a `EvalCtxt::try_evaluate_added_goals` call.
120120
EvaluateGoals(AddedGoalsEvaluation<'tcx>),
121121
/// A call to `probe` while proving the current goal. This is

compiler/rustc_middle/src/traits/solve/inspect/format.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,13 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
123123
self.nested(|this| {
124124
for step in &probe.steps {
125125
match step {
126-
ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
126+
ProbeStep::AddGoal(source, goal) => {
127+
let source = match source {
128+
GoalSource::Misc => "misc",
129+
GoalSource::ImplWhereBound => "impl where-bound",
130+
};
131+
writeln!(this.f, "ADDED GOAL ({source}): {goal:?}")?
132+
}
127133
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
128134
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
129135
ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?,

compiler/rustc_trait_selection/src/solve/alias_relate.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both
1212
//! may apply, then we can compute the "intersection" of both normalizes-to by
1313
//! performing them together. This is used specifically to resolve ambiguities.
14-
use super::EvalCtxt;
14+
use super::{EvalCtxt, GoalSource};
1515
use rustc_infer::infer::DefineOpaqueTypes;
1616
use rustc_infer::traits::query::NoSolution;
1717
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
@@ -89,11 +89,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
8989
ty::TermKind::Const(_) => {
9090
if let Some(alias) = term.to_alias_ty(self.tcx()) {
9191
let term = self.next_term_infer_of_kind(term);
92-
self.add_goal(Goal::new(
93-
self.tcx(),
94-
param_env,
95-
ty::NormalizesTo { alias, term },
96-
));
92+
self.add_goal(
93+
GoalSource::Misc,
94+
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
95+
);
9796
self.try_evaluate_added_goals()?;
9897
Ok(Some(self.resolve_vars_if_possible(term)))
9998
} else {
@@ -109,7 +108,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
109108
opaque: ty::AliasTy<'tcx>,
110109
term: ty::Term<'tcx>,
111110
) -> QueryResult<'tcx> {
112-
self.add_goal(Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }));
111+
self.add_goal(
112+
GoalSource::Misc,
113+
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }),
114+
);
113115
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
114116
}
115117

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Code shared by trait and projection goals for candidate assembly.
22
33
use super::{EvalCtxt, SolverMode};
4+
use crate::solve::GoalSource;
45
use crate::traits::coherence;
56
use rustc_hir::def_id::DefId;
67
use rustc_infer::traits::query::NoSolution;
@@ -62,7 +63,9 @@ pub(super) trait GoalKind<'tcx>:
6263
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
6364
) -> QueryResult<'tcx> {
6465
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
65-
ecx.add_goals(requirements);
66+
// FIXME(-Znext-solver=coinductive): check whether this should be
67+
// `GoalSource::ImplWhereBound` for any caller.
68+
ecx.add_goals(GoalSource::Misc, requirements);
6669
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
6770
})
6871
}
@@ -94,12 +97,16 @@ pub(super) trait GoalKind<'tcx>:
9497
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
9598
bug!("expected object type in `consider_object_bound_candidate`");
9699
};
97-
ecx.add_goals(structural_traits::predicates_for_object_candidate(
98-
ecx,
99-
goal.param_env,
100-
goal.predicate.trait_ref(tcx),
101-
bounds,
102-
));
100+
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
101+
ecx.add_goals(
102+
GoalSource::Misc,
103+
structural_traits::predicates_for_object_candidate(
104+
ecx,
105+
goal.param_env,
106+
goal.predicate.trait_ref(tcx),
107+
bounds,
108+
),
109+
);
103110
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
104111
})
105112
}
@@ -364,7 +371,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
364371
let normalized_ty = ecx.next_ty_infer();
365372
let normalizes_to_goal =
366373
goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() });
367-
ecx.add_goal(normalizes_to_goal);
374+
ecx.add_goal(GoalSource::Misc, normalizes_to_goal);
368375
if let Err(NoSolution) = ecx.try_evaluate_added_goals() {
369376
debug!("self type normalization failed");
370377
return vec![];

compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ use rustc_middle::ty::{
2323
use rustc_session::config::DumpSolverProofTree;
2424
use rustc_span::DUMMY_SP;
2525
use std::io::Write;
26+
use std::iter;
2627
use std::ops::ControlFlow;
2728

2829
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
2930

3031
use super::inspect::ProofTreeBuilder;
31-
use super::SolverMode;
3232
use super::{search_graph, GoalEvaluationKind};
3333
use super::{search_graph::SearchGraph, Goal};
34+
use super::{GoalSource, SolverMode};
3435
pub use select::InferCtxtSelectExt;
3536

3637
mod canonical;
@@ -105,7 +106,7 @@ pub(super) struct NestedGoals<'tcx> {
105106
/// can be unsound with more powerful coinduction in the future.
106107
pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
107108
/// The rest of the goals which have not yet processed or remain ambiguous.
108-
pub(super) goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
109+
pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
109110
}
110111

111112
impl<'tcx> NestedGoals<'tcx> {
@@ -439,7 +440,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
439440
} else {
440441
let kind = self.infcx.instantiate_binder_with_placeholders(kind);
441442
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
442-
self.add_goal(goal);
443+
self.add_goal(GoalSource::Misc, goal);
443444
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
444445
}
445446
}
@@ -488,6 +489,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
488489
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
489490

490491
self.inspect.evaluate_added_goals_loop_start();
492+
493+
fn with_misc_source<'tcx>(
494+
it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
495+
) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> {
496+
iter::zip(iter::repeat(GoalSource::Misc), it)
497+
}
498+
491499
// If this loop did not result in any progress, what's our final certainty.
492500
let mut unchanged_certainty = Some(Certainty::Yes);
493501
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
@@ -503,7 +511,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
503511
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
504512
unconstrained_goal,
505513
)?;
506-
self.nested_goals.goals.extend(instantiate_goals);
514+
self.nested_goals.goals.extend(with_misc_source(instantiate_goals));
507515

508516
// Finally, equate the goal's RHS with the unconstrained var.
509517
// We put the nested goals from this into goals instead of
@@ -512,7 +520,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
512520
// matters in practice, though.
513521
let eq_goals =
514522
self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
515-
goals.goals.extend(eq_goals);
523+
goals.goals.extend(with_misc_source(eq_goals));
516524

517525
// We only look at the `projection_ty` part here rather than
518526
// looking at the "has changed" return from evaluate_goal,
@@ -533,20 +541,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
533541
}
534542
}
535543

536-
for goal in goals.goals.drain(..) {
544+
for (source, goal) in goals.goals.drain(..) {
537545
let (has_changed, certainty, instantiate_goals) = self.evaluate_goal(
538546
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
539547
goal,
540548
)?;
541-
self.nested_goals.goals.extend(instantiate_goals);
549+
self.nested_goals.goals.extend(with_misc_source(instantiate_goals));
542550
if has_changed {
543551
unchanged_certainty = None;
544552
}
545553

546554
match certainty {
547555
Certainty::Yes => {}
548556
Certainty::Maybe(_) => {
549-
self.nested_goals.goals.push(goal);
557+
self.nested_goals.goals.push((source, goal));
550558
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
551559
}
552560
}
@@ -670,7 +678,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
670678
.at(&ObligationCause::dummy(), param_env)
671679
.eq(DefineOpaqueTypes::No, lhs, rhs)
672680
.map(|InferOk { value: (), obligations }| {
673-
self.add_goals(obligations.into_iter().map(|o| o.into()));
681+
self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
674682
})
675683
.map_err(|e| {
676684
debug!(?e, "failed to equate");
@@ -689,7 +697,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
689697
.at(&ObligationCause::dummy(), param_env)
690698
.sub(DefineOpaqueTypes::No, sub, sup)
691699
.map(|InferOk { value: (), obligations }| {
692-
self.add_goals(obligations.into_iter().map(|o| o.into()));
700+
self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
693701
})
694702
.map_err(|e| {
695703
debug!(?e, "failed to subtype");
@@ -709,7 +717,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
709717
.at(&ObligationCause::dummy(), param_env)
710718
.relate(DefineOpaqueTypes::No, lhs, variance, rhs)
711719
.map(|InferOk { value: (), obligations }| {
712-
self.add_goals(obligations.into_iter().map(|o| o.into()));
720+
self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
713721
})
714722
.map_err(|e| {
715723
debug!(?e, "failed to relate");
@@ -842,7 +850,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
842850
true,
843851
&mut obligations,
844852
)?;
845-
self.add_goals(obligations.into_iter().map(|o| o.into()));
853+
self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
846854
Ok(())
847855
}
848856

@@ -862,7 +870,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
862870
hidden_ty,
863871
&mut obligations,
864872
);
865-
self.add_goals(obligations.into_iter().map(|o| o.into()));
873+
self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
866874
}
867875

868876
// Do something for each opaque/hidden pair defined with `def_id` in the

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
119119
) {
120120
for step in &probe.steps {
121121
match step {
122-
&inspect::ProbeStep::AddGoal(goal) => nested_goals.push(goal),
122+
&inspect::ProbeStep::AddGoal(_source, goal) => nested_goals.push(goal),
123123
inspect::ProbeStep::NestedProbe(ref probe) => {
124124
// Nested probes have to prove goals added in their parent
125125
// but do not leak them, so we truncate the added goals

compiler/rustc_trait_selection/src/solve/inspect/build.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::mem;
77

88
use rustc_middle::traits::query::NoSolution;
99
use rustc_middle::traits::solve::{
10-
CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
10+
CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult,
1111
};
1212
use rustc_middle::ty::{self, TyCtxt};
1313
use rustc_session::config::DumpSolverProofTree;
@@ -216,7 +216,7 @@ impl<'tcx> WipProbe<'tcx> {
216216

217217
#[derive(Eq, PartialEq, Debug)]
218218
enum WipProbeStep<'tcx> {
219-
AddGoal(inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
219+
AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
220220
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
221221
NestedProbe(WipProbe<'tcx>),
222222
CommitIfOkStart,
@@ -226,7 +226,7 @@ enum WipProbeStep<'tcx> {
226226
impl<'tcx> WipProbeStep<'tcx> {
227227
fn finalize(self) -> inspect::ProbeStep<'tcx> {
228228
match self {
229-
WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal),
229+
WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
230230
WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
231231
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
232232
WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart,
@@ -428,7 +428,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
428428
}
429429
}
430430

431-
pub fn add_goal(ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
431+
pub fn add_goal(
432+
ecx: &mut EvalCtxt<'_, 'tcx>,
433+
source: GoalSource,
434+
goal: Goal<'tcx, ty::Predicate<'tcx>>,
435+
) {
432436
// Can't use `if let Some(this) = ecx.inspect.as_mut()` here because
433437
// we have to immutably use the `EvalCtxt` for `make_canonical_state`.
434438
if ecx.inspect.is_noop() {
@@ -442,7 +446,9 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
442446
evaluation: WipProbe { steps, .. },
443447
..
444448
})
445-
| DebugSolver::Probe(WipProbe { steps, .. }) => steps.push(WipProbeStep::AddGoal(goal)),
449+
| DebugSolver::Probe(WipProbe { steps, .. }) => {
450+
steps.push(WipProbeStep::AddGoal(source, goal))
451+
}
446452
s => unreachable!("tried to add {goal:?} to {s:?}"),
447453
}
448454
}

compiler/rustc_trait_selection/src/solve/mod.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use rustc_infer::infer::DefineOpaqueTypes;
1919
use rustc_infer::traits::query::NoSolution;
2020
use rustc_middle::infer::canonical::CanonicalVarInfos;
2121
use rustc_middle::traits::solve::{
22-
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult,
23-
Response,
22+
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
23+
QueryResult, Response,
2424
};
2525
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex};
2626
use rustc_middle::ty::{
@@ -157,7 +157,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
157157
) -> QueryResult<'tcx> {
158158
match self.well_formed_goals(goal.param_env, goal.predicate) {
159159
Some(goals) => {
160-
self.add_goals(goals);
160+
self.add_goals(GoalSource::Misc, goals);
161161
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
162162
}
163163
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
@@ -223,15 +223,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
223223
}
224224

225225
#[instrument(level = "debug", skip(self))]
226-
fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
227-
inspect::ProofTreeBuilder::add_goal(self, goal);
228-
self.nested_goals.goals.push(goal);
226+
fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
227+
inspect::ProofTreeBuilder::add_goal(self, source, goal);
228+
self.nested_goals.goals.push((source, goal));
229229
}
230230

231231
#[instrument(level = "debug", skip(self, goals))]
232-
fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) {
232+
fn add_goals(
233+
&mut self,
234+
source: GoalSource,
235+
goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
236+
) {
233237
for goal in goals {
234-
self.add_goal(goal);
238+
self.add_goal(source, goal);
235239
}
236240
}
237241

@@ -335,7 +339,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
335339
param_env,
336340
ty::NormalizesTo { alias, term: normalized_ty.into() },
337341
);
338-
this.add_goal(normalizes_to_goal);
342+
this.add_goal(GoalSource::Misc, normalizes_to_goal);
339343
this.try_evaluate_added_goals()?;
340344
let ty = this.resolve_vars_if_possible(normalized_ty);
341345
Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty))

0 commit comments

Comments
 (0)