1
Fork 0

Rollup merge of #135228 - compiler-errors:normalizes-ur-dispatch, r=BoxyUwU

Improve `DispatchFromDyn` and `CoerceUnsized` impl validation

* Disallow arbitrary 1-ZST fields in `DispatchFromDyn` -- only `PhantomData`, and 1-ZSTs that mention no params (which is needed to support, e.g., the `Global` alloctor in `Box<T, U = Global>`).
* Don't allow coercing between non-ZSTs to ZSTs (since the previous check wasn't actually checking the field tys were the same before checking the layout...)
* Normalize the field before checking it's `PhantomData`.

Fixes #135215
Fixes #135214
Fixes #135220

r? ```@BoxyUwU``` or reassign
This commit is contained in:
Jubilee 2025-01-14 19:56:30 -08:00 committed by GitHub
commit f256f9ef22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 153 additions and 11 deletions

View file

@ -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

View file

@ -259,19 +259,37 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
let coerced_fields = fields
.iter()
.filter(|field| {
// Ignore PhantomData fields
let unnormalized_ty = tcx.type_of(field.did).instantiate_identity();
if tcx
.try_normalize_erasing_regions(
ty::TypingEnv::non_body_analysis(tcx, def_a.did()),
unnormalized_ty,
)
.unwrap_or(unnormalized_ty)
.is_phantom_data()
{
return false;
}
let ty_a = field.ty(tcx, args_a);
let ty_b = field.ty(tcx, args_b);
// FIXME: We could do normalization here, but is it really worth it?
if ty_a == ty_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;
}
}
if ty_a == ty_b {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {
span,
name: field.name,
@ -460,8 +478,16 @@ pub(crate) fn coerce_unsized_info<'tcx>(
.filter_map(|(i, f)| {
let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b));
if tcx.type_of(f.did).instantiate_identity().is_phantom_data() {
// Ignore PhantomData fields
let unnormalized_ty = tcx.type_of(f.did).instantiate_identity();
if tcx
.try_normalize_erasing_regions(
ty::TypingEnv::non_body_analysis(tcx, def_a.did()),
unnormalized_ty,
)
.unwrap_or(unnormalized_ty)
.is_phantom_data()
{
return None;
}

View file

@ -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<T, U> DispatchFromDyn<WrapperWithExtraField<U>> for WrapperWithExtraField<T>
@ -35,7 +35,7 @@ LL | | where
LL | | T: Unsize<U>,
| |_________________^
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<T: ?Sized, U: ?Sized> DispatchFromDyn<OverAligned<U>> for OverAligned<T>

View file

@ -0,0 +1,25 @@
// We used to allow erroneous `DispatchFromDyn` impls whose RHS type contained
// fields that weren't ZSTs. I don't believe this was possible to abuse, but
// it's at least nice to give users better errors.
#![feature(arbitrary_self_types)]
#![feature(unsize)]
#![feature(dispatch_from_dyn)]
use std::marker::Unsize;
use std::ops::DispatchFromDyn;
struct Dispatchable<T: ?Sized, Z> {
_ptr: Box<T>,
z: Z,
}
impl<T, U> DispatchFromDyn<Dispatchable<U, i32>> for Dispatchable<T, ()>
//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions
where
T: Unsize<U> + ?Sized,
U: ?Sized,
{
}
fn main() {}

View file

@ -0,0 +1,16 @@
error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions
--> $DIR/dispatch-from-dyn-zst-transmute-zst-nonzst.rs:17:1
|
LL | / impl<T, U> DispatchFromDyn<Dispatchable<U, i32>> for Dispatchable<T, ()>
LL | |
LL | | where
LL | | T: Unsize<U> + ?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: `_ptr` (`Box<T>` to `Box<U>`), `z` (`()` to `i32`)
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0378`.

View file

@ -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<T: ?Sized>(PhantomData<fn(T) -> T>);
struct Foo<'a, U: ?Sized> {
token: IsSendToken<U>,
ptr: &'a U,
}
impl<'a, T, U> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T>
//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions
where
T: Unsize<U> + ?Sized,
U: ?Sized {}
trait Bar {
fn f(self: Foo<'_, Self>);
}
impl<U: ?Sized> Deref for Foo<'_, U> {
type Target = U;
fn deref(&self) -> &U {
self.ptr
}
}
fn main() {}

View file

@ -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<Foo<'a, U>> for Foo<'a, T>
LL | |
LL | | where
LL | | T: Unsize<U> + ?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<T>` to `IsSendToken<U>`), `ptr` (`&'a T` to `&'a U`)
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0378`.

View file

@ -0,0 +1,25 @@
//@ check-pass
#![feature(coerce_unsized, dispatch_from_dyn, unsize)]
use std::marker::Unsize;
use std::ops::{CoerceUnsized, DispatchFromDyn};
use std::marker::PhantomData;
trait Mirror {
type Assoc;
}
impl<T> Mirror for T {
type Assoc = T;
}
struct W<T: 'static> {
t: &'static T,
f: <PhantomData<T> as Mirror>::Assoc,
}
impl<T, U> CoerceUnsized<W<U>> for W<T> where T: Unsize<U> {}
impl<T, U> DispatchFromDyn<W<U>> for W<T> where T: Unsize<U> {}
fn main() {}