Skip to content

Commit 4fa5fb6

Browse files
lcnrBoxyUwU
authored andcommitted
move leak check out of candidate evaluation
this prevents higher ranked goals from guiding selection
1 parent 98efd80 commit 4fa5fb6

28 files changed

+865
-193
lines changed

compiler/rustc_trait_selection/src/traits/select/mod.rs

+62-14
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
6060
mod candidate_assembly;
6161
mod confirmation;
6262

63+
/// Whether to consider the binder of higher ranked goals for the `leak_check` when
64+
/// evaluating higher-ranked goals. See #119820 for more info.
65+
///
66+
/// While this is a bit hacky, it is necessary to match the behavior of the new solver:
67+
/// We eagerly instantiate binders in the new solver, outside of candidate selection, so
68+
/// the leak check inside of candidates does not consider any bound vars from the higher
69+
/// ranked goal. However, we do exit the binder once we're completely finished with a goal,
70+
/// so the leak-check can be used in evaluate by causing nested higher-ranked goals to fail.
71+
#[derive(Debug, Copy, Clone)]
72+
enum LeakCheckHigherRankedGoal {
73+
No,
74+
Yes,
75+
}
76+
6377
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
6478
pub enum IntercrateAmbiguityCause<'tcx> {
6579
DownstreamCrate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> },
@@ -384,7 +398,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
384398
let mut no_candidates_apply = true;
385399

