Skip to content

Commit a595f32

Browse files
committed
Auto merge of rust-lang#126150 - RalfJung:offset_of_slice, r=compiler-errors
offset_of: allow (unstably) taking the offset of slice tail fields Fields of type `[T]` have a statically known offset, so there is no reason to forbid them in `offset_of!`. This PR adds the `offset_of_slice` feature to allow them. I created a tracking issue: rust-lang#126151.
2 parents 6c4755d + eb584a2 commit a595f32

File tree

17 files changed

+215
-46
lines changed

17 files changed

+215
-46
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -832,9 +832,10 @@ fn codegen_stmt<'tcx>(
832832
let val = match null_op {
833833
NullOp::SizeOf => layout.size.bytes(),
834834
NullOp::AlignOf => layout.align.abi.bytes(),
835-
NullOp::OffsetOf(fields) => {
836-
layout.offset_of_subfield(fx, fields.iter()).bytes()
837-
}
835+
NullOp::OffsetOf(fields) => fx
836+
.tcx
837+
.offset_of_subfield(ParamEnv::reveal_all(), layout, fields.iter())
838+
.bytes(),
838839
NullOp::UbChecks => {
839840
let val = fx.tcx.sess.ub_checks();
840841
let val = CValue::by_val(

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
680680
bx.cx().const_usize(val)
681681
}
682682
mir::NullOp::OffsetOf(fields) => {
683-
let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes();
683+
let val = bx
684+
.tcx()
685+
.offset_of_subfield(bx.param_env(), layout, fields.iter())
686+
.bytes();
684687
bx.cx().const_usize(val)
685688
}
686689
mir::NullOp::UbChecks => {

compiler/rustc_const_eval/src/interpret/step.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
253253
Scalar::from_target_usize(val, self)
254254
}
255255
mir::NullOp::OffsetOf(fields) => {
256-
let val = layout.offset_of_subfield(self, fields.iter()).bytes();
256+
let val = self
257+
.tcx
258+
.offset_of_subfield(self.param_env, layout, fields.iter())
259+
.bytes();
257260
Scalar::from_target_usize(val, self)
258261
}
259262
mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()),

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ declare_features! (
559559
(unstable, offset_of_enum, "1.75.0", Some(120141)),
560560
/// Allows using multiple nested field accesses in offset_of!
561561
(unstable, offset_of_nested, "1.77.0", Some(120140)),
562+
/// Allows using fields with slice type in offset_of!
563+
(unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)),
562564
/// Allows using `#[optimize(X)]`.
563565
(unstable, optimize_attribute, "1.34.0", Some(54882)),
564566
/// Allows postfix match `expr.match { ... }`

compiler/rustc_hir_typeck/src/expr.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -3363,7 +3363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33633363

33643364
let field_ty = self.field_ty(expr.span, field, args);
33653365

3366-
// FIXME: DSTs with static alignment should be allowed
3366+
// Enums are anyway always sized. But just to safeguard against future
3367+
// language extensions, let's double-check.
33673368
self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc);
33683369

