rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.06k stars 12.69k forks source link

Passing pointer to value of type alias with `impl Trait` through function breaks this type #123983

Open nikvoid opened 6 months ago

nikvoid commented 6 months ago

I tried this code:

(1) I originally encountered this issue with pointers

#![feature(type_alias_impl_trait)]
struct Buffered<T>(T);
struct Inner<T, U>(T, U);
struct Regular;

trait Tr {}

type Stream = Buffered<Inner<Regular, impl Tr>>;

struct Ext(u64);

fn transform_to_ext(s: *mut Stream) -> *mut Ext {
    s.cast()
}

fn opaque() -> impl Tr + 'static {
    struct O(u64);
    impl Tr for O {}

    O(3)
}

fn wrap<R, T: Tr>(r: R, o: T) -> Inner<R, T> {
    Inner(r, o)
}

fn bug() -> *mut Ext {
    let inner = wrap(Regular, opaque());
    let boxed = Box::new(Buffered(inner));
    // *mut Stream -> *mut Ext, this should be fine
    transform_to_ext(Box::leak(boxed) as *mut Stream)
}

// fn works() -> *mut Stream {
//     let inner = wrap(Regular, opaque());
//     let boxed = Box::new(Buffered(inner));
//     // *mut Stream -> *mut Ext, this should be fine
//     Box::leak(boxed) as *mut Stream
// }

(2) If you remove pointers, there is the same-ish error, but with more (pointless for me though) diagnostics:

#![feature(type_alias_impl_trait)]
struct Buffered<T>(T);
struct Inner<T, U>(T, U);
struct Regular;

trait Tr {}

type Stream = Buffered<Inner<Regular, impl Tr>>;

struct Ext(u64);

fn transform_to_ext(s: Stream) -> Ext {
    unimplemented!()
}

fn opaque() -> impl Tr {
    struct O(u64);
    impl Tr for O {}

    O(3)
}

fn wrap<R, T: Tr>(r: R, o: T) -> Inner<R, T> {
    Inner(r, o)
}

fn bug() -> Ext {
    let inner = wrap(Regular, opaque());
    transform_to_ext(Buffered(inner))
}

I expected to see this happen: no errors

Instead, this happened: (1)

error[E0606]: casting `&mut Buffered<Inner<Regular, impl Tr + 'static>>` as `*mut Buffered<Inner<Regular, Stream::{opaque#0}>>` is invalid
  --> src/main.rs:31:26
   |
31 |         transform_to_ext(Box::leak(boxed) as *mut Stream)
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

(2)

error[E0308]: mismatched types
  --> src/main.rs:29:31
   |
8  | type Stream = Buffered<Inner<Regular, impl Tr>>;
   |                                       ------- the expected opaque type
...
16 | fn opaque() -> impl Tr {
   |                ------- the found opaque type
...
29 |     transform_to_ext(Buffered(inner))
   |                      -------- ^^^^^ expected `Inner<Regular, Stream::{opaque#0}>`, found `Inner<Regular, impl Tr>`
   |                      |
   |                      arguments to this struct are incorrect
   |
   = note: expected struct `Inner<_, Stream::{opaque#0}>`
              found struct `Inner<_, impl Tr>`
   = note: distinct uses of `impl Trait` result in different opaque types
help: the type constructed contains `Inner<Regular, impl Tr>` due to the type of the argument passed
  --> src/main.rs:29:22
   |
29 |     transform_to_ext(Buffered(inner))
   |                      ^^^^^^^^^-----^
   |                               |
   |                               this argument influences the type of `Buffered`
note: tuple struct defined here
  --> src/main.rs:2:8
   |
2  | struct Buffered<T>(T);
   |        ^^^^^^^^

Meta

rustc --version --verbose:

rustc 1.79.0-nightly (0d8b3346a 2024-04-14)
binary: rustc
commit-hash: 0d8b3346a3992ab11ea35ff0fb95a6864b91f797
commit-date: 2024-04-14
host: x86_64-pc-windows-msvc
release: 1.79.0-nightly
LLVM version: 18.1.3
nikvoid commented 6 months ago

Follow-up: type cycle when using transmute on mutable reference to get pointer

fn bug() -> *mut Ext {
    let inner = wrap(Regular, opaque());
    let boxed = Box::new(Buffered(inner));
    // *mut Stream -> *mut Ext, this should be fine
    transform_to_ext(unsafe { std::mem::transmute(Box::leak(boxed)) })
}
error[E0391]: cycle detected when computing type of `Stream::{opaque#0}`
  --> src/main.rs:8:39
   |
8  | type Stream = Buffered<Inner<Regular, impl Tr>>;
   |                                       ^^^^^^^
   |
note: ...which requires computing type of opaque `Stream::{opaque#0}`...
  --> src/main.rs:8:39
   |
8  | type Stream = Buffered<Inner<Regular, impl Tr>>;
   |                                       ^^^^^^^
note: ...which requires type-checking `bug`...
  --> src/main.rs:27:1
   |
27 | fn bug() -> *mut Ext {
   | ^^^^^^^^^^^^^^^^^^^^
   = note: ...which requires computing layout of `*mut Buffered<Inner<Regular, Stream::{opaque#0}>>`...
   = note: ...which requires normalizing `*mut Buffered<Inner<Regular, Stream::{opaque#0}>>`...
   = note: ...which again requires computing type of `Stream::{opaque#0}`, completing the cycle
note: cycle used when checking that `Stream::{opaque#0}` is well-formed
  --> src/main.rs:8:39
   |
8  | type Stream = Buffered<Inner<Regular, impl Tr>>;
   |                                       ^^^^^^^
   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
nikvoid commented 6 months ago

Another follow-up: this works

fn workaround() -> *mut Ext {
    fn eyepatch() -> *mut Stream {
        let inner = wrap(Regular, opaque());
        let boxed = Box::new(Buffered(inner));
        Box::leak(boxed) as *mut _
    }
    transform_to_ext(eyepatch())
}
compiler-errors commented 6 months ago
fn bug() -> *mut Ext {
    let inner = wrap(Regular, opaque());
    let boxed = Box::new(Buffered(inner));
    // *mut Stream -> *mut Ext, this should be fine
    transform_to_ext(Box::leak(boxed) as *mut Stream)
}

This is attempting to define the type of the Stream TAIT, but it doesn't follow the rule that the TAIT must be mentioned in the signature of the function. Adding where Stream: to the where clause should fix it, but that's kind of a hack.

We're (@oli-obk / me / @traviscross, among others) are thinking about changing the rules for defining TAITs anyways (e.g. a #[defines] attribute), so maybe this hack will be good enough for now.

nikvoid commented 6 months ago

Thanks for pointing it out, this hack looks better than inner function