Skip to content

Commit

Permalink
Auto merge of #123794 - oli-obk:define_opaque_types2, r=<try>
Browse files Browse the repository at this point in the history
More DefineOpaqueTypes::Yes

This accepts more code on stable. It is now possible to have match arms return a function item `foo::<ConcreteType>` and a function item `foo::<OpaqueTypeInDefiningScope>` in another, and that will constrain `OpaqueTypeInDefiningScope` to have the hidden type `ConcreteType`. So the following function will now compile, but on master it errors with a type mismatch on the second match arm

```rust
// The function item whose generic params we want to merge.
fn foo<T>(t: T) -> T { t }
// Helper ensuring we can constrain `T` on `F` without explicitly specifying it
fn bind<T, F: FnOnce(T) -> T>(_: T, f: F) -> F { f }

fn k() -> impl Sized {
    let x = match true {
        true => {
            // `f` is `FnDef(foo, [infer_var])`
            let f = foo;
            // Get a value of an opaque type on stable
            let t = k();
            // this returns `FnDef(foo, [k::return])`
            bind(t, f)
        }
        false => foo::<()>,
    };
    todo!()
}
```

r? `@compiler-errors`
  • Loading branch information
bors committed Apr 12, 2024
2 parents 7bdae13 + 4abbda6 commit f0a46ff
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 4 deletions.
6 changes: 3 additions & 3 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
use rustc_type_ir::TyKind::*;
match (source.kind(), target.kind()) {
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, r_a, *r_b).is_ok()
if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::Yes, r_a, *r_b).is_ok()
&& mutbl_a == *mutbl_b =>
{
Ok(())
Expand Down Expand Up @@ -231,7 +231,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
}

if let Ok(ok) =
infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, ty_a, ty_b)
infcx.at(&cause, param_env).eq(DefineOpaqueTypes::Yes, ty_a, ty_b)
{
if ok.obligations.is_empty() {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {
Expand Down Expand Up @@ -437,7 +437,7 @@ pub fn coerce_unsized_info<'tcx>(
// we may have to evaluate constraint
// expressions in the course of execution.)
// See e.g., #41936.
if let Ok(ok) = infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, a, b) {
if let Ok(ok) = infcx.at(&cause, param_env).eq(DefineOpaqueTypes::Yes, a, b) {
if ok.obligations.is_empty() {
return None;
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1145,7 +1145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// are the same function and their parameters have a LUB.
match self.commit_if_ok(|_| {
self.at(cause, self.param_env).lub(
DefineOpaqueTypes::No,
DefineOpaqueTypes::Yes,
prev_ty,
new_ty,
)
Expand Down
57 changes: 57 additions & 0 deletions tests/ui/fn/fn_def_coercion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! Test that coercing between function items of the same function,
//! but with different args works.
fn foo<T>(t: T) -> T {
t
}

fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
let mut x = foo::<&'a ()>; //~ ERROR: lifetime may not live long enough
x = foo::<&'b ()>; //~ ERROR: lifetime may not live long enough
x = foo::<&'c ()>;
x(a);
x(b);
x(c);
}

fn g<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
let x = foo::<&'c ()>;
let _: &'c () = x(a); //~ ERROR lifetime may not live long enough
}

fn h<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
let x = foo::<&'a ()>;
let _: &'a () = x(c);
}

fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
let mut x = foo::<&'c ()>;
x = foo::<&'b ()>; //~ ERROR lifetime may not live long enough
x = foo::<&'a ()>; //~ ERROR lifetime may not live long enough
x(a);
x(b);
x(c);
}

fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
let x = match true {
true => foo::<&'b ()>, //~ ERROR lifetime may not live long enough
false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough
};
x(a);
x(b);
x(c);
}

fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
let x = match true {
true => foo::<&'c ()>,
false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough
};

x(a);
x(b); //~ ERROR lifetime may not live long enough
x(c);
}

fn main() {}
154 changes: 154 additions & 0 deletions tests/ui/fn/fn_def_coercion.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:9:17
|
LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let mut x = foo::<&'a ()>;
| ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:10:5
|
LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let mut x = foo::<&'a ()>;
LL | x = foo::<&'b ()>;
| ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

help: `'a` and `'b` must be the same: replace one with the other

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:19:12
|
LL | fn g<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'c` defined here
| |
| lifetime `'a` defined here
LL | let x = foo::<&'c ()>;
LL | let _: &'c () = x(a);
| ^^^^^^ type annotation requires that `'a` must outlive `'c`
|
= help: consider adding the following bound: `'a: 'c`

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:29:5
|
LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let mut x = foo::<&'c ()>;
LL | x = foo::<&'b ()>;
| ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:30:5
|
LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | x = foo::<&'a ()>;
| ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

help: `'a` and `'b` must be the same: replace one with the other
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:38:17
|
LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let x = match true {
LL | true => foo::<&'b ()>,
| ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:39:18
|
LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | false => foo::<&'a ()>,
| ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

help: `'a` and `'b` must be the same: replace one with the other
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:49:18
|
LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'c` defined here
| |
| lifetime `'a` defined here
...
LL | false => foo::<&'a ()>,
| ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'c`
|
= help: consider adding the following bound: `'a: 'c`
= note: requirement occurs because of a function pointer to `foo`
= note: the function `foo` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> $DIR/fn_def_coercion.rs:53:5
|
LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | x(b);
| ^^^^ argument requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`

help: the following changes may resolve your lifetime errors
|
= help: add bound `'a: 'c`
= help: add bound `'b: 'a`

error: aborting due to 9 previous errors

69 changes: 69 additions & 0 deletions tests/ui/fn/fn_def_opaque_coercion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Test that coercing between function items of the same function,
//! but with different generic args works.
//@check-pass

#![feature(type_alias_impl_trait)]

fn foo<T>(t: T) -> T {
t
}

type F = impl Sized;

fn f(a: F) {
let mut x = foo::<F>;
x = foo::<()>;
x(a);
x(());
}

type G = impl Sized;

fn g(a: G) {
let x = foo::<()>;
let _: () = x(a);
}

type H = impl Sized;

fn h(a: H) {
let x = foo::<H>;
let _: H = x(());
}

type I = impl Sized;

fn i(a: I) {
let mut x = foo::<()>;
x = foo::<I>;
x(a);
x(());
}

type J = impl Sized;

fn j(a: J) {
let x = match true {
true => foo::<J>,
false => foo::<()>,
};
x(a);
x(());
}

fn k() -> impl Sized {
fn bind<T, F: FnOnce(T) -> T>(_: T, f: F) -> F {
f
}
let x = match true {
true => {
let f = foo;
bind(k(), f)
}
false => foo::<()>,
};
todo!()
}

fn main() {}

0 comments on commit f0a46ff

Please sign in to comment.