Skip to content

Commit 43f4f2a

Browse files
committed
Auto merge of #119820 - lcnr:leak-check-2, r=jackh726
instantiate higher ranked goals outside of candidate selection This PR modifies `evaluate` to more eagerly instantiate higher-ranked goals, preventing the `leak_check` during candidate selection from detecting placeholder errors involving that binder. For a general background regarding higher-ranked region solving and the leak check, see https://hackmd.io/qd9Wp03cQVy06yOLnro2Kg. > The first is something called the **leak check**. You can think of it as a "quick and dirty" approximation for the region check, which will come later. The leak check detects some kinds of errors early, essentially deciding between "this set of outlives constraints are guaranteed to result in an error eventually" or "this set of outlives constraints may be solvable". ## The ideal future We would like to end up with the following idealized design to handle universal binders: ```rust fn enter_forall<'tcx, T, R>( forall: Binder<'tcx, T>, f: impl FnOnce(T) -> R, ) -> R { let new_universe = infcx.increment_universe_index(); let value = instantiate_binder_with_placeholders_in(new_universe, forall); let result = f(value); eagerly_handle_higher_ranked_region_constraints_in(new_universe); infcx.decrement_universe_index(); assert!(!result.has_placeholders_in_or_above(new_universe)); result } ``` That is, when universally instantiating a binder, anything using the placeholders has to happen inside of a limited scope (the closure `f`). After this closure has completed, all constraints involving placeholders are known. We then handle any *external constraints* which name these placeholders. We destructure `TypeOutlives` constraints involving placeholders and eagerly handle any region constraints involving these placeholders. We do not return anything mentioning the placeholders created inside of this function to the caller. Being able to eagerly handle *all* region constraints involving placeholders will be difficult due to complex `TypeOutlives` constraints, involving inference variables or alias types, and higher ranked implied bounds. The exact issues and possible solutions are out of scope of this FCP. #### How does the leak check fit into this The `leak_check` is an underapproximation of `eagerly_handle_higher_ranked_region_constraints_in`. It detects some kinds of errors involving placeholders from `new_universe`, but not all of them. It only looks at region outlives constraints, ignoring `TypeOutlives`, and checks whether one of the following two conditions are met for **placeholders in or above `new_universe`**, in which case it results in an error: - `'!p1: '!p2` a placeholder `'!p2` outlives a different placeholder `'!p1` - `'!p1: '?2` an inference variable `'?2` outlives a placeholder `'!p1` *which it cannot name* It does not handle all higher ranked region constraints, so we still return constraints involving placeholders from `new_universe` which are then (re)checked by `lexical_region_resolve` or MIR borrowck. As we check higher ranked constraints in the full regionck anyways, the `leak_check` is not soundness critical. It's current only purpose is to move some higher ranked region errors earlier, enabling it to guide type inference and trait solving. Adding additional uses of the `leak_check` in the future would only strengthen inference and is therefore not breaking. ## Where do we use currently use the leak check The `leak_check` is currently used in two places: Coherence does not use a proper regionck, only relying on the `leak_check` called [at the end of the implicit negative overlap check](https://github.com/rust-lang/rust/blob/8b94152af68a0ed6d6af0b5ba57491e40481008e/compiler/rustc_trait_selection/src/traits/coherence.rs#L235-L238). During coherence all parameters are instantiated with inference variables, so the only possible region errors are higher-ranked. We currently also sometimes make guesses when destructuring `TypeOutlives` constraints which can theoretically result in incorrect errors. This could result in overlapping impls. We also use the `leak_check` [at the end of `fn evaluation_probe`](https://github.com/rust-lang/rust/blob/8b94152af68a0ed6d6af0b5ba57491e40481008e/compiler/rustc_trait_selection/src/traits/select/mod.rs#L607-L610). This function is used during candidate assembly for `Trait` goals. Most notably we use [inside of `evaluate_candidate` during winnowing](https://github.com/rust-lang/rust/blob/0e4243538b9119654c22dce688f8a63c81864de9/compiler/rustc_trait_selection/src/traits/select/mod.rs#L491-L502). Conceptionally, it is as if we compute each candidate in a separate `enter_forall`. ## The current use in `fn evaluation_probe` is undesirable Because we only instantiate a higher-ranked goal once inside of `fn evaluation_probe`, errors involving placeholders from that binder can impact selection. This results in inconsistent behavior ([playground]( *[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dac60ebdd517201788899ffa77364831)*)): ```rust trait Leak<'a> {} impl Leak<'_> for Box<u32> {} impl Leak<'static> for Box<u16> {} fn impls_leak<T: for<'a> Leak<'a>>() {} trait IndirectLeak<'a> {} impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} fn impls_indirect_leak<T: for<'a> IndirectLeak<'a>>() {} fn main() { // ok // // The `Box<u16>` impls fails the leak check, // meaning that we apply the `Box<u32>` impl. impls_leak::<Box<_>>(); // error: type annotations needed // // While the `Box<u16>` impl would fail the leak check // we have already instantiated the binder while applying // the generic `IndirectLeak` impl, so during candidate // selection of `Leak` we do not detect the placeholder error. // Evaluation of `Box<_>: Leak<'!a>` is therefore ambiguous, // resulting in `for<'a> Box<_>: Leak<'a>` also being ambiguous. impls_indirect_leak::<Box<_>>(); } ``` We generally prefer `where`-bounds over implementations during candidate selection, both for [trait goals](https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_trait_selection/src/traits/select/mod.rs#L1863-L1887) and during [normalization](https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_trait_selection/src/traits/project.rs#L184-L198). However, we currently **do not** use the `leak_check` during candidate assembly in normalizing. This can result in inconsistent behavior: ```rust trait Trait<'a> { type Assoc; } impl<'a, T> Trait<'a> for T { type Assoc = usize; } fn trait_bound<T: for<'a> Trait<'a>>() {} fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {} // A function with a trivial where-bound which is more // restrictive than the impl. fn function<T: Trait<'static, Assoc = usize>>() { // ok // // Proving `for<'a> T: Trait<'a>` using the where-bound results // in a leak check failure, so we use the more general impl, // causing this to succeed. trait_bound::<T>(); // error // // Proving the `Projection` goal `for<'a> T: Trait<'a, Assoc = usize>` // does not use the leak check when trying the where-bound, causing us // to prefer it over the impl, resulting in a placeholder error. projection_bound::<T>(); // error // // Trying to normalize the type `for<'a> fn(<T as Trait<'a>>::Assoc)` // only gets to `<T as Trait<'a>>::Assoc` once `'a` has been already // instantiated, causing us to prefer the where-bound over the impl // resulting in a placeholder error. Even if were were to also use the // leak check during candidate selection for normalization, this // case would still not compile. let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| (); } ``` This is also likely to be more performant. It enables more caching in the new trait solver by simply [recursively calling the canonical query][new solver] after instantiating the higher-ranked goal. It is also unclear how to add the leak check to normalization in the new solver. To handle rust-lang/trait-system-refactor-initiative#1 `Projection` goals are implemented via `AliasRelate`. This again means that we instantiate the binder before ever normalizing any alias. Even if we were to avoid this, we lose the ability to [cache normalization by itself, ignoring the expected `term`](https://github.com/rust-lang/rust/blob/5bd5d214effd494f4bafb29b3a7a2f6c2070ca5c/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs#L34-L49). We cannot replace the `term` with an inference variable before instantiating the binder, as otherwise `for<'a> T: Trait<Assoc<'a> = &'a ()>` breaks. If we only replace the term after instantiating the binder, we cannot easily evaluate the goal in a separate context, as [we'd then lose the information necessary for the leak check](https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_next_trait_solver/src/canonicalizer.rs#L230-L232). Adding this information to the canonical input also seems non-trivial. ## Proposed solution I propose to instantiate the binder outside of candidate assembly, causing placeholders from higher-ranked goals to get ignored while selecting their candidate. This mostly[^1] matches the [current behavior of the new solver][new solver]. The impact of this change is therefore as follows: ```rust trait Leak<'a> {} impl Leak<'_> for Box<u32> {} impl Leak<'static> for Box<u16> {} fn impls_leak<T: for<'a> Leak<'a>>() {} trait IndirectLeak<'a> {} impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} fn impls_indirect_leak<T: for<'a> IndirectLeak<'a>>() {} fn guide_selection() { // ok -> ambiguous impls_leak::<Box<_>>(); // ambiguous impls_indirect_leak::<Box<_>>(); } trait Trait<'a> { type Assoc; } impl<'a, T> Trait<'a> for T { type Assoc = usize; } fn trait_bound<T: for<'a> Trait<'a>>() {} fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {} // A function which a trivial where-bound which is more // restrictive than the impl. fn function<T: Trait<'static, Assoc = usize>>() { // ok -> error trait_bound::<T>(); // error projection_bound::<T>(); // error let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| (); } ``` This does not change the behavior if candidates have higher ranked nested goals, as in this case the `leak_check` causes the nested goal to result in an error ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a74c25300b23db9022226de99d8a2fa6)): ```rust trait LeakCheckFailure<'a> {} impl LeakCheckFailure<'static> for () {} trait Trait<T> {} impl Trait<u32> for () where for<'a> (): LeakCheckFailure<'a> {} impl Trait<u16> for () {} fn impls_trait<T: Trait<U>, U>() {} fn main() { // ok // // It does not matter whether candidate assembly // considers the placeholders from higher-ranked goal. // // Either `for<'a> (): LeakCheckFailure<'a>` has no // applicable candidate or it has a single applicable candidate // when then later results in an error. This allows us to // infer `U` to `u16`. impls_trait::<(), _>() } ``` ## Impact on existing crates This is a **breaking change**. [A crater run](#119820 (comment)) found 17 regressed crates with 7 root causes. For a full analysis of all affected crates, see https://gist.github.com/lcnr/7c1c652f30567048ea240554a36ed95c. --- I believe this breakage to be acceptable and would merge this change. I am confident that the new position of the leak check matches our idealized future and cannot envision any other consistent alternative. Where possible, I intend to open PRs fixing/avoiding the regressions before landing this PR. I originally intended to remove the `coherence_leak_check` lint in the same PR. However, while I am confident in the *position* of the leak check, deciding on its exact behavior is left as future work, cc #112999. This PR therefore only moves the leak check while keeping the lint when relying on it in coherence. [new solver]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs#L479-L484 [^1]: the new solver has a separate cause of inconsistent behavior rn rust-lang/trait-system-refactor-initiative#53 (comment) r? `@nikomatsakis`
2 parents 0accf4e + f090de8 commit 43f4f2a

29 files changed

+866
-194
lines changed

Diff for: 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
}

Diff for: src/tools/tidy/src/issues.txt

-1
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,6 @@
11321132
"ui/generics/issue-98432.rs",
11331133
"ui/higher-ranked/trait-bounds/issue-100689.rs",
11341134
"ui/higher-ranked/trait-bounds/issue-102899.rs",
1135-
"ui/higher-ranked/trait-bounds/issue-30786.rs",
11361135
"ui/higher-ranked/trait-bounds/issue-36139-normalize-closure-sig.rs",
11371136
"ui/higher-ranked/trait-bounds/issue-39292.rs",
11381137
"ui/higher-ranked/trait-bounds/issue-42114.rs",
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:28: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:39: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:39: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:18: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:55: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:55: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)