Skip to content

Commit d65e936

Browse files
committedDec 2, 2023
reject projecting to fields whose offset we cannot compute
1 parent 6ba7c5c commit d65e936

File tree

7 files changed

+84
-73
lines changed

7 files changed

+84
-73
lines changed
 

‎compiler/rustc_codegen_ssa/src/mir/place.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
9999
let offset = self.layout.fields.offset(ix);
100100
let effective_field_align = self.align.restrict_for_offset(offset);
101101

102+
// `simple` is called when we don't need to adjust the offset to
103+
// the dynamic alignment of the field.
102104
let mut simple = || {
103105
let llval = match self.layout.abi {
104106
_ if offset.bytes() == 0 => {
@@ -141,19 +143,13 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
141143
};
142144

143145
// Simple cases, which don't need DST adjustment:
144-
// * no metadata available - just log the case
145-
// * known alignment - sized types, `[T]`, `str` or a foreign type
146+
// * known alignment - sized types, `[T]`, `str`
147+
// * offset 0 -- rounding up to alignment cannot change the offset
146148
// * packed struct - there is no alignment padding
147149
match field.ty.kind() {
148-
_ if self.llextra.is_none() => {
149-
debug!(
150-
"unsized field `{}`, of `{:?}` has no metadata for adjustment",
151-
ix, self.llval
152-
);
153-
return simple();
154-
}
155150
_ if field.is_sized() => return simple(),
156-
ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
151+
ty::Slice(..) | ty::Str => return simple(),
152+
_ if offset.bytes() == 0 => return simple(),
157153
ty::Adt(def, _) => {
158154
if def.repr().packed() {
159155
// FIXME(eddyb) generalize the adjustment when we

‎compiler/rustc_const_eval/src/interpret/projection.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,15 @@ where
164164
// happens at run-time so that's okay.
165165
match self.size_and_align_of(&base_meta, &field_layout)? {
166166
Some((_, align)) => (base_meta, offset.align_to(align)),
167-
None => {
168-
// For unsized types with an extern type tail we perform no adjustments.
169-
// NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
170-
assert!(matches!(base_meta, MemPlaceMeta::None));
167+
None if offset == Size::ZERO => {
168+
// If the offset is 0, then rounding it up to alignment wouldn't change anything,
169+
// so we can do this even for types where we cannot determine the alignment.
171170
(base_meta, offset)
172171
}
172+
None => {
173+
// We don't know the alignment of this field, so we cannot adjust.
174+
throw_unsup_format!("`extern type` does not have a known offset")
175+
}
173176
}
174177
} else {
175178
// base_meta could be present; we might be accessing a sized field of an unsized
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Test that we can handle unsized types with an extern type tail part.
2+
// Regression test for issue #91827.
3+
4+
#![feature(extern_types)]
5+
6+
use std::ptr::addr_of;
7+
8+
extern "C" {
9+
type Opaque;
10+
}
11+
12+
struct Newtype(Opaque);
13+
14+
struct S {
15+
i: i32,
16+
a: Opaque,
17+
}
18+
19+
const NEWTYPE: () = unsafe {
20+
// Projecting to the newtype works, because it is always at offset 0.
21+
let x: &Newtype = unsafe { &*(1usize as *const Newtype) };
22+
let field = &x.0;
23+
};
24+
25+
const OFFSET: () = unsafe {
26+
// This needs to compute the field offset, but we don't know the type's alignment, so this fail.
27+
let x: &S = unsafe { &*(1usize as *const S) };
28+
let field = &x.a; //~ERROR: evaluation of constant value failed
29+
//~| does not have a known offset
30+
};
31+
32+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/issue-91827-extern-types-field-offset.rs:28:17
3+
|
4+
LL | let field = &x.a;
5+
| ^^^^ `extern type` does not have a known offset
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.

‎tests/ui/consts/const-eval/issue-91827-extern-types.rs

-59
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// run-fail
2+
// check-run-results
3+
// normalize-stderr-test "panicking\.rs:\d+:\d+:" -> "panicking.rs:"
4+
#![feature(extern_types)]
5+
6+
extern "C" {
7+
type Opaque;
8+
}
9+
10+
struct Newtype(Opaque);
11+
12+
struct S {
13+
i: i32,
14+
a: Opaque,
15+
}
16+
17+
fn main() {
18+
// Projecting to the newtype works, because it is always at offset 0.
19+
let x: &Newtype = unsafe { &*(1usize as *const Newtype) };
20+
let field = &x.0;
21+
22+
// This needs to compute the field offset, but we don't know the type's alignment,
23+
// so this panics.
24+
let x: &S = unsafe { &*(1usize as *const S) };
25+
let field = &x.a;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
thread 'main' panicked at library/core/src/panicking.rs:
2+
attempted to compute the size or alignment of extern type `Opaque`
3+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
4+
thread caused non-unwinding panic. aborting.

0 commit comments

Comments
 (0)