33693370
if field.vis.is_accessible_from(sub_def_scope, self.tcx) {
@@ -3391,8 +3392,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33913392
{
33923393
let field_ty = self.field_ty(expr.span, field, args);
33933394

3394-
// FIXME: DSTs with static alignment should be allowed
3395-
self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc);
3395+
if self.tcx.features().offset_of_slice {
3396+
self.require_type_has_static_alignment(
3397+
field_ty,
3398+
expr.span,
3399+
ObligationCauseCode::Misc,
3400+
);
3401+
} else {
3402+
self.require_type_is_sized(
3403+
field_ty,
3404+
expr.span,
3405+
ObligationCauseCode::Misc,
3406+
);
3407+
}
33963408

33973409
if field.vis.is_accessible_from(def_scope, self.tcx) {
33983410
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
@@ -3412,10 +3424,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34123424
if let Ok(index) = field.as_str().parse::<usize>()
34133425
&& field.name == sym::integer(index)
34143426
{
3415-
for ty in tys.iter().take(index + 1) {
3416-
self.require_type_is_sized(ty, expr.span, ObligationCauseCode::Misc);
3417-
}
34183427
if let Some(&field_ty) = tys.get(index) {
3428+
if self.tcx.features().offset_of_slice {
3429+
self.require_type_has_static_alignment(
3430+
field_ty,
3431+
expr.span,
3432+
ObligationCauseCode::Misc,
3433+
);
3434+
} else {
3435+
self.require_type_is_sized(
3436+
field_ty,
3437+
expr.span,
3438+
ObligationCauseCode::Misc,
3439+
);
3440+
}
3441+
34193442
field_indices.push((FIRST_VARIANT, index.into()));
34203443
current_container = field_ty;
34213444

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+20
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
386386
}
387387
}
388388

389+
pub fn require_type_has_static_alignment(
390+
&self,
391+
ty: Ty<'tcx>,
392+
span: Span,
393+
code: traits::ObligationCauseCode<'tcx>,
394+
) {
395+
if !ty.references_error() {
396+
let tail =
397+
self.tcx.struct_tail_with_normalize(ty, |ty| self.normalize(span, ty), || {});
398+
// Sized types have static alignment, and so do slices.
399+
if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) {
400+
// Nothing else is required here.
401+
} else {
402+
// We can't be sure, let's required full `Sized`.
403+
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
404+
self.require_type_meets(ty, span, code, lang_item);
405+
}
406+
}
407+
}
408+
389409
pub fn register_bound(
390410
&self,
391411
ty: Ty<'tcx>,

compiler/rustc_middle/src/ty/layout.rs

+34
Original file line numberDiff line numberDiff line change
@@ -1351,3 +1351,37 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
13511351
}
13521352

13531353
impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}
1354+
1355+
impl<'tcx> TyCtxt<'tcx> {
1356+
pub fn offset_of_subfield<I>(
1357+
self,
1358+
param_env: ty::ParamEnv<'tcx>,
1359+
mut layout: TyAndLayout<'tcx>,
1360+
indices: I,
1361+
) -> Size
1362+
where
1363+
I: Iterator<Item = (VariantIdx, FieldIdx)>,
1364+
{
1365+
let cx = LayoutCx { tcx: self, param_env };
1366+
let mut offset = Size::ZERO;
1367+
1368+
for (variant, field) in indices {
1369+
layout = layout.for_variant(&cx, variant);
1370+
let index = field.index();
1371+
offset += layout.fields.offset(index);
1372+
layout = layout.field(&cx, index);
1373+
if !layout.is_sized() {
1374+
// If it is not sized, then the tail must still have at least a known static alignment.
1375+
let tail = self.struct_tail_erasing_lifetimes(layout.ty, param_env);
1376+
if !matches!(tail.kind(), ty::Slice(..)) {
1377+
bug!(
1378+
"offset of not-statically-aligned field (type {:?}) cannot be computed statically",
1379+
layout.ty
1380+
);
1381+
}
1382+
}
1383+
}
1384+
1385+
offset
1386+
}
1387+
}

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_middle::bug;
1010
use rustc_middle::mir::interpret::{InterpResult, Scalar};
1111
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
1212
use rustc_middle::mir::*;
13-
use rustc_middle::ty::layout::LayoutOf;
13+
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
1414
use rustc_middle::ty::{self, Ty, TyCtxt};
1515
use rustc_mir_dataflow::value_analysis::{
1616
Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
@@ -285,9 +285,11 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
285285
let val = match null_op {
286286
NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
287287
NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
288-
NullOp::OffsetOf(fields) => {
289-
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
290-
}
288+
NullOp::OffsetOf(fields) => self
289+
.ecx
290+
.tcx
291+
.offset_of_subfield(self.ecx.param_env(), layout, fields.iter())
292+
.bytes(),
291293
_ => return ValueOrPlace::Value(FlatSet::Top),
292294
};
293295
FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))

compiler/rustc_mir_transform/src/gvn.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ use rustc_middle::bug;
9595
use rustc_middle::mir::interpret::GlobalAlloc;
9696
use rustc_middle::mir::visit::*;
9797
use rustc_middle::mir::*;
98-
use rustc_middle::ty::layout::LayoutOf;
98+
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
9999
use rustc_middle::ty::{self, Ty, TyCtxt};
100100
use rustc_span::def_id::DefId;
101101
use rustc_span::DUMMY_SP;
@@ -484,9 +484,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
484484
let val = match null_op {
485485
NullOp::SizeOf => layout.size.bytes(),
486486
NullOp::AlignOf => layout.align.abi.bytes(),
487-
NullOp::OffsetOf(fields) => {
488-
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
489-
}
487+
NullOp::OffsetOf(fields) => self
488+
.ecx
489+
.tcx
490+
.offset_of_subfield(self.ecx.param_env(), layout, fields.iter())
491+
.bytes(),
490492
NullOp::UbChecks => return None,
491493
};
492494
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();

compiler/rustc_mir_transform/src/known_panics_lint.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -625,9 +625,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
625625
let val = match null_op {
626626
NullOp::SizeOf => op_layout.size.bytes(),
627627
NullOp::AlignOf => op_layout.align.abi.bytes(),
628-
NullOp::OffsetOf(fields) => {
629-
op_layout.offset_of_subfield(self, fields.iter()).bytes()
630-
}
628+
NullOp::OffsetOf(fields) => self
629+
.tcx
630+
.offset_of_subfield(self.param_env, op_layout, fields.iter())
631+
.bytes(),
631632
NullOp::UbChecks => return None,
632633
};
633634
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@ symbols! {
13041304
offset_of,
13051305
offset_of_enum,
13061306
offset_of_nested,
1307+
offset_of_slice,
13071308
ok_or_else,
13081309
omit_gdb_pretty_printer_section,
13091310
on,

compiler/rustc_target/src/abi/mod.rs

-23
Original file line numberDiff line numberDiff line change
@@ -256,29 +256,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
256256
Ty::is_transparent(self)
257257
}
258258

259-
pub fn offset_of_subfield<C, I>(self, cx: &C, indices: I) -> Size
260-
where
261-
Ty: TyAbiInterface<'a, C>,
262-
I: Iterator<Item = (VariantIdx, FieldIdx)>,
263-
{
264-
let mut layout = self;
265-
let mut offset = Size::ZERO;
266-
267-
for (variant, field) in indices {
268-
layout = layout.for_variant(cx, variant);
269-
let index = field.index();
270-
offset += layout.fields.offset(index);
271-
layout = layout.field(cx, index);
272-
assert!(
273-
layout.is_sized(),
274-
"offset of unsized field (type {:?}) cannot be computed statically",
275-
layout.ty
276-
);
277-
}
278-
279-
offset
280-
}
281-
282259
/// Finds the one field that is not a 1-ZST.
283260
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
284261
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use std::mem::offset_of;
2+
3+
struct S {
4+
a: u8,
5+
b: (u8, u8),
6+
c: [i32],
7+
}
8+
9+
struct T {
10+
x: i32,
11+
y: S,
12+
}
13+
14+
type Tup = (i32, [i32]);
15+
16+
fn main() {
17+
offset_of!(S, c); //~ ERROR size for values of type `[i32]` cannot be known at compilation time
18+
}
19+
20+
fn other() {
21+
offset_of!(T, y); //~ ERROR size for values of type `[i32]` cannot be known at compilation time
22+
}
23+
24+
fn tuple() {
25+
offset_of!(Tup, 1); //~ ERROR size for values of type `[i32]` cannot be known at compilation time
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
2+
--> $DIR/feature-gate-offset-of-slice.rs:17:5
3+
|
4+
LL | offset_of!(S, c);
5+
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
6+
|
7+
= help: the trait `Sized` is not implemented for `[i32]`
8+
= note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
9+
10+
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
11+
--> $DIR/feature-gate-offset-of-slice.rs:21:5
12+
|
13+
LL | offset_of!(T, y);
14+
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
15+
|
16+
= help: within `S`, the trait `Sized` is not implemented for `[i32]`, which is required by `S: Sized`
17+
note: required because it appears within the type `S`
18+
--> $DIR/feature-gate-offset-of-slice.rs:3:8
19+
|
20+
LL | struct S {
21+
| ^
22+
= note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
23+
24+
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
25+
--> $DIR/feature-gate-offset-of-slice.rs:25:5
26+
|
27+
LL | offset_of!(Tup, 1);
28+
| ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
29+
|
30+
= help: the trait `Sized` is not implemented for `[i32]`
31+
= note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
32+
33+
error: aborting due to 3 previous errors
34+
35+
For more information about this error, try `rustc --explain E0277`.

tests/ui/offset-of/offset-of-dst-field.rs

+4
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ fn delta() {
4949
fn generic_with_maybe_sized<T: ?Sized>() -> usize {
5050
offset_of!(Delta<T>, z) //~ ERROR the size for values of type
5151
}
52+
53+
fn illformed_tuple() {
54+
offset_of!(([u8], u8), 1); //~ ERROR the size for values of type
55+
}

tests/ui/offset-of/offset-of-dst-field.stderr

+10-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ LL - fn generic_with_maybe_sized<T: ?Sized>() -> usize {
8181
LL + fn generic_with_maybe_sized<T>() -> usize {
8282
|
8383

84-
error: aborting due to 8 previous errors
84+
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
85+
--> $DIR/offset-of-dst-field.rs:54:16
86+
|
87+
LL | offset_of!(([u8], u8), 1);
88+
| ^^^^^^^^^^ doesn't have a size known at compile-time
89+
|
90+
= help: the trait `Sized` is not implemented for `[u8]`
91+
= note: only the last element of a tuple may have a dynamically sized type
92+
93+
error: aborting due to 9 previous errors
8594

8695
For more information about this error, try `rustc --explain E0277`.

tests/ui/offset-of/offset-of-slice.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@run-pass
2+
#![feature(offset_of_slice, offset_of_nested)]
3+
4+
use std::mem::offset_of;
5+
6+
#[repr(C)]
7+
struct S {
8+
a: u8,
9+
b: (u8, u8),
10+
c: [i32],
11+
}
12+
13+
#[repr(C)]
14+
struct T {
15+
x: i8,
16+
y: S,
17+
}
18+
19+
type Tup = (i16, [i32]);
20+
21+
fn main() {
22+
assert_eq!(offset_of!(S, c), 4);
23+
assert_eq!(offset_of!(T, y), 4);
24+
assert_eq!(offset_of!(T, y.c), 8);
25+
assert_eq!(offset_of!(Tup, 1), 4);
26+
}

0 commit comments

Comments
 (0)