diff --git a/tests/fail/validity/dyn-transmute-inner-binder.rs b/tests/fail/validity/dyn-transmute-inner-binder.rs new file mode 100644 index 0000000000..7de4aef422 --- /dev/null +++ b/tests/fail/validity/dyn-transmute-inner-binder.rs @@ -0,0 +1,30 @@ +// Test that transmuting from `&dyn Trait` to `&dyn Trait fn(&'a ())>` is UB. +// +// The vtable of `() as Trait` and `() as Trait fn(&'a ())>` can have +// different entries and, because in the former the entry for `foo` is vacant, this test will +// segfault at runtime. + +trait Trait { + fn foo(&self) + where + U: HigherRanked, + { + } +} +impl Trait for T {} + +trait HigherRanked {} +impl HigherRanked for for<'a> fn(&'a ()) {} + +// 2nd candidate is required so that selecting `(): Trait` will +// evaluate the candidates and fail the leak check instead of returning the +// only applicable candidate. +trait Unsatisfied {} +impl HigherRanked for T {} + +fn main() { + let x: &dyn Trait = &(); + let y: &dyn Trait fn(&'a ())> = unsafe { std::mem::transmute(x) }; + //~^ ERROR: wrong trait in wide pointer vtable + y.foo(); +} diff --git a/tests/fail/validity/dyn-transmute-inner-binder.stderr b/tests/fail/validity/dyn-transmute-inner-binder.stderr new file mode 100644 index 0000000000..cfdf279a60 --- /dev/null +++ b/tests/fail/validity/dyn-transmute-inner-binder.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait fn(&'a ())>`, but encountered `Trait` + --> tests/fail/validity/dyn-transmute-inner-binder.rs:LL:CC + | +LL | let y: &dyn Trait fn(&'a ())> = unsafe { std::mem::transmute(x) }; + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait fn(&'a ())>`, but encountered `Trait` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/validity/dyn-transmute-inner-binder.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/pass/dyn-upcast.rs b/tests/pass/dyn-upcast.rs index 6f8adc0964..875891df23 100644 --- a/tests/pass/dyn-upcast.rs +++ b/tests/pass/dyn-upcast.rs @@ -9,6 +9,7 @@ fn main() { drop_principal(); modulo_binder(); modulo_assoc(); + bidirectional_subtyping(); } fn vtable_nop_cast() { @@ -531,3 +532,32 @@ fn modulo_assoc() { (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0); } + +fn bidirectional_subtyping() { + // Test that transmuting between subtypes of dyn traits is fine, even in the + // "wrong direction", i.e. going from a lower-ranked to a higher-ranked dyn trait. + // Note that compared to the `dyn-transmute-inner-binder` test, the `for` is on the + // *outside* here! + + trait Trait {} + impl Trait for T {} + + struct Wrapper(T); + + let x: &dyn Trait = &(); + let _y: &dyn for<'a> Trait = unsafe { std::mem::transmute(x) }; + + let x: &dyn for<'a> Trait = &(); + let _y: &dyn Trait = unsafe { std::mem::transmute(x) }; + + let x: &dyn Trait> = &(); + let _y: &dyn for<'a> Trait> = unsafe { std::mem::transmute(x) }; + + let x: &dyn for<'a> Trait> = &(); + let _y: &dyn Trait> = unsafe { std::mem::transmute(x) }; + + // This lowers to a ptr-to-ptr cast (which behaves like a transmute) + // and not an unsizing coercion: + let x: *const dyn for<'a> Trait<&'a ()> = &(); + let _y: *const Wrapper> = x as _; +}