handle/hack for arbitrary-self dyn receivers

This commit is contained in:
Ralf Jung 2023-09-09 12:08:49 +02:00
parent 4999000da1
commit e00120906e

View File

@ -11,7 +11,7 @@ use rustc_middle::{
}, },
}; };
use rustc_span::sym; use rustc_span::sym;
use rustc_target::abi::FieldIdx; use rustc_target::abi::{self, FieldIdx};
use rustc_target::abi::{ use rustc_target::abi::{
call::{ArgAbi, FnAbi, PassMode}, call::{ArgAbi, FnAbi, PassMode},
Integer, Integer,
@ -289,39 +289,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
/// Unwrap types that are guaranteed a null-pointer-optimization /// Unwrap types that are guaranteed a null-pointer-optimization
fn unfold_npo(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, Ty<'tcx>> { fn unfold_npo(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
// Check if this is `Option` wrapping some type. // Check if this is `Option` wrapping some type.
let inner = match ty.kind() { let inner = match layout.ty.kind() {
ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => { ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => {
args[0].as_type().unwrap() args[0].as_type().unwrap()
} }
_ => { _ => {
// Not an `Option`. // Not an `Option`.
return Ok(ty); return Ok(layout);
} }
}; };
let inner = self.layout_of(inner)?;
// Check if the inner type is one of the NPO-guaranteed ones. // Check if the inner type is one of the NPO-guaranteed ones.
// For that we first unpeel transparent *structs* (but not unions). // For that we first unpeel transparent *structs* (but not unions).
let is_npo = |def: AdtDef<'tcx>| { let is_npo = |def: AdtDef<'tcx>| {
self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
}; };
let inner = self.unfold_transparent(self.layout_of(inner)?, /* may_unfold */ |def| { let inner = self.unfold_transparent(inner, /* may_unfold */ |def| {
// Stop at NPO tpyes so that we don't miss that attribute in the check below! // Stop at NPO tpyes so that we don't miss that attribute in the check below!
def.is_struct() && !is_npo(def) def.is_struct() && !is_npo(def)
}); });
Ok(match inner.ty.kind() { Ok(match inner.ty.kind() {
ty::Ref(..) | ty::FnPtr(..) => { ty::Ref(..) | ty::FnPtr(..) => {
// Option<&T> behaves like &T, and same for fn() // Option<&T> behaves like &T, and same for fn()
inner.ty inner
} }
ty::Adt(def, _) if is_npo(*def) => { ty::Adt(def, _) if is_npo(*def) => {
// Once we found a `nonnull_optimization_guaranteed` type, further strip off // Once we found a `nonnull_optimization_guaranteed` type, further strip off
// newtype structs from it to find the underlying ABI type. // newtype structs from it to find the underlying ABI type.
self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct()).ty self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct())
} }
_ => { _ => {
// Everything else we do not unfold. // Everything else we do not unfold.
ty layout
} }
}) })
} }
@ -331,28 +332,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// that only checking the `PassMode` is insufficient.) /// that only checking the `PassMode` is insufficient.)
fn layout_compat( fn layout_compat(
&self, &self,
caller_layout: TyAndLayout<'tcx>, caller: TyAndLayout<'tcx>,
callee_layout: TyAndLayout<'tcx>, callee: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, bool> { ) -> InterpResult<'tcx, bool> {
// Fast path: equal types are definitely compatible. // Fast path: equal types are definitely compatible.
if caller_layout.ty == callee_layout.ty { if caller.ty == callee.ty {
return Ok(true); return Ok(true);
} }
// 1-ZST are compatible with all 1-ZST (and with nothing else). // 1-ZST are compatible with all 1-ZST (and with nothing else).
if caller_layout.is_1zst() || callee_layout.is_1zst() { if caller.is_1zst() || callee.is_1zst() {
return Ok(caller_layout.is_1zst() && callee_layout.is_1zst()); return Ok(caller.is_1zst() && callee.is_1zst());
} }
// Unfold newtypes and NPO optimizations. // Unfold newtypes and NPO optimizations.
let caller_ty = let unfold = |layout: TyAndLayout<'tcx>| {
self.unfold_npo(self.unfold_transparent(caller_layout, /* may_unfold */ |_| true).ty)?; self.unfold_npo(self.unfold_transparent(layout, /* may_unfold */ |_def| true))
let callee_ty = };
self.unfold_npo(self.unfold_transparent(callee_layout, /* may_unfold */ |_| true).ty)?; let caller = unfold(caller)?;
let callee = unfold(callee)?;
// Now see if these inner types are compatible. // Now see if these inner types are compatible.
// Compatible pointer types. // Compatible pointer types. For thin pointers, we have to accept even non-`repr(transparent)`
let pointee_ty = |ty: Ty<'tcx>| { // things as compatible due to `DispatchFromDyn`. For instance, `Rc<i32>` and `*mut i32`
// must be compatible. So we just accept everything with Pointer ABI as compatible,
// even if this will accept some code that is not stably guaranteed to work.
// This also handles function pointers.
let thin_pointer = |layout: TyAndLayout<'tcx>| match layout.abi {
abi::Abi::Scalar(s) => match s.primitive() {
abi::Primitive::Pointer(addr_space) => Some(addr_space),
_ => None,
},
_ => None,
};
if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) {
return Ok(caller == callee);
}
// For wide pointers we have to get the pointee type.
let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option<Ty<'tcx>>> {
// We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`. // We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`.
Some(match ty.kind() { Ok(Some(match ty.kind() {
ty::Ref(_, ty, _) => *ty, ty::Ref(_, ty, _) => *ty,
ty::RawPtr(mt) => mt.ty, ty::RawPtr(mt) => mt.ty,
// We should only accept `Box` with the default allocator. // We should only accept `Box` with the default allocator.
@ -363,10 +380,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
{ {
args[0].expect_ty() args[0].expect_ty()
} }
_ => return None, _ => return Ok(None),
}) }))
}; };
if let (Some(left), Some(right)) = (pointee_ty(caller_ty), pointee_ty(callee_ty)) { if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) {
// This is okay if they have the same metadata type. // This is okay if they have the same metadata type.
let meta_ty = |ty: Ty<'tcx>| { let meta_ty = |ty: Ty<'tcx>| {
let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, |ty| ty); let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, |ty| ty);
@ -376,12 +393,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
); );
meta meta
}; };
return Ok(meta_ty(left) == meta_ty(right)); return Ok(meta_ty(caller) == meta_ty(callee));
}
// Compatible function pointer types.
if let (ty::FnPtr(..), ty::FnPtr(..)) = (caller_ty.kind(), callee_ty.kind()) {
return Ok(true);
} }
// Compatible integer types (in particular, usize vs ptr-sized-u32/u64). // Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
@ -392,14 +404,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
_ => return None, _ => return None,
}) })
}; };
if let (Some(left), Some(right)) = (int_ty(caller_ty), int_ty(callee_ty)) { if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) {
// This is okay if they are the same integer type. // This is okay if they are the same integer type.
return Ok(left == right); return Ok(caller == callee);
} }
// Fall back to exact equality. // Fall back to exact equality.
// FIXME: We are missing the rules for "repr(C) wrapping compatible types". // FIXME: We are missing the rules for "repr(C) wrapping compatible types".
Ok(caller_ty == callee_ty) Ok(caller == callee)
} }
fn check_argument_compat( fn check_argument_compat(