Skip to content

Commit

Permalink
reject projecting to fields whose offset we cannot compute
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Dec 2, 2023
1 parent 6ba7c5c commit 0e67c75
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 82 deletions.
23 changes: 4 additions & 19 deletions compiler/rustc_codegen_ssa/src/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,27 +141,12 @@ 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
// * packed struct - there is no alignment padding
// * known alignment - sized types, `[T]`, `str`
// * offset 0 -- rounding up to alignment cannot change the offset
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::Adt(def, _) => {
if def.repr().packed() {
// FIXME(eddyb) generalize the adjustment when we
// start supporting packing to larger alignments.
assert_eq!(self.layout.align.abi.bytes(), 1);
return simple();
}
}
ty::Slice(..) | ty::Str => return simple(),
_ if offset.bytes() == 0 => return simple(),
_ => {}
}

Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_const_eval/src/interpret/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,15 @@ where
// happens at run-time so that's okay.
match self.size_and_align_of(&base_meta, &field_layout)? {
Some((_, align)) => (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
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
59 changes: 0 additions & 59 deletions tests/ui/consts/const-eval/issue-91827-extern-types.rs

This file was deleted.

21 changes: 21 additions & 0 deletions tests/ui/consts/const-eval/issue-91827-extern-types.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0080]: could not evaluate static initializer
--> $DIR/issue-91827-extern-types.rs:50:15
|
LL | unsafe { (addr_of!(list.tail) as *const u8).offset_from(list as *const List<T> as *const u8) }
| ^^^^^^^^^^^^^^^^^^^ `extern type` does not have known offset
|
note: inside `tail_offset::<u128>`
--> $DIR/issue-91827-extern-types.rs:50:15
|
LL | unsafe { (addr_of!(list.tail) as *const u8).offset_from(list as *const List<T> as *const u8) }
| ^^^^^^^^^^^^^^^^^^^
note: inside `A_TAIL_OFFSET`
--> $DIR/issue-91827-extern-types.rs:47:35
|
LL | pub static A_TAIL_OFFSET: isize = tail_offset(A.as_list());
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
26 changes: 26 additions & 0 deletions tests/ui/extern/extern-types-field-offset.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions tests/ui/extern/extern-types-field-offset.run.stderr
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit 0e67c75

Please sign in to comment.