386400
for c in candidate_set.vec.iter() {
387-
if self.evaluate_candidate(stack, c)?.may_apply() {
401+
if self
402+
.evaluate_candidate(stack, c, LeakCheckHigherRankedGoal::No)?
403+
.may_apply()
404+
{
388405
no_candidates_apply = false;
389406
break;
390407
}
@@ -455,7 +472,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
455472
// is needed for specialization. Propagate overflow if it occurs.
456473
let mut candidates = candidates
457474
.into_iter()
458-
.map(|c| match self.evaluate_candidate(stack, &c) {
475+
.map(|c| match self.evaluate_candidate(stack, &c, LeakCheckHigherRankedGoal::No) {
459476
Ok(eval) if eval.may_apply() => {
460477
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
461478
}
@@ -545,7 +562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
545562
obligation: &PredicateObligation<'tcx>,
546563
) -> Result<EvaluationResult, OverflowError> {
547564
debug_assert!(!self.infcx.next_trait_solver());
548-
self.evaluation_probe(|this| {
565+
self.evaluation_probe(|this, _outer_universe| {
549566
let goal =
550567
this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
551568
let mut result = this.evaluate_predicate_recursively(
@@ -561,13 +578,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
561578
})
562579
}
563580

581+
/// Computes the evaluation result of `op`, discarding any constraints.
582+
///
583+
/// This also runs for leak check to allow higher ranked region errors to impact
584+
/// selection. By default it checks for leaks from all universes created inside of
585+
/// `op`, but this can be overwritten if necessary.
564586
fn evaluation_probe(
565587
&mut self,
566-
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
588+
op: impl FnOnce(&mut Self, &mut ty::UniverseIndex) -> Result<EvaluationResult, OverflowError>,
567589
) -> Result<EvaluationResult, OverflowError> {
568590
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
569-
let outer_universe = self.infcx.universe();
570-
let result = op(self)?;
591+
let mut outer_universe = self.infcx.universe();
592+
let result = op(self, &mut outer_universe)?;
571593

572594
match self.infcx.leak_check(outer_universe, Some(snapshot)) {
573595
Ok(()) => {}
@@ -586,9 +608,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
586608
})
587609
}
588610

589-
/// Evaluates the predicates in `predicates` recursively. Note that
590-
/// this applies projections in the predicates, and therefore
611+
/// Evaluates the predicates in `predicates` recursively. This may
612+
/// guide inference. If this is not desired, run it inside of a
591613
/// is run within an inference probe.
614+
/// `probe`.
592615
#[instrument(skip(self, stack), level = "debug")]
593616
fn evaluate_predicates_recursively<'o, I>(
594617
&mut self,
@@ -1194,7 +1217,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11941217
}
11951218

11961219
match self.candidate_from_obligation(stack) {
1197-
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
1220+
Ok(Some(c)) => self.evaluate_candidate(stack, &c, LeakCheckHigherRankedGoal::Yes),
11981221
Ok(None) => Ok(EvaluatedToAmbig),
11991222
Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical),
12001223
Err(..) => Ok(EvaluatedToErr),
@@ -1219,6 +1242,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12191242
/// Further evaluates `candidate` to decide whether all type parameters match and whether nested
12201243
/// obligations are met. Returns whether `candidate` remains viable after this further
12211244
/// scrutiny.
1245+
///
1246+
/// Depending on the value of [LeakCheckHigherRankedGoal], we may ignore the binder of the goal
1247+
/// when eagerly detecting higher ranked region errors via the `leak_check`. See that enum for
1248+
/// more info.
12221249
#[instrument(
12231250
level = "debug",
12241251
skip(self, stack),
@@ -1229,10 +1256,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12291256
&mut self,
12301257
stack: &TraitObligationStack<'o, 'tcx>,
12311258
candidate: &SelectionCandidate<'tcx>,
1259+
leak_check_higher_ranked_goal: LeakCheckHigherRankedGoal,
12321260
) -> Result<EvaluationResult, OverflowError> {
1233-
let mut result = self.evaluation_probe(|this| {
1234-
let candidate = (*candidate).clone();
1235-
match this.confirm_candidate(stack.obligation, candidate) {
1261+
let mut result = self.evaluation_probe(|this, outer_universe| {
1262+
// We eagerly instantiate higher ranked goals to prevent universe errors
1263+
// from impacting candidate selection. This matches the behavior of the new
1264+
// solver. This slightly weakens type inference.
1265+
//
1266+
// In case there are no unresolved type or const variables this
1267+
// should still not be necessary to select a unique impl as any overlap
1268+
// relying on a universe error from higher ranked goals should have resulted
1269+
// in an overlap error in coherence.
1270+
let p = self.infcx.enter_forall_and_leak_universe(stack.obligation.predicate);
1271+
let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p));
1272+
match leak_check_higher_ranked_goal {
1273+
LeakCheckHigherRankedGoal::No => *outer_universe = self.infcx.universe(),
1274+
LeakCheckHigherRankedGoal::Yes => {}
1275+
}
1276+
1277+
match this.confirm_candidate(&obligation, candidate.clone()) {
12361278
Ok(selection) => {
12371279
debug!(?selection);
12381280
this.evaluate_predicates_recursively(
@@ -1657,8 +1699,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16571699
stack: &TraitObligationStack<'o, 'tcx>,
16581700
where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
16591701
) -> Result<EvaluationResult, OverflowError> {
1660-
self.evaluation_probe(|this| {
1661-
match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
1702+
self.evaluation_probe(|this, outer_universe| {
1703+
// Eagerly instantiate higher ranked goals.
1704+
//
1705+
// See the comment in `evaluate_candidate` to see why.
1706+
let p = self.infcx.enter_forall_and_leak_universe(stack.obligation.predicate);
1707+
let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p));
1708+
*outer_universe = self.infcx.universe();
1709+
match this.match_where_clause_trait_ref(&obligation, where_clause_trait_ref) {
16621710
Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations),
16631711
Err(()) => Ok(EvaluatedToErr),
16641712
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// cc #119820
2+
3+
trait Trait {}
4+
5+
impl<T: Trait> Trait for &T {}
6+
impl Trait for u32 {}
7+
8+
fn hr_bound<T>()
9+
where
10+
for<'a> &'a T: Trait,
11+
{
12+
}
13+
14+
fn foo<T>()
15+
where
16+
T: Trait,
17+
for<'a> &'a &'a T: Trait,
18+
{
19+
// We get a universe error when using the `param_env` candidate
20+
// but are able to successfully use the impl candidate. Without
21+
// the leak check both candidates may apply and we prefer the
22+
// `param_env` candidate in winnowing.
23+
hr_bound::<&T>();
24+
//~^ ERROR the parameter type `T` may not live long enough
25+
//~| ERROR implementation of `Trait` is not general enough
26+
}
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0310]: the parameter type `T` may not live long enough
2+
--> $DIR/candidate-from-env-universe-err-1.rs:23:5
3+
|
4+
LL | hr_bound::<&T>();
5+
| ^^^^^^^^^^^^^^
6+
| |
7+
| the parameter type `T` must be valid for the static lifetime...
8+
| ...so that the type `T` will meet its required lifetime bounds
9+
|
10+
help: consider adding an explicit lifetime bound
11+
|
12+
LL | T: Trait + 'static,
13+
| +++++++++
14+
15+
error: implementation of `Trait` is not general enough
16+
--> $DIR/candidate-from-env-universe-err-1.rs:23:5
17+
|
18+
LL | hr_bound::<&T>();
19+
| ^^^^^^^^^^^^^^ implementation of `Trait` is not general enough
20+
|
21+
= note: `Trait` would have to be implemented for the type `&'0 &T`, for any lifetime `'0`...
22+
= note: ...but `Trait` is actually implemented for the type `&'1 &'1 T`, for some specific lifetime `'1`
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0310`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/candidate-from-env-universe-err-2.rs:14:5
3+
|
4+
LL | fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() {
5+
| -- lifetime `'a` defined here
6+
LL | impl_hr::<T>();
7+
| ^^^^^^^^^^^^ requires that `'a` must outlive `'static`
8+
|
9+
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
10+
--> $DIR/candidate-from-env-universe-err-2.rs:11:19
11+
|
12+
LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {}
13+
| ^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: implementation of `Trait` is not general enough
16+
--> $DIR/candidate-from-env-universe-err-2.rs:14:5
17+
|
18+
LL | impl_hr::<T>();
19+
| ^^^^^^^^^^^^ implementation of `Trait` is not general enough
20+
|
21+
= note: `T` must implement `Trait<'0, '_>`, for any lifetime `'0`...
22+
= note: ...but it actually implements `Trait<'1, '_>`, for some specific lifetime `'1`
23+
24+
error: aborting due to 2 previous errors
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0277]: the trait bound `for<'a> T: Trait<'a, '_>` is not satisfied
2+
--> $DIR/candidate-from-env-universe-err-2.rs:14:5
3+
|
4+
LL | impl_hr::<T>();
5+
| ^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a, '_>` is not implemented for `T`
6+
|
7+
note: required by a bound in `impl_hr`
8+
--> $DIR/candidate-from-env-universe-err-2.rs:11:19
9+
|
10+
LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {}
11+
| ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impl_hr`
12+
help: consider further restricting this bound
13+
|
14+
LL | fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static> + for<'a> Trait<'a, '_>>() {
15+
| +++++++++++++++++++++++
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/candidate-from-env-universe-err-2.rs:14:5
3+
|
4+
LL | fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() {
5+
| -- lifetime `'a` defined here
6+
LL | impl_hr::<T>();
7+
| ^^^^^^^^^^^^ requires that `'a` must outlive `'static`
8+
|
9+
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
10+
--> $DIR/candidate-from-env-universe-err-2.rs:11:19
11+
|
12+
LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {}
13+
| ^^^^^^^^^^^^^^^^^^^^^
14+
15+
error[E0308]: mismatched types
16+
--> $DIR/candidate-from-env-universe-err-2.rs:14:5
17+
|
18+
LL | impl_hr::<T>();
19+
| ^^^^^^^^^^^^ one type is more general than the other
20+
|
21+
= note: expected trait `for<'a> Trait<'a, '_>`
22+
found trait `for<'b> Trait<'_, 'b>`
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ revisions: current next
2+
//@[next] compile-flags: -Znext-solver
3+
4+
// cc #119820
5+
6+
trait Trait<'a, 'b> {}
7+
8+
trait OtherTrait<'b> {}
9+
impl<'a, 'b, T: OtherTrait<'b>> Trait<'a, 'b> for T {}
10+
11+
fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {}
12+
13+
fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() {
14+
impl_hr::<T>();
15+
//[next]~^ ERROR the trait bound `for<'a> T: Trait<'a, '_>` is not satisfied
16+
//[current]~^^ERROR lifetime may not live long enough
17+
//[current]~| ERROR implementation of `Trait` is not general enough
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
error: implementation of `Trait` is not general enough
2+
--> $DIR/candidate-from-env-universe-err-project.rs:31:5
3+
|
4+
LL | trait_bound::<T>();
5+
| ^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough
6+
|
7+
= note: `T` must implement `Trait<'0>`, for any lifetime `'0`...
8+
= note: ...but it actually implements `Trait<'static>`
9+
10+
error: implementation of `Trait` is not general enough
11+
--> $DIR/candidate-from-env-universe-err-project.rs:41:5
12+
|
13+
LL | projection_bound::<T>();
14+
| ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough
15+
|
16+
= note: `T` must implement `Trait<'0>`, for any lifetime `'0`...
17+
= note: ...but it actually implements `Trait<'static>`
18+
19+
error[E0308]: mismatched types
20+
--> $DIR/candidate-from-env-universe-err-project.rs:41:5
21+
|
22+
LL | projection_bound::<T>();
23+
| ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
24+
|
25+
= note: expected associated type `<T as Trait<'static>>::Assoc`
26+
found associated type `<T as Trait<'a>>::Assoc`
27+
note: the lifetime requirement is introduced here
28+
--> $DIR/candidate-from-env-universe-err-project.rs:21:42
29+
|
30+
LL | fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {}
31+
| ^^^^^^^^^^^^^
32+
33+
error[E0308]: mismatched types
34+
--> $DIR/candidate-from-env-universe-err-project.rs:56:30
35+
|
36+
LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
38+
|
39+
= note: expected associated type `<T as Trait<'static>>::Assoc`
40+
found associated type `<T as Trait<'a>>::Assoc`
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/candidate-from-env-universe-err-project.rs:56:30
44+
|
45+
LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
46+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
47+
|
48+
= note: expected associated type `<T as Trait<'static>>::Assoc`
49+
found associated type `<T as Trait<'a>>::Assoc`
50+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
51+
52+
error: aborting due to 5 previous errors
53+
54+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)