Auto merge of #112842 - lcnr:non-defining-use, r=oli-obk
check for non-defining uses of RPIT This PR requires defining uses of RPIT and the async functions return type to use unique generic parameters as type and const arguments, (mostly) fixing #111935. This changes the following snippet to an error (it compiled since 1.62): ```rust fn foo<T>() -> impl Sized { let _: () = foo::<u8>(); //~ ERROR non-defining use of `impl Sized` } ``` Since 1.62 we only checked that the generic arguments of opaque types are unique parameters for TAIT and ignored RPITs, so this PR changes the behavior here to be consistent. For defining uses which do not have unique params as arguments it is unclear how the hidden type should map to the generic params of the opaque. In the following snippet, should the hidden type of `foo<T>::opaque` be `T` or `u32`. ```rust fn foo<T>() -> impl Sized { let _: u32 = foo::<u32>(); foo::<T>() } ``` There are no crater regressions caused by this change. --- The same issue exists for lifetime arguments which is not fixed by this PR, currently resulting in an ICE in mir borrowck (I wasn't able to get an example which didn't ICE, it might be possible): ```rust fn foo<'a: 'a>() -> impl Sized { let _: &'static () = foo::<'static>(); //~^ ICE opaque type with non-universal region substs foo::<'a>() } ``` Fixing this for lifetimes as well is blocked on https://github.com/rust-lang/rust/issues/113916. Due to this issue, functions returning an RPIT with lifetime parameters equal in the region constraint graph would always result in an error, resulting in breakage found via crater: https://github.com/rust-lang/rust/pull/112842#issuecomment-1610057887 ```rust trait Trait<'a, 'b> {} impl Trait<'_, '_> for () {} struct Type<'a>(&'a ()); impl<'a> Type<'a> { // `'b == 'a` fn do_stuff<'b: 'a>(&'b self) -> impl Trait<'a, 'b> { // This fails as long there is something in the body // which adds the outlives constraints to the constraint graph. // // This is the case for nested closures. (|| ())() } } ```
This commit is contained in:
commit
e8459109bb
@ -371,40 +371,27 @@ fn check_opaque_type_parameter_valid(
|
||||
span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id);
|
||||
match opaque_ty_hir.expect_opaque_ty().origin {
|
||||
// No need to check return position impl trait (RPIT)
|
||||
// because for type and const parameters they are correct
|
||||
// by construction: we convert
|
||||
//
|
||||
// fn foo<P0..Pn>() -> impl Trait
|
||||
//
|
||||
// into
|
||||
//
|
||||
// type Foo<P0...Pn>
|
||||
// fn foo<P0..Pn>() -> Foo<P0...Pn>.
|
||||
//
|
||||
// For lifetime parameters we convert
|
||||
//
|
||||
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
|
||||
//
|
||||
// into
|
||||
//
|
||||
// type foo::<'p0..'pn>::Foo<'q0..'qm>
|
||||
// fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
|
||||
//
|
||||
// which would error here on all of the `'static` args.
|
||||
OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return Ok(()),
|
||||
// Check these
|
||||
OpaqueTyOrigin::TyAlias { .. } => {}
|
||||
}
|
||||
let is_ty_alias = match opaque_ty_hir.expect_opaque_ty().origin {
|
||||
OpaqueTyOrigin::TyAlias { .. } => true,
|
||||
OpaqueTyOrigin::AsyncFn(..) | OpaqueTyOrigin::FnReturn(..) => false,
|
||||
};
|
||||
|
||||
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
|
||||
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
|
||||
for (i, arg) in opaque_type_key.args.iter().enumerate() {
|
||||
if let Err(guar) = arg.error_reported() {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
let arg_is_param = match arg.unpack() {
|
||||
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
|
||||
GenericArgKind::Lifetime(lt) => {
|
||||
GenericArgKind::Lifetime(lt) if is_ty_alias => {
|
||||
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
|
||||
}
|
||||
// FIXME(#113916): we can't currently check for unique lifetime params,
|
||||
// see that issue for more. We will also have to ignore unused lifetime
|
||||
// params for RPIT, but that's comparatively trivial ✨
|
||||
GenericArgKind::Lifetime(_) => continue,
|
||||
GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@ fn test<T: Display>(t: T, recurse: bool) -> impl Display {
|
||||
let f = || {
|
||||
let i: u32 = test::<i32>(-1, false);
|
||||
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||
//~| ERROR expected generic type parameter, found `i32`
|
||||
println!("{i}");
|
||||
};
|
||||
if recurse {
|
||||
|
@ -1,3 +1,12 @@
|
||||
error[E0792]: expected generic type parameter, found `i32`
|
||||
--> $DIR/issue-99073-2.rs:9:22
|
||||
|
|
||||
LL | fn test<T: Display>(t: T, recurse: bool) -> impl Display {
|
||||
| - this generic parameter must be used with a generic type parameter
|
||||
LL | let f = || {
|
||||
LL | let i: u32 = test::<i32>(-1, false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/issue-99073-2.rs:9:22
|
||||
|
|
||||
@ -5,10 +14,11 @@ LL | let i: u32 = test::<i32>(-1, false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ expected `T`, got `u32`
|
||||
|
|
||||
note: previous use here
|
||||
--> $DIR/issue-99073-2.rs:16:5
|
||||
--> $DIR/issue-99073-2.rs:7:45
|
||||
|
|
||||
LL | t
|
||||
| ^
|
||||
LL | fn test<T: Display>(t: T, recurse: bool) -> impl Display {
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
@ -5,4 +5,5 @@ fn main() {
|
||||
fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() {
|
||||
move || f(fix(&f))
|
||||
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||
//~| ERROR expected generic type parameter, found `&F`
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
error[E0792]: expected generic type parameter, found `&F`
|
||||
--> $DIR/issue-99073.rs:6:11
|
||||
|
|
||||
LL | fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() {
|
||||
| - this generic parameter must be used with a generic type parameter
|
||||
LL | move || f(fix(&f))
|
||||
| ^^^^^^^^^^ expected `[closure@$DIR/issue-99073.rs:6:3: 6:10]`, got `G`
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/issue-99073.rs:6:13
|
||||
|
|
||||
LL | move || f(fix(&f))
|
||||
| ^^^^^^^ expected `[closure@$DIR/issue-99073.rs:6:3: 6:10]`, got `G`
|
||||
|
|
||||
note: previous use here
|
||||
--> $DIR/issue-99073.rs:6:3
|
||||
--> $DIR/issue-99073.rs:5:36
|
||||
|
|
||||
LL | move || f(fix(&f))
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
LL | fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() {
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
19
tests/ui/impl-trait/rpit/equal-lifetime-params-ok.rs
Normal file
19
tests/ui/impl-trait/rpit/equal-lifetime-params-ok.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// check-pass
|
||||
|
||||
// related to #113916, check that using RPITs in functions with lifetime params
|
||||
// which are constrained to be equal compiles.
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl Trait<'_, '_> for () {}
|
||||
fn pass<'a: 'b, 'b: 'a>() -> impl Trait<'a, 'b> {
|
||||
(|| {})()
|
||||
}
|
||||
|
||||
struct Foo<'a>(&'a ());
|
||||
impl<'a> Foo<'a> {
|
||||
fn bar<'b: 'a>(&'b self) -> impl Trait<'a, 'b> {
|
||||
let _: &'a &'b &'a ();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
14
tests/ui/impl-trait/rpit/non-defining-use.rs
Normal file
14
tests/ui/impl-trait/rpit/non-defining-use.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// Regression test for #111935 that non-defining uses of RPIT result in errors
|
||||
#![allow(unconditional_recursion)]
|
||||
fn foo<T>() -> impl Sized {
|
||||
let _: () = foo::<u8>(); //~ ERROR expected generic type parameter, found `u8`
|
||||
}
|
||||
|
||||
fn bar<T>(val: T) -> impl Sized {
|
||||
let _: u8 = bar(0u8);
|
||||
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||
//~| ERROR expected generic type parameter, found `u8`
|
||||
val
|
||||
}
|
||||
|
||||
fn main() {}
|
31
tests/ui/impl-trait/rpit/non-defining-use.stderr
Normal file
31
tests/ui/impl-trait/rpit/non-defining-use.stderr
Normal file
@ -0,0 +1,31 @@
|
||||
error[E0792]: expected generic type parameter, found `u8`
|
||||
--> $DIR/non-defining-use.rs:4:12
|
||||
|
|
||||
LL | fn foo<T>() -> impl Sized {
|
||||
| - this generic parameter must be used with a generic type parameter
|
||||
LL | let _: () = foo::<u8>();
|
||||
| ^^
|
||||
|
||||
error[E0792]: expected generic type parameter, found `u8`
|
||||
--> $DIR/non-defining-use.rs:8:12
|
||||
|
|
||||
LL | fn bar<T>(val: T) -> impl Sized {
|
||||
| - this generic parameter must be used with a generic type parameter
|
||||
LL | let _: u8 = bar(0u8);
|
||||
| ^^
|
||||
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/non-defining-use.rs:8:17
|
||||
|
|
||||
LL | let _: u8 = bar(0u8);
|
||||
| ^^^^^^^^ expected `T`, got `u8`
|
||||
|
|
||||
note: previous use here
|
||||
--> $DIR/non-defining-use.rs:7:22
|
||||
|
|
||||
LL | fn bar<T>(val: T) -> impl Sized {
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
Loading…
x
Reference in New Issue
Block a user