diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index a3fb3b9d783ef..c0bb3ac5661af 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 4d9e296d5441b..0f3b6b25c6157 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 0000000000000..7384e71db3a7a --- /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 0000000000000..645ad8121bcd4 --- /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 c9aaa6e558747..0000000000000 --- 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 0000000000000..f2682293dcf0c --- /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 0000000000000..d6ebf3e391dc4 --- /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.