-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
std::mem::transmute
doesn't compile even though both types are guaranteed to have the same size.
#116585
Comments
std::mem::transmute
doesn't compile when both types are guaranteed to have the same size.std::mem::transmute
doesn't compile even though both types are guaranteed to have the same size.
Could you provide an example without dependencies. |
Yes, made an MRE pub struct Foo<const SIZE: usize> {
bar: [usize; SIZE],
}
impl <const SIZE: usize> Foo<SIZE> {
fn builder() -> FooBuilder<SIZE, false> {
FooBuilder::new()
}
}
pub struct FooBuilder<const SIZE: usize, const __BUILDER_CONST: bool> {
bar: Option<[usize; SIZE]>,
}
impl<const SIZE: usize> FooBuilder<SIZE, false> {
pub fn new() -> FooBuilder<SIZE, false> {
FooBuilder {
bar: None,
}
}
}
impl<const SIZE: usize> FooBuilder<SIZE, false> {
pub fn bar(mut self, bar: [usize; SIZE]) -> FooBuilder<SIZE, true> {
self.bar = Some(bar);
unsafe { std::mem::transmute(self) }
// ^ Should work, because the value of `__BUILDER_CONST` has no influence
// on the layout of the struct, and `SIZE` is identical
}
}
impl<const SIZE: usize> FooBuilder<SIZE, true> {
pub fn build(self) -> Foo<SIZE> {
Foo::<SIZE> {
bar: self.bar.unwrap()
}
}
} |
I opened up the compiler to see what's going on and after playing around with it, it makes sense for this to not compile because Code below compiles fine. const hey:bool = false;
pub struct Foo<const SIZE: usize> {
bar: [usize; SIZE],
}
impl <const SIZE: usize> Foo<SIZE> {
fn builder() -> FooBuilder<SIZE, hey> {
FooBuilder::new()
}
}
pub struct FooBuilder<const SIZE: usize, const __BUILDER_CONST: bool> {
bar: Option<[usize; SIZE]>,
}
impl<const SIZE: usize> FooBuilder<SIZE, hey> {
pub fn new() -> FooBuilder<SIZE, hey> {
FooBuilder {
bar: None,
}
}
}
impl<const SIZE: usize> FooBuilder<SIZE, hey> {
pub fn bar(mut self, bar: [usize; SIZE]) -> FooBuilder<SIZE, hey> {
self.bar = Some(bar);
unsafe { std::mem::transmute(self) }
}
}
impl<const SIZE: usize> FooBuilder<SIZE, hey> {
pub fn build(self) -> Foo<SIZE> {
Foo::<SIZE> {
bar: self.bar.unwrap()
}
}
} |
Minimized: #![allow(dead_code)]
// an explicit repr does not resolve the issue
pub struct UwU<const N: usize, const M: usize> {
_arr: [u8; N],
}
// note: -> UwU<N, N> or any other value that's not exactly M fails to compile too
fn meow<const N: usize, const M: usize>(x: UwU<N, M>) -> UwU<N, 10> {
unsafe { core::mem::transmute(x) }
} Having a type with a size dependent on a const generic and then adding any const generic that does not match between the type, whether or not it affects the final size of the computation, causes the transmute to not compile. |
Note that in the general case, transmuting between |
Yeah transmute size checks aren't infinitely smart. I'm not familiar with how they work though, so I can't help here. There's always the work-around of using |
I can provide two interesting cases, and the error reporting is misleading. Seems like the compiler can infer that T is of the same size, but fails to reason that A is always the same A use std::marker::PhantomData;
use std::mem::transmute;
struct S<T> {
a: PhantomData<T>,
}
struct Foo<T, A> {
s: T,
a: A,
}
fn foo<A>(a: A) -> Foo<S<u64>, A> {
unsafe {
let foo = Foo {
s: S {
a: PhantomData::<u32>,
},
a,
};
transmute(foo)
}
}
fn bar() -> Foo<S<u64>, ()> {
unsafe {
let bar = Foo {
s: S {
a: PhantomData::<u32>,
},
a: ()
};
transmute(bar)
}
}
|
Let me preface that I'm actively looking into other options than transmute, but I expected this to work, because it could basically be an no-op. Also the doc on
transmute
says: "Compilation will fail if this is not guaranteed", and these types are essentially the same.I'm working on a
Builder
derive and I'm working on the implementation of the setters. I was trying something new.I tried this code:
It's part of a bigger piece of code:
I expected to see this happen: Because both the input and the output are guaranteed to be the same size, I expected it to compile.
Instead, this happened: It didn't compile, and I got this error:
Meta
rustc --version --verbose
:I also ran it on the latest nigthly and stable, with the same response
Backtrace
I'm not sure what I'm doing wrong but if I run `cargo test --workspace -- -Z macro-backtrace` I only get this:
The text was updated successfully, but these errors were encountered: