From 3c3186148e4a66a507e606f191c35ed49d873b08 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 Jan 2025 00:28:47 +0000 Subject: [PATCH] Don't allow transmuting ZSTs in dispatch_from_dyn impl --- compiler/rustc_hir_analysis/messages.ftl | 2 +- .../src/coherence/builtin.rs | 17 +++++++--- .../ui/invalid_dispatch_from_dyn_impls.stderr | 4 +-- .../self/dispatch-from-dyn-zst-transmute.rs | 34 +++++++++++++++++++ .../dispatch-from-dyn-zst-transmute.stderr | 16 +++++++++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 tests/ui/self/dispatch-from-dyn-zst-transmute.rs create mode 100644 tests/ui/self/dispatch-from-dyn-zst-transmute.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 0c3ed9b5c60..d7ab6eca84b 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -135,7 +135,7 @@ hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]` -hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else +hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else .note = extra field `{$name}` of type `{$ty}` is not allowed hir_analysis_drop_impl_negative = negative `Drop` impls are not supported diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3b98f358b1e..760c09a1e72 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -259,16 +259,25 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() let coerced_fields = fields .iter() .filter(|field| { + // Ignore PhantomData fields + if tcx.type_of(field.did).instantiate_identity().is_phantom_data() { + return false; + } + let ty_a = field.ty(tcx, args_a); let ty_b = field.ty(tcx, args_b); + // Allow 1-ZSTs that don't mention type params. + // + // Allowing type params here would allow us to possibly transmute + // between ZSTs, which may be used to create library unsoundness. if let Ok(layout) = tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a)) + && layout.is_1zst() + && !ty_a.has_non_region_param() { - if layout.is_1zst() { - // ignore 1-ZST fields - return false; - } + // ignore 1-ZST fields + return false; } if ty_a == ty_b { diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.stderr b/tests/ui/invalid_dispatch_from_dyn_impls.stderr index 168ed37d0e6..02718334c73 100644 --- a/tests/ui/invalid_dispatch_from_dyn_impls.stderr +++ b/tests/ui/invalid_dispatch_from_dyn_impls.stderr @@ -1,4 +1,4 @@ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1 | LL | / impl DispatchFromDyn> for WrapperWithExtraField @@ -35,7 +35,7 @@ LL | | where LL | | T: Unsize, | |_________________^ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else --> $DIR/invalid_dispatch_from_dyn_impls.rs:46:1 | LL | / impl DispatchFromDyn> for OverAligned diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute.rs b/tests/ui/self/dispatch-from-dyn-zst-transmute.rs new file mode 100644 index 00000000000..57c255b4d7b --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-zst-transmute.rs @@ -0,0 +1,34 @@ +#![feature(arbitrary_self_types)] +#![feature(unsize)] +#![feature(dispatch_from_dyn)] + +use std::marker::PhantomData; +use std::marker::Unsize; +use std::ops::DispatchFromDyn; +use std::ops::Deref; + +struct IsSendToken(PhantomData T>); + +struct Foo<'a, U: ?Sized> { + token: IsSendToken, + ptr: &'a U, +} + +impl<'a, T, U> DispatchFromDyn> for Foo<'a, T> +//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions +where + T: Unsize + ?Sized, + U: ?Sized {} + +trait Bar { + fn f(self: Foo<'_, Self>); +} + +impl Deref for Foo<'_, U> { + type Target = U; + fn deref(&self) -> &U { + self.ptr + } +} + +fn main() {} diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr b/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr new file mode 100644 index 00000000000..5a8ae88b5f1 --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr @@ -0,0 +1,16 @@ +error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions + --> $DIR/dispatch-from-dyn-zst-transmute.rs:17:1 + | +LL | / impl<'a, T, U> DispatchFromDyn> for Foo<'a, T> +LL | | +LL | | where +LL | | T: Unsize + ?Sized, +LL | | U: ?Sized {} + | |_____________^ + | + = note: the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced + = note: currently, 2 fields need coercions: `token` (`IsSendToken` to `IsSendToken`), `ptr` (`&'a T` to `&'a U`) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0378`.