From b1613ebc432ae495ecfcfa822548390c250849a8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 2 Dec 2023 12:25:01 +0100 Subject: codegen: panic when trying to compute size/align of extern type --- compiler/rustc_codegen_ssa/src/glue.rs | 126 -------------------- compiler/rustc_codegen_ssa/src/lib.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/intrinsic.rs | 22 ++-- compiler/rustc_codegen_ssa/src/mir/operand.rs | 6 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 4 +- compiler/rustc_codegen_ssa/src/size_of_val.rs | 152 ++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 145 deletions(-) delete mode 100644 compiler/rustc_codegen_ssa/src/glue.rs create mode 100644 compiler/rustc_codegen_ssa/src/size_of_val.rs (limited to 'compiler/rustc_codegen_ssa/src') diff --git a/compiler/rustc_codegen_ssa/src/glue.rs b/compiler/rustc_codegen_ssa/src/glue.rs deleted file mode 100644 index c34f1dbf856..00000000000 --- a/compiler/rustc_codegen_ssa/src/glue.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! -// -// Code relating to drop glue. - -use crate::common::IntPredicate; -use crate::meth; -use crate::traits::*; -use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::WrappingRange; - -pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - t: Ty<'tcx>, - info: Option, -) -> (Bx::Value, Bx::Value) { - let layout = bx.layout_of(t); - debug!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout); - if layout.is_sized() { - let size = bx.const_usize(layout.size.bytes()); - let align = bx.const_usize(layout.align.abi.bytes()); - return (size, align); - } - match t.kind() { - ty::Dynamic(..) => { - // Load size/align from vtable. - let vtable = info.unwrap(); - let size = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_SIZE) - .get_usize(bx, vtable); - let align = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ALIGN) - .get_usize(bx, vtable); - - // Size is always <= isize::MAX. - let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128; - bx.range_metadata(size, WrappingRange { start: 0, end: size_bound }); - // Alignment is always nonzero. - bx.range_metadata(align, WrappingRange { start: 1, end: !0 }); - - (size, align) - } - ty::Slice(_) | ty::Str => { - let unit = layout.field(bx, 0); - // The info in this case is the length of the str, so the size is that - // times the unit size. - ( - // All slice sizes must fit into `isize`, so this multiplication cannot (signed) wrap. - // NOTE: ideally, we want the effects of both `unchecked_smul` and `unchecked_umul` - // (resulting in `mul nsw nuw` in LLVM IR), since we know that the multiplication - // cannot signed wrap, and that both operands are non-negative. But at the time of writing, - // the `LLVM-C` binding can't do this, and it doesn't seem to enable any further optimizations. - bx.unchecked_smul(info.unwrap(), bx.const_usize(unit.size.bytes())), - bx.const_usize(unit.align.abi.bytes()), - ) - } - _ => { - // First get the size of all statically known fields. - // Don't use size_of because it also rounds up to alignment, which we - // want to avoid, as the unsized field's alignment could be smaller. - assert!(!t.is_simd()); - debug!("DST {} layout: {:?}", t, layout); - - let i = layout.fields.count() - 1; - let sized_size = layout.fields.offset(i).bytes(); - let sized_align = layout.align.abi.bytes(); - debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align); - let sized_size = bx.const_usize(sized_size); - let sized_align = bx.const_usize(sized_align); - - // Recurse to get the size of the dynamically sized field (must be - // the last field). - let field_ty = layout.field(bx, i).ty; - let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); - - // FIXME (#26403, #27023): We should be adding padding - // to `sized_size` (to accommodate the `unsized_align` - // required of the unsized field that follows) before - // summing it with `sized_size`. (Note that since #26403 - // is unfixed, we do not yet add the necessary padding - // here. But this is where the add would go.) - - // Return the sum of sizes and max of aligns. - let size = bx.add(sized_size, unsized_size); - - // Packed types ignore the alignment of their fields. - if let ty::Adt(def, _) = t.kind() { - if def.repr().packed() { - unsized_align = sized_align; - } - } - - // Choose max of two known alignments (combined value must - // be aligned according to more restrictive of the two). - let align = match ( - bx.const_to_opt_u128(sized_align, false), - bx.const_to_opt_u128(unsized_align, false), - ) { - (Some(sized_align), Some(unsized_align)) => { - // If both alignments are constant, (the sized_align should always be), then - // pick the correct alignment statically. - bx.const_usize(std::cmp::max(sized_align, unsized_align) as u64) - } - _ => { - let cmp = bx.icmp(IntPredicate::IntUGT, sized_align, unsized_align); - bx.select(cmp, sized_align, unsized_align) - } - }; - - // Issue #27023: must add any necessary padding to `size` - // (to make it a multiple of `align`) before returning it. - // - // Namely, the returned size should be, in C notation: - // - // `size + ((size & (align-1)) ? align : 0)` - // - // emulated via the semi-standard fast bit trick: - // - // `(size + (align-1)) & -align` - let one = bx.const_usize(1); - let addend = bx.sub(align, one); - let add = bx.add(size, addend); - let neg = bx.neg(align); - let size = bx.and(add, neg); - - (size, align) - } - } -} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index cd5eb77e06e..9b60f0844a0 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -51,10 +51,10 @@ pub mod codegen_attrs; pub mod common; pub mod debuginfo; pub mod errors; -pub mod glue; pub mod meth; pub mod mir; pub mod mono_item; +pub mod size_of_val; pub mod target_features; pub mod traits; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 136d06d561a..a5bffc33d39 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -4,8 +4,8 @@ use super::FunctionCx; use crate::common::IntPredicate; use crate::errors; use crate::errors::InvalidMonomorphization; -use crate::glue; use crate::meth; +use crate::size_of_val; use crate::traits::*; use crate::MemFlags; @@ -88,21 +88,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::va_end => bx.va_end(args[0].immediate()), sym::size_of_val => { let tp_ty = fn_args.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (llsize, _) = glue::size_and_align_of_dst(bx, tp_ty, Some(meta)); - llsize - } else { - bx.const_usize(bx.layout_of(tp_ty).size.bytes()) - } + let meta = + if let OperandValue::Pair(_, meta) = args[0].val { Some(meta) } else { None }; + let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); + llsize } sym::min_align_of_val => { let tp_ty = fn_args.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (_, llalign) = glue::size_and_align_of_dst(bx, tp_ty, Some(meta)); - llalign - } else { - bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes()) - } + let meta = + if let OperandValue::Pair(_, meta) = args[0].val { Some(meta) } else { None }; + let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); + llalign } sym::vtable_size | sym::vtable_align => { let vtable = args[0].immediate(); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index feee3ac03d0..e8c58f6b6f8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -2,7 +2,7 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::glue; +use crate::size_of_val; use crate::traits::*; use crate::MemFlags; @@ -466,13 +466,13 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { .ty; let OperandValue::Ref(llptr, Some(llextra), _) = self else { - bug!("store_unsized called with a sized value") + bug!("store_unsized called with a sized value (or with an extern type)") }; // Allocate an appropriate region on the stack, and copy the value into it. Since alloca // doesn't support dynamic alignment, we allocate an extra align - 1 bytes, and align the // pointer manually. - let (size, align) = glue::size_and_align_of_dst(bx, unsized_ty, Some(llextra)); + let (size, align) = size_of_val::size_and_align_of_dst(bx, unsized_ty, Some(llextra)); let one = bx.const_usize(1); let align_minus_1 = bx.sub(align, one); let size_extra = bx.add(size, align_minus_1); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 83425dee1a8..a3fb3b9d783 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -2,7 +2,7 @@ use super::operand::OperandValue; use super::{FunctionCx, LocalRef}; use crate::common::IntPredicate; -use crate::glue; +use crate::size_of_val; use crate::traits::*; use rustc_middle::mir; @@ -179,7 +179,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let unaligned_offset = bx.cx().const_usize(offset.bytes()); // Get the alignment of the field - let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); + let (_, mut unsized_align) = size_of_val::size_and_align_of_dst(bx, field.ty, meta); // For packed types, we need to cap alignment. if let ty::Adt(def, _) = self.layout.ty.kind() diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs new file mode 100644 index 00000000000..b8a4949d59f --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -0,0 +1,152 @@ +//! Computing the size and alignment of a value. + +use crate::common; +use crate::common::IntPredicate; +use crate::meth; +use crate::traits::*; +use rustc_hir::LangItem; +use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; +use rustc_middle::ty::{self, Ty}; +use rustc_target::abi::WrappingRange; + +pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + t: Ty<'tcx>, + info: Option, +) -> (Bx::Value, Bx::Value) { + let layout = bx.layout_of(t); + trace!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout); + if layout.is_sized() { + let size = bx.const_usize(layout.size.bytes()); + let align = bx.const_usize(layout.align.abi.bytes()); + return (size, align); + } + match t.kind() { + ty::Dynamic(..) => { + // Load size/align from vtable. + let vtable = info.unwrap(); + let size = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_SIZE) + .get_usize(bx, vtable); + let align = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ALIGN) + .get_usize(bx, vtable); + + // Size is always <= isize::MAX. + let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128; + bx.range_metadata(size, WrappingRange { start: 0, end: size_bound }); + // Alignment is always nonzero. + bx.range_metadata(align, WrappingRange { start: 1, end: !0 }); + + (size, align) + } + ty::Slice(_) | ty::Str => { + let unit = layout.field(bx, 0); + // The info in this case is the length of the str, so the size is that + // times the unit size. + ( + // All slice sizes must fit into `isize`, so this multiplication cannot (signed) wrap. + // NOTE: ideally, we want the effects of both `unchecked_smul` and `unchecked_umul` + // (resulting in `mul nsw nuw` in LLVM IR), since we know that the multiplication + // cannot signed wrap, and that both operands are non-negative. But at the time of writing, + // the `LLVM-C` binding can't do this, and it doesn't seem to enable any further optimizations. + bx.unchecked_smul(info.unwrap(), bx.const_usize(unit.size.bytes())), + bx.const_usize(unit.align.abi.bytes()), + ) + } + ty::Foreign(_) => { + // `extern` type. We cannot compute the size, so panic. + let msg_str = with_no_visible_paths!({ + with_no_trimmed_paths!({ + format!("attempted to compute the size or alignment of extern type `{t}`") + }) + }); + let msg = bx.const_str(&msg_str); + + // Obtain the panic entry point. + let (fn_abi, llfn) = common::build_langcall(bx, None, LangItem::PanicNounwind); + + // Generate the call. + // Cannot use `do_call` since we don't have a MIR terminator so we can't create a `TerminationCodegenHelper`. + // (But we are in good company, this code is duplicated plenty of times.) + let fn_ty = bx.fn_decl_backend_type(fn_abi); + + bx.call(fn_ty, /* fn_attrs */ None, Some(fn_abi), llfn, &[msg.0, msg.1], None); + + // This function does not return so we can now return whatever we want. + let size = bx.const_usize(layout.size.bytes()); + let align = bx.const_usize(layout.align.abi.bytes()); + (size, align) + } + ty::Adt(..) | ty::Tuple(..) => { + // First get the size of all statically known fields. + // Don't use size_of because it also rounds up to alignment, which we + // want to avoid, as the unsized field's alignment could be smaller. + assert!(!t.is_simd()); + debug!("DST {} layout: {:?}", t, layout); + + let i = layout.fields.count() - 1; + let sized_size = layout.fields.offset(i).bytes(); + let sized_align = layout.align.abi.bytes(); + debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align); + let sized_size = bx.const_usize(sized_size); + let sized_align = bx.const_usize(sized_align); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let field_ty = layout.field(bx, i).ty; + let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + + // Return the sum of sizes and max of aligns. + let size = bx.add(sized_size, unsized_size); + + // Packed types ignore the alignment of their fields. + if let ty::Adt(def, _) = t.kind() { + if def.repr().packed() { + unsized_align = sized_align; + } + } + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). + let align = match ( + bx.const_to_opt_u128(sized_align, false), + bx.const_to_opt_u128(unsized_align, false), + ) { + (Some(sized_align), Some(unsized_align)) => { + // If both alignments are constant, (the sized_align should always be), then + // pick the correct alignment statically. + bx.const_usize(std::cmp::max(sized_align, unsized_align) as u64) + } + _ => { + let cmp = bx.icmp(IntPredicate::IntUGT, sized_align, unsized_align); + bx.select(cmp, sized_align, unsized_align) + } + }; + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + let one = bx.const_usize(1); + let addend = bx.sub(align, one); + let add = bx.add(size, addend); + let neg = bx.neg(align); + let size = bx.and(add, neg); + + (size, align) + } + _ => bug!("size_and_align_of_dst: {t} not supported"), + } +} -- cgit 1.4.1-3-g733a5 From 9ef1e351665d9023483f4c281903848421319007 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 2 Dec 2023 12:43:56 +0100 Subject: reject projecting to fields whose offset we cannot compute --- compiler/rustc_codegen_ssa/src/mir/place.rs | 19 +++---- .../rustc_const_eval/src/interpret/projection.rs | 11 ++-- .../issue-91827-extern-types-field-offset.rs | 32 ++++++++++++ .../issue-91827-extern-types-field-offset.stderr | 9 ++++ .../consts/const-eval/issue-91827-extern-types.rs | 59 ---------------------- tests/ui/extern/extern-types-field-offset.rs | 26 ++++++++++ .../ui/extern/extern-types-field-offset.run.stderr | 4 ++ 7 files changed, 85 insertions(+), 75 deletions(-) create mode 100644 tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs create mode 100644 tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr delete mode 100644 tests/ui/consts/const-eval/issue-91827-extern-types.rs create mode 100644 tests/ui/extern/extern-types-field-offset.rs create mode 100644 tests/ui/extern/extern-types-field-offset.run.stderr (limited to 'compiler/rustc_codegen_ssa/src') diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index a3fb3b9d783..c0bb3ac5661 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -99,6 +99,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let offset = self.layout.fields.offset(ix); let effective_field_align = self.align.restrict_for_offset(offset); + // `simple` is called when we don't need to adjust the offset to + // the dynamic alignment of the field. let mut simple = || { let llval = match self.layout.abi { _ if offset.bytes() == 0 => { @@ -141,28 +143,21 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { }; // Simple cases, which don't need DST adjustment: - // * no metadata available - just log the case - // * known alignment - sized types, `[T]`, `str` or a foreign type + // * known alignment - sized types, `[T]`, `str` + // * offset 0 -- rounding up to alignment cannot change the offset // Note that looking at `field.align` is incorrect since that is not necessarily equal // to the dynamic alignment of the type. match field.ty.kind() { - _ if self.llextra.is_none() => { - debug!( - "unsized field `{}`, of `{:?}` has no metadata for adjustment", - ix, self.llval - ); - return simple(); - } _ if field.is_sized() => return simple(), - ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(), + ty::Slice(..) | ty::Str => return simple(), + _ if offset.bytes() == 0 => return simple(), _ => {} } // We need to get the pointer manually now. // We do this by casting to a `*i8`, then offsetting it by the appropriate amount. // We do this instead of, say, simply adjusting the pointer from the result of a GEP - // because the field may have an arbitrary alignment in the LLVM representation - // anyway. + // because the field may have an arbitrary alignment in the LLVM representation. // // To demonstrate: // diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 4d9e296d544..0f3b6b25c61 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -174,12 +174,15 @@ where }; (base_meta, offset.align_to(align)) } - None => { - // For unsized types with an extern type tail we perform no adjustments. - // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. - assert!(matches!(base_meta, MemPlaceMeta::None)); + None if offset == Size::ZERO => { + // If the offset is 0, then rounding it up to alignment wouldn't change anything, + // so we can do this even for types where we cannot determine the alignment. (base_meta, offset) } + None => { + // We don't know the alignment of this field, so we cannot adjust. + throw_unsup_format!("`extern type` does not have a known offset") + } } } else { // base_meta could be present; we might be accessing a sized field of an unsized diff --git a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs new file mode 100644 index 00000000000..7384e71db3a --- /dev/null +++ b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs @@ -0,0 +1,32 @@ +// Test that we can handle unsized types with an extern type tail part. +// Regression test for issue #91827. + +#![feature(extern_types)] + +use std::ptr::addr_of; + +extern "C" { + type Opaque; +} + +struct Newtype(Opaque); + +struct S { + i: i32, + a: Opaque, +} + +const NEWTYPE: () = unsafe { + // Projecting to the newtype works, because it is always at offset 0. + let x: &Newtype = unsafe { &*(1usize as *const Newtype) }; + let field = &x.0; +}; + +const OFFSET: () = unsafe { + // This needs to compute the field offset, but we don't know the type's alignment, so this fail. + let x: &S = unsafe { &*(1usize as *const S) }; + let field = &x.a; //~ERROR: evaluation of constant value failed + //~| does not have a known offset +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr new file mode 100644 index 00000000000..645ad8121bc --- /dev/null +++ b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/issue-91827-extern-types-field-offset.rs:28:17 + | +LL | let field = &x.a; + | ^^^^ `extern type` does not have a known offset + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/issue-91827-extern-types.rs b/tests/ui/consts/const-eval/issue-91827-extern-types.rs deleted file mode 100644 index c9aaa6e5587..00000000000 --- a/tests/ui/consts/const-eval/issue-91827-extern-types.rs +++ /dev/null @@ -1,59 +0,0 @@ -// run-pass -// -// Test that we can handle unsized types with an extern type tail part. -// Regression test for issue #91827. - -#![feature(extern_types)] - -use std::ptr::addr_of; - -extern "C" { - type Opaque; -} - -unsafe impl Sync for Opaque {} - -#[repr(C)] -pub struct List { - len: usize, - data: [T; 0], - tail: Opaque, -} - -#[repr(C)] -pub struct ListImpl { - len: usize, - data: [T; N], -} - -impl List { - const fn as_slice(&self) -> &[T] { - unsafe { - let ptr = addr_of!(self.tail) as *const T; - std::slice::from_raw_parts(ptr, self.len) - } - } -} - -impl ListImpl { - const fn as_list(&self) -> &List { - unsafe { std::mem::transmute(self) } - } -} - -pub static A: ListImpl = ListImpl { - len: 3, - data: [5, 6, 7], -}; -pub static A_REF: &'static List = A.as_list(); -pub static A_TAIL_OFFSET: isize = tail_offset(A.as_list()); - -const fn tail_offset(list: &List) -> isize { - unsafe { (addr_of!(list.tail) as *const u8).offset_from(list as *const List as *const u8) } -} - -fn main() { - assert_eq!(A_REF.as_slice(), &[5, 6, 7]); - // Check that interpreter and code generation agree about the position of the tail field. - assert_eq!(A_TAIL_OFFSET, tail_offset(A_REF)); -} diff --git a/tests/ui/extern/extern-types-field-offset.rs b/tests/ui/extern/extern-types-field-offset.rs new file mode 100644 index 00000000000..f2682293dcf --- /dev/null +++ b/tests/ui/extern/extern-types-field-offset.rs @@ -0,0 +1,26 @@ +// run-fail +// check-run-results +// normalize-stderr-test "panicking\.rs:\d+:\d+:" -> "panicking.rs:" +#![feature(extern_types)] + +extern "C" { + type Opaque; +} + +struct Newtype(Opaque); + +struct S { + i: i32, + a: Opaque, +} + +fn main() { + // Projecting to the newtype works, because it is always at offset 0. + let x: &Newtype = unsafe { &*(1usize as *const Newtype) }; + let field = &x.0; + + // This needs to compute the field offset, but we don't know the type's alignment, + // so this panics. + let x: &S = unsafe { &*(1usize as *const S) }; + let field = &x.a; +} diff --git a/tests/ui/extern/extern-types-field-offset.run.stderr b/tests/ui/extern/extern-types-field-offset.run.stderr new file mode 100644 index 00000000000..d6ebf3e391d --- /dev/null +++ b/tests/ui/extern/extern-types-field-offset.run.stderr @@ -0,0 +1,4 @@ +thread 'main' panicked at library/core/src/panicking.rs: +attempted to compute the size or alignment of extern type `Opaque` +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +thread caused non-unwinding panic. aborting. -- cgit 1.4.1-3-g733a5