Skip to content

Report higher-ranked trait error when higher-ranked projection goal fails in new solver #139513

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

Merged
merged 1 commit into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,34 @@ impl<'tcx> BestObligation<'tcx> {
}
}

/// When a higher-ranked projection goal fails, check that the corresponding
/// higher-ranked trait goal holds or not. This is because the process of
/// instantiating and then re-canonicalizing the binder of the projection goal
/// forces us to be unable to see that the leak check failed in the nested
/// `NormalizesTo` goal, so we don't fall back to the rigid projection check
/// that should catch when a projection goal fails due to an unsatisfied trait
/// goal.
fn detect_error_in_higher_ranked_projection(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn detect_error_in_higher_ranked_projection(
fn detect_trait_error_in_higher_ranked_projection(

r=me with this change

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already got rolled up, and I don't think this is a significant enough change to kick it out of the rollup. I'll fold this into #139513 which is based off this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this fails to merge in which case I'll amend this. I'll keep an eye on the rollup lol

&mut self,
goal: &inspect::InspectGoal<'_, 'tcx>,
) -> ControlFlow<PredicateObligation<'tcx>> {
let tcx = goal.infcx().tcx;
if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
&& !projection_clause.bound_vars().is_empty()
{
let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx));
self.with_derived_obligation(self.obligation.with(tcx, pred), |this| {
goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(tcx, pred),
goal.depth() + 1,
this,
)
})
} else {
ControlFlow::Continue(())
}
}

/// It is likely that `NormalizesTo` failed without any applicable candidates
/// because the alias is not well-formed.
///
Expand Down Expand Up @@ -374,7 +402,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
source: CandidateSource::Impl(impl_def_id),
result: _,
} = candidate.kind()
&& goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
&& tcx.do_not_recommend_impl(impl_def_id)
{
trace!("#[do_not_recommend] -> exit");
return ControlFlow::Break(self.obligation.clone());
Expand Down Expand Up @@ -486,7 +514,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
if let Some(obligation) = goal
.infcx()
.visit_proof_tree_at_depth(
goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
goal.goal().with(tcx, ty::ClauseKind::WellFormed(lhs.into())),
goal.depth() + 1,
self,
)
Expand All @@ -496,7 +524,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
} else if let Some(obligation) = goal
.infcx()
.visit_proof_tree_at_depth(
goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
goal.goal().with(tcx, ty::ClauseKind::WellFormed(rhs.into())),
goal.depth() + 1,
self,
)
Expand All @@ -506,6 +534,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
}
}

self.detect_error_in_higher_ranked_projection(goal)?;

ControlFlow::Break(self.obligation.clone())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ LL | fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {}
| ^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/candidate-from-env-universe-err-project.rs:53:30
--> $DIR/candidate-from-env-universe-err-project.rs:52:30
|
LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
Expand All @@ -22,7 +22,7 @@ LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
found associated type `<T as Trait<'a>>::Assoc`

error[E0308]: mismatched types
--> $DIR/candidate-from-env-universe-err-project.rs:53:30
--> $DIR/candidate-from-env-universe-err-project.rs:52:30
|
LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,20 @@ note: required by a bound in `projection_bound`
LL | fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `projection_bound`

error[E0271]: type mismatch resolving `<T as Trait<'a>>::Assoc == usize`
--> $DIR/candidate-from-env-universe-err-project.rs:38:24
|
LL | projection_bound::<T>();
| ^ type mismatch resolving `<T as Trait<'a>>::Assoc == usize`
|
note: types differ
--> $DIR/candidate-from-env-universe-err-project.rs:14:18
|
LL | type Assoc = usize;
| ^^^^^
note: required by a bound in `projection_bound`
--> $DIR/candidate-from-env-universe-err-project.rs:18:42
|
LL | fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {}
| ^^^^^^^^^^^^^ required by this bound in `projection_bound`

error: higher-ranked subtype error
--> $DIR/candidate-from-env-universe-err-project.rs:53:30
--> $DIR/candidate-from-env-universe-err-project.rs:52:30
|
LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: higher-ranked subtype error
--> $DIR/candidate-from-env-universe-err-project.rs:53:30
--> $DIR/candidate-from-env-universe-err-project.rs:52:30
|
LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 5 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ fn function2<T: Trait<'static, 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>();
//[next]~^ ERROR type mismatch resolving `<T as Trait<'a>>::Assoc == usize`
//[next]~| ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied
//[current]~^^^ ERROR mismatched types
//[next]~^ ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied
//[current]~^^ ERROR mismatched types
}

