Skip to content

Commit

Permalink
Rollup merge of rust-lang#124297 - oli-obk:define_opaque_types13, r=j…
Browse files Browse the repository at this point in the history
…ackh726

Allow coercing functions whose signature differs in opaque types in their defining scope into a shared function pointer type

r? `@compiler-errors`

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

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

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

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 => bar::<()>,
    };
    todo!()
}
```

cc rust-lang#116652

This is very similar to rust-lang#123794, and with the same rationale:

> this is for consistency with `-Znext-solver`. the new solver does not have the concept of "non-defining use of opaque" right now and we would like to ideally keep it that way. Moving to `DefineOpaqueTypes::Yes` in more cases removes subtlety from the type system. Right now we have to be careful when relating `Opaque` with another type as the behavior changes depending on whether we later use the `Opaque` or its hidden type directly (even though they are equal), if that later use is with `DefineOpaqueTypes::No`*
  • Loading branch information
matthiaskrgr authored May 23, 2024
2 parents 39d2f2a + c24148e commit abcf400
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 1 deletion.
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 @@ -1159,7 +1159,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let sig = self
.at(cause, self.param_env)
.trace(prev_ty, new_ty)
.lub(DefineOpaqueTypes::No, a_sig, b_sig)
.lub(DefineOpaqueTypes::Yes, a_sig, b_sig)
.map(|ok| self.register_infer_ok_obligations(ok))?;

// Reify both sides and return the reified fn pointer type.
Expand Down
57 changes: 57 additions & 0 deletions tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! Test that coercing between function items of different functions works,
//! as long as their signatures match. The resulting value is a function pointer.

#![feature(type_alias_impl_trait)]

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

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

type F = impl Sized;

fn f(a: F) {
let mut x = bar::<F>;
x = foo::<()>; //~ ERROR: mismatched types
x(a);
x(());
}

type I = impl Sized;

fn i(a: I) {
let mut x = bar::<()>;
x = foo::<I>; //~ ERROR: mismatched types
x(a);
x(());
}

type J = impl Sized;

fn j(a: J) {
let x = match true {
true => bar::<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 => bar::<()>,
};
todo!()
}

fn main() {}
38 changes: 38 additions & 0 deletions tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
error[E0308]: mismatched types
--> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:18:9
|
LL | type F = impl Sized;
| ---------- the expected opaque type
...
LL | let mut x = bar::<F>;
| -------- expected due to this value
LL | x = foo::<()>;
| ^^^^^^^^^ expected fn item, found a different fn item
|
= note: expected fn item `fn(F) -> F {bar::<F>}`
found fn item `fn(()) {foo::<()>}`

error[E0308]: mismatched types
--> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:27:9
|
LL | fn foo<T>(t: T) -> T {
| -------------------- function `foo` defined here
...
LL | type I = impl Sized;
| ---------- the found opaque type
...
LL | let mut x = bar::<()>;
| --------- expected due to this value
LL | x = foo::<I>;
| ^^^^^^^^ expected fn item, found a different fn item
|
= note: expected fn item `fn(()) {bar::<()>}`
found fn item `fn(I) -> I {foo::<I>}`
help: use parentheses to call this function
|
LL | x = foo::<I>(/* I */);
| +++++++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.

0 comments on commit abcf400

Please sign in to comment.