reject projecting to fields whose offset we cannot compute
This commit is contained in:
parent
b1613ebc43
commit
9ef1e35166
@ -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:
|
||||
//
|
||||
|
@ -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
|
||||
|
@ -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() {}
|
@ -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`.
|
@ -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<T> {
|
||||
len: usize,
|
||||
data: [T; 0],
|
||||
tail: Opaque,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ListImpl<T, const N: usize> {
|
||||
len: usize,
|
||||
data: [T; N],
|
||||
}
|
||||
|
||||
impl<T> List<T> {
|
||||
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<T, const N: usize> ListImpl<T, N> {
|
||||
const fn as_list(&self) -> &List<T> {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
pub static A: ListImpl<u128, 3> = ListImpl {
|
||||
len: 3,
|
||||
data: [5, 6, 7],
|
||||
};
|
||||
pub static A_REF: &'static List<u128> = A.as_list();
|
||||
pub static A_TAIL_OFFSET: isize = tail_offset(A.as_list());
|
||||
|
||||
const fn tail_offset<T>(list: &List<T>) -> isize {
|
||||
unsafe { (addr_of!(list.tail) as *const u8).offset_from(list as *const List<T> 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));
|
||||
}
|
26
tests/ui/extern/extern-types-field-offset.rs
vendored
Normal file
26
tests/ui/extern/extern-types-field-offset.rs
vendored
Normal file
@ -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;
|
||||
}
|
4
tests/ui/extern/extern-types-field-offset.run.stderr
vendored
Normal file
4
tests/ui/extern/extern-types-field-offset.run.stderr
vendored
Normal file
@ -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.
|
Loading…
x
Reference in New Issue
Block a user