fn function3<T: Trait<'static, Assoc = usize>>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: implementation of `FnOnce` is not general enough
--> $DIR/closure-mismatch.rs:8:5
--> $DIR/closure-mismatch.rs:12:5
|
LL | baz(|_| ());
| ^^^^^^^^^^^ implementation of `FnOnce` is not general enough
Expand All @@ -8,7 +8,7 @@ LL | baz(|_| ());
= note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2`

error: implementation of `Fn` is not general enough
--> $DIR/closure-mismatch.rs:8:5
--> $DIR/closure-mismatch.rs:12:5
|
LL | baz(|_| ());
| ^^^^^^^^^^^ implementation of `Fn` is not general enough
Expand All @@ -17,7 +17,7 @@ LL | baz(|_| ());
= note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
--> $DIR/closure-mismatch.rs:11:5
--> $DIR/closure-mismatch.rs:16:5
|
LL | baz(|x| ());
| ^^^^^^^^^^^ implementation of `FnOnce` is not general enough
Expand All @@ -26,7 +26,7 @@ LL | baz(|x| ());
= note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2`

error: implementation of `Fn` is not general enough
--> $DIR/closure-mismatch.rs:11:5
--> $DIR/closure-mismatch.rs:16:5
|
LL | baz(|x| ());
| ^^^^^^^^^^^ implementation of `Fn` is not general enough
Expand Down
67 changes: 67 additions & 0 deletions tests/ui/mismatched_types/closure-mismatch.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
error[E0277]: the trait bound `{closure@$DIR/closure-mismatch.rs:12:9: 12:12}: Foo` is not satisfied
--> $DIR/closure-mismatch.rs:12:9
|
LL | baz(|_| ());
| --- ^^^^^^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
= help: the trait `for<'a> FnOnce(&'a ())` is not implemented for closure `{closure@$DIR/closure-mismatch.rs:12:9: 12:12}`
= note: expected a closure with signature `for<'a> fn(&'a ())`
found a closure with signature `fn(&())`
note: this is a known limitation of the trait solver that will be lifted in the future
--> $DIR/closure-mismatch.rs:12:9
|
LL | baz(|_| ());
| ----^^^----
| | |
| | the trait solver is unable to infer the generic types that should be inferred from this argument
| add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required for `{closure@$DIR/closure-mismatch.rs:12:9: 12:12}` to implement `Foo`
--> $DIR/closure-mismatch.rs:7:18
|
LL | impl<T: Fn(&())> Foo for T {}
| ------- ^^^ ^
| |
| unsatisfied trait bound introduced here
note: required by a bound in `baz`
--> $DIR/closure-mismatch.rs:9:11
|
LL | fn baz<T: Foo>(_: T) {}
| ^^^ required by this bound in `baz`

error[E0277]: the trait bound `{closure@$DIR/closure-mismatch.rs:16:9: 16:12}: Foo` is not satisfied
--> $DIR/closure-mismatch.rs:16:9
|
LL | baz(|x| ());
| --- ^^^^^^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
= help: the trait `for<'a> FnOnce(&'a ())` is not implemented for closure `{closure@$DIR/closure-mismatch.rs:16:9: 16:12}`
= note: expected a closure with signature `for<'a> fn(&'a ())`
found a closure with signature `fn(&())`
note: this is a known limitation of the trait solver that will be lifted in the future
--> $DIR/closure-mismatch.rs:16:9
|
LL | baz(|x| ());
| ----^^^----
| | |
| | the trait solver is unable to infer the generic types that should be inferred from this argument
| add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required for `{closure@$DIR/closure-mismatch.rs:16:9: 16:12}` to implement `Foo`
--> $DIR/closure-mismatch.rs:7:18
|
LL | impl<T: Fn(&())> Foo for T {}
| ------- ^^^ ^
| |
| unsatisfied trait bound introduced here
note: required by a bound in `baz`
--> $DIR/closure-mismatch.rs:9:11
|
LL | fn baz<T: Foo>(_: T) {}
| ^^^ required by this bound in `baz`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
14 changes: 10 additions & 4 deletions tests/ui/mismatched_types/closure-mismatch.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver

trait Foo {}

impl<T: Fn(&())> Foo for T {}
Expand All @@ -6,9 +10,11 @@ fn baz<T: Foo>(_: T) {}

fn main() {
baz(|_| ());
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `Fn` is not general enough
//[current]~^ ERROR implementation of `FnOnce` is not general enough
//[current]~| ERROR implementation of `Fn` is not general enough
//[next]~^^^ ERROR Foo` is not satisfied
baz(|x| ());
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `Fn` is not general enough
//[current]~^ ERROR implementation of `FnOnce` is not general enough
//[current]~| ERROR implementation of `Fn` is not general enough
//[next]~^^^ ERROR Foo` is not satisfied
}
Loading