Skip to content

Commit e001209

Browse files
committed
handle/hack for arbitrary-self dyn receivers
1 parent 4999000 commit e001209

File tree

1 file changed

+44
-32
lines changed

1 file changed

+44
-32
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::{
1111
},
1212
};
1313
use rustc_span::sym;
14-
use rustc_target::abi::FieldIdx;
14+
use rustc_target::abi::{self, FieldIdx};
1515
use rustc_target::abi::{
1616
call::{ArgAbi, FnAbi, PassMode},
1717
Integer,
@@ -289,39 +289,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
289289
}
290290

291291
/// Unwrap types that are guaranteed a null-pointer-optimization
292-
fn unfold_npo(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, Ty<'tcx>> {
292+
fn unfold_npo(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
293293
// Check if this is `Option` wrapping some type.
294-
let inner = match ty.kind() {
294+
let inner = match layout.ty.kind() {
295295
ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => {
296296
args[0].as_type().unwrap()
297297
}
298298
_ => {
299299
// Not an `Option`.
300-
return Ok(ty);
300+
return Ok(layout);
301301
}
302302
};
303+
let inner = self.layout_of(inner)?;
303304
// Check if the inner type is one of the NPO-guaranteed ones.
304305
// For that we first unpeel transparent *structs* (but not unions).
305306
let is_npo = |def: AdtDef<'tcx>| {
306307
self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
307308
};
308-
let inner = self.unfold_transparent(self.layout_of(inner)?, /* may_unfold */ |def| {
309+
let inner = self.unfold_transparent(inner, /* may_unfold */ |def| {
309310
// Stop at NPO tpyes so that we don't miss that attribute in the check below!
310311
def.is_struct() && !is_npo(def)
311312
});
312313
Ok(match inner.ty.kind() {
313314
ty::Ref(..) | ty::FnPtr(..) => {
314315
// Option<&T> behaves like &T, and same for fn()
315-
inner.ty
316+
inner
316317
}
317318
ty::Adt(def, _) if is_npo(*def) => {
318319
// Once we found a `nonnull_optimization_guaranteed` type, further strip off
319320
// newtype structs from it to find the underlying ABI type.
320-
self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct()).ty
321+
self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct())
321322
}
322323
_ => {
323324
// Everything else we do not unfold.
324-
ty
325+
layout
325326
}
326327
})
327328
}
@@ -331,28 +332,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
331332
/// that only checking the `PassMode` is insufficient.)
332333
fn layout_compat(
333334
&self,
334-
caller_layout: TyAndLayout<'tcx>,
335-
callee_layout: TyAndLayout<'tcx>,
335+
caller: TyAndLayout<'tcx>,
336+
callee: TyAndLayout<'tcx>,
336337
) -> InterpResult<'tcx, bool> {
337338
// Fast path: equal types are definitely compatible.
338-
if caller_layout.ty == callee_layout.ty {
339+
if caller.ty == callee.ty {
339340
return Ok(true);
340341
}
341342
// 1-ZST are compatible with all 1-ZST (and with nothing else).
342-
if caller_layout.is_1zst() || callee_layout.is_1zst() {
343-
return Ok(caller_layout.is_1zst() && callee_layout.is_1zst());
343+
if caller.is_1zst() || callee.is_1zst() {
344+
return Ok(caller.is_1zst() && callee.is_1zst());
344345
}
345346
// Unfold newtypes and NPO optimizations.
346-
let caller_ty =
347-
self.unfold_npo(self.unfold_transparent(caller_layout, /* may_unfold */ |_| true).ty)?;
348-
let callee_ty =
349-
self.unfold_npo(self.unfold_transparent(callee_layout, /* may_unfold */ |_| true).ty)?;
347+
let unfold = |layout: TyAndLayout<'tcx>| {
348+
self.unfold_npo(self.unfold_transparent(layout, /* may_unfold */ |_def| true))
349+
};
350+
let caller = unfold(caller)?;
351+
let callee = unfold(callee)?;
350352
// Now see if these inner types are compatible.
351353

352-
// Compatible pointer types.
353-
let pointee_ty = |ty: Ty<'tcx>| {
354+
// Compatible pointer types. For thin pointers, we have to accept even non-`repr(transparent)`
355+
// things as compatible due to `DispatchFromDyn`. For instance, `Rc<i32>` and `*mut i32`
356+
// must be compatible. So we just accept everything with Pointer ABI as compatible,
357+
// even if this will accept some code that is not stably guaranteed to work.
358+
// This also handles function pointers.
359+
let thin_pointer = |layout: TyAndLayout<'tcx>| match layout.abi {
360+
abi::Abi::Scalar(s) => match s.primitive() {
361+
abi::Primitive::Pointer(addr_space) => Some(addr_space),
362+
_ => None,
363+
},
364+
_ => None,
365+
};
366+
if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) {
367+
return Ok(caller == callee);
368+
}
369+
// For wide pointers we have to get the pointee type.
370+
let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option<Ty<'tcx>>> {
354371
// We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`.
355-
Some(match ty.kind() {
372+
Ok(Some(match ty.kind() {
356373
ty::Ref(_, ty, _) => *ty,
357374
ty::RawPtr(mt) => mt.ty,
358375
// 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> {
363380
{
364381
args[0].expect_ty()
365382
}
366-
_ => return None,
367-
})
383+
_ => return Ok(None),
384+
}))
368385
};
369-
if let (Some(left), Some(right)) = (pointee_ty(caller_ty), pointee_ty(callee_ty)) {
386+
if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) {
370387
// This is okay if they have the same metadata type.
371388
let meta_ty = |ty: Ty<'tcx>| {
372389
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> {
376393
);
377394
meta
378395
};
379-
return Ok(meta_ty(left) == meta_ty(right));
380-
}
381-
382-
// Compatible function pointer types.
383-
if let (ty::FnPtr(..), ty::FnPtr(..)) = (caller_ty.kind(), callee_ty.kind()) {
384-
return Ok(true);
396+
return Ok(meta_ty(caller) == meta_ty(callee));
385397
}
386398

387399
// 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> {
392404
_ => return None,
393405
})
394406
};
395-
if let (Some(left), Some(right)) = (int_ty(caller_ty), int_ty(callee_ty)) {
407+
if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) {
396408
// This is okay if they are the same integer type.
397-
return Ok(left == right);
409+
return Ok(caller == callee);
398410
}
399411

400412
// Fall back to exact equality.
401413
// FIXME: We are missing the rules for "repr(C) wrapping compatible types".
402-
Ok(caller_ty == callee_ty)
414+
Ok(caller == callee)
403415
}
404416

405417
fn check_argument_compat(

0 commit comments

Comments
 (0)