-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Don't infer closure signatures with late-bound type vars #108827
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -308,13 +308,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | |
) -> QueryResult<'tcx> { | ||
let tcx = ecx.tcx(); | ||
let Some(tupled_inputs_and_output) = | ||
structural_traits::extract_tupled_inputs_and_output_from_callable( | ||
tcx, | ||
goal.predicate.self_ty(), | ||
goal_kind, | ||
)? else { | ||
return ecx.make_canonical_response(Certainty::AMBIGUOUS); | ||
}; | ||
structural_traits::extract_tupled_inputs_and_output_from_callable( | ||
tcx, | ||
goal.predicate.self_ty(), | ||
goal_kind, | ||
)? else { | ||
return ecx.make_canonical_response(Certainty::AMBIGUOUS); | ||
}; | ||
|
||
// FIXME(non_lifetime_binders): Higher-ranked Fn trait candidates are not (yet) supported. | ||
// Make sure that the inputs/output don't capture any placeholder types. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree with this. Canonicalization also maps I generally think that this check should happen somewhere else 🤔 |
||
if (goal.predicate.projection_ty.substs[1], goal.predicate.term) | ||
.has_non_region_placeholders() | ||
{ | ||
return Err(NoSolution); | ||
} | ||
|
||
let output_is_sized_pred = tupled_inputs_and_output | ||
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -760,7 +760,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
}; | ||
|
||
let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs); | ||
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; | ||
|
||
// FIXME(non_lifetime_binders): Perform the equivalent of a "leak check" here. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we could let this succeed, then error out during HIR typeck or something, though it would be harder to point to the source of the error. |
||
let mut nested = self.infcx.commit_if_ok(|_| { | ||
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; | ||
|
||
if self | ||
.infcx | ||
.resolve_vars_if_possible(substs.as_closure().sig()) | ||
.has_non_region_placeholders() | ||
{ | ||
return Err(SelectionError::Unimplemented); | ||
} | ||
|
||
Ok(nested) | ||
})?; | ||
|
||
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations"); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
// Unit test for the "user substitutions" that are annotated on each | ||
// node. | ||
|
||
// compile-flags:-Zverbose | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test's output changes with the modification to how bound/placeholder tys print under |
||
|
||
#![feature(rustc_attrs)] | ||
|
||
// Note: we reference the names T and U in the comments below. | ||
|
@@ -26,7 +24,7 @@ fn main() { | |
let x = foo::<u32>; | ||
x(22); | ||
|
||
let x = foo::<&'static u32>; //~ ERROR [&ReStatic u32] | ||
let x = foo::<&'static u32>; //~ ERROR [&'static u32] | ||
x(&22); | ||
|
||
// Here: we only want the `T` to be given, the rest should be variables. | ||
|
@@ -41,7 +39,7 @@ fn main() { | |
x(&22, 44, 66); | ||
|
||
// Here: all are given and we have a lifetime. | ||
let x = <u8 as Bazoom<&'static u16>>::method::<u32>; //~ ERROR [u8, &ReStatic u16, u32] | ||
let x = <u8 as Bazoom<&'static u16>>::method::<u32>; //~ ERROR [u8, &'static u16, u32] | ||
x(&22, &44, 66); | ||
|
||
// Here: we want in particular that *only* the method `U` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes | ||
--> $DIR/closure-infer.rs:4:12 | ||
| | ||
LL | #![feature(non_lifetime_binders)] | ||
| ^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information | ||
= note: `#[warn(incomplete_features)]` on by default | ||
|
||
error[E0277]: expected a `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
--> $DIR/closure-infer.rs:12:15 | ||
| | ||
LL | fn take2() -> impl for<T> Fn(T) -> T { | ||
| ^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
| | ||
= help: the trait `Fn<(T,)>` is not implemented for closure `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
|
||
error[E0277]: expected a `FnOnce<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
--> $DIR/closure-infer.rs:12:15 | ||
| | ||
LL | fn take2() -> impl for<T> Fn(T) -> T { | ||
| ^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
| | ||
= help: the trait `FnOnce<(T,)>` is not implemented for closure `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
|
||
error[E0277]: expected a `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
--> $DIR/closure-infer.rs:20:10 | ||
| | ||
LL | take(|x| x) | ||
| ---- ^^^^^ expected an `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
| | | ||
| required by a bound introduced by this call | ||
| | ||
= help: the trait `Fn<(T,)>` is not implemented for closure `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
note: required by a bound in `take` | ||
--> $DIR/closure-infer.rs:7:18 | ||
| | ||
LL | fn take(id: impl for<T> Fn(T) -> T) { | ||
| ^^^^^^^^^^^^^^^^^ required by this bound in `take` | ||
|
||
error[E0277]: expected a `FnOnce<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
--> $DIR/closure-infer.rs:20:10 | ||
| | ||
LL | take(|x| x) | ||
| ---- ^^^^^ expected an `FnOnce<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
| | | ||
| required by a bound introduced by this call | ||
| | ||
= help: the trait `FnOnce<(T,)>` is not implemented for closure `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
note: required by a bound in `take` | ||
--> $DIR/closure-infer.rs:7:34 | ||
| | ||
LL | fn take(id: impl for<T> Fn(T) -> T) { | ||
| ^ required by this bound in `take` | ||
|
||
error: aborting due to 4 previous errors; 1 warning emitted | ||
|
||
For more information about this error, try `rustc --explain E0277`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes | ||
--> $DIR/closure-infer.rs:4:12 | ||
| | ||
LL | #![feature(non_lifetime_binders)] | ||
| ^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information | ||
= note: `#[warn(incomplete_features)]` on by default | ||
|
||
error[E0277]: expected a `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
--> $DIR/closure-infer.rs:12:15 | ||
| | ||
LL | fn take2() -> impl for<T> Fn(T) -> T { | ||
| ^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
| | ||
= help: the trait `Fn<(T,)>` is not implemented for closure `[closure@$DIR/closure-infer.rs:16:5: 16:8]` | ||
|
||
error[E0271]: type mismatch resolving `<[closure@closure-infer.rs:16:5] as FnOnce<(T,)>>::Output == T` | ||
--> $DIR/closure-infer.rs:12:15 | ||
| | ||
LL | fn take2() -> impl for<T> Fn(T) -> T { | ||
| ^^^^^^^^^^^^^^^^^^^^^^ types differ | ||
|
||
error[E0277]: expected a `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
--> $DIR/closure-infer.rs:20:10 | ||
| | ||
LL | take(|x| x) | ||
| ---- ^^^^^ expected an `Fn<(T,)>` closure, found `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
| | | ||
| required by a bound introduced by this call | ||
| | ||
= help: the trait `Fn<(T,)>` is not implemented for closure `[closure@$DIR/closure-infer.rs:20:10: 20:13]` | ||
note: required by a bound in `take` | ||
--> $DIR/closure-infer.rs:7:18 | ||
| | ||
LL | fn take(id: impl for<T> Fn(T) -> T) { | ||
| ^^^^^^^^^^^^^^^^^ required by this bound in `take` | ||
|
||
error[E0271]: type mismatch resolving `<[closure@closure-infer.rs:20:10] as FnOnce<(T,)>>::Output == T` | ||
--> $DIR/closure-infer.rs:20:10 | ||
| | ||
LL | take(|x| x) | ||
| ---- ^^^^^ types differ | ||
| | | ||
| required by a bound introduced by this call | ||
| | ||
note: required by a bound in `take` | ||
--> $DIR/closure-infer.rs:7:34 | ||
| | ||
LL | fn take(id: impl for<T> Fn(T) -> T) { | ||
| ^ required by this bound in `take` | ||
|
||
error: aborting due to 4 previous errors; 1 warning emitted | ||
|
||
Some errors have detailed explanations: E0271, E0277. | ||
For more information about an error, try `rustc --explain E0271`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// revisions: classic next | ||
//[next] compile-flags: -Ztrait-solver=next | ||
|
||
#![feature(non_lifetime_binders)] | ||
//~^ WARNING the feature `non_lifetime_binders` is incomplete | ||
|
||
fn take(id: impl for<T> Fn(T) -> T) { | ||
id(0); | ||
id(""); | ||
} | ||
|
||
fn take2() -> impl for<T> Fn(T) -> T { | ||
//~^ ERROR expected a `Fn<(T,)>` closure, found | ||
//[classic]~| ERROR expected a `FnOnce<(T,)>` closure, found | ||
//[next]~| ERROR type mismatch resolving | ||
|x| x | ||
} | ||
|
||
fn main() { | ||
take(|x| x) | ||
//~^ ERROR expected a `Fn<(T,)>` closure, found | ||
//[classic]~| ERROR expected a `FnOnce<(T,)>` closure, found | ||
//[next]~| ERROR type mismatch resolving | ||
} |
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.
is this check not enough to prevent ICEs here? also, can you check that
!inferred_sig.has_placeholders
because inferring a closure signature to contain placeholders is wrong as the closure type is used outside of the binder which created themThere 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.
No, this just makes sure that we just don't eagerly deduce a closure signature from pending obligations.
We can still infer placeholders when we process pending obligations later, so we'd need to do something after typechecking to make sure that the closures don't capture any type placeholders.
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 agree that check here along with
!inferred_sig.has_placeholders
should be enough.We keep getting ICE because of #109505 and probably because we're not using ty vars in the root universe.