Skip to content

Commit 825bb5a

Browse files
committed
Support enum variants in offset_of!
1 parent c57393e commit 825bb5a

File tree

26 files changed

+444
-74
lines changed

26 files changed

+444
-74
lines changed

compiler/rustc_abi/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,13 @@ rustc_index::newtype_index! {
11191119
pub struct FieldIdx {}
11201120
}
11211121

1122+
/// `offset_of` can traverse fields and enum variants and should keep track of which is which.
1123+
#[derive(Copy, Clone, Debug, Eq, Hash, HashStable_Generic, PartialEq, Encodable, Decodable)]
1124+
pub enum OffsetOfIdx {
1125+
Field(FieldIdx),
1126+
Variant(VariantIdx),
1127+
}
1128+
11221129
/// Describes how the fields of a type are located in memory.
11231130
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
11241131
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]

compiler/rustc_codegen_cranelift/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ fn codegen_stmt<'tcx>(
778778
NullOp::SizeOf => layout.size.bytes(),
779779
NullOp::AlignOf => layout.align.abi.bytes(),
780780
NullOp::OffsetOf(fields) => {
781-
layout.offset_of_subfield(fx, fields.iter().map(|f| f.index())).bytes()
781+
layout.offset_of_subfield(fx, fields.iter()).bytes()
782782
}
783783
};
784784
let val = CValue::by_val(

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
680680
layout.align.abi.bytes()
681681
}
682682
mir::NullOp::OffsetOf(fields) => {
683-
layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes()
683+
layout.offset_of_subfield(bx.cx(), fields.iter()).bytes()
684684
}
685685
};
686686
let val = bx.cx().const_usize(val);

compiler/rustc_const_eval/src/interpret/step.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
278278
mir::NullOp::SizeOf => layout.size.bytes(),
279279
mir::NullOp::AlignOf => layout.align.abi.bytes(),
280280
mir::NullOp::OffsetOf(fields) => {
281-
layout.offset_of_subfield(self, fields.iter().map(|f| f.index())).bytes()
281+
layout.offset_of_subfield(self, fields.iter()).bytes()
282282
}
283283
};
284284
self.write_scalar(Scalar::from_target_usize(val, self), &dest)?;

compiler/rustc_const_eval/src/transform/validate.rs

+22-11
Original file line numberDiff line numberDiff line change
@@ -965,33 +965,44 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
965965
}
966966
}
967967
}
968-
Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
968+
Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => {
969969
let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
970970
this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
971971
};
972972

973973
let mut current_ty = *container;
974+
let mut indices = indices.into_iter();
974975

975-
for field in fields.iter() {
976-
match current_ty.kind() {
977-
ty::Tuple(fields) => {
976+
use rustc_target::abi::OffsetOfIdx::*;
977+
978+
while let Some(index) = indices.next() {
979+
match (current_ty.kind(), index) {
980+
(ty::Tuple(fields), Field(field)) => {
978981
let Some(&f_ty) = fields.get(field.as_usize()) else {
979982
fail_out_of_bounds(self, location, field, current_ty);
980983
return;
981984
};
982985

983986
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
984987
}
985-
ty::Adt(adt_def, args) => {
986-
if adt_def.is_enum() {
988+
(ty::Adt(adt_def, args), Field(field)) if !adt_def.is_enum() => {
989+
let Some(field) = adt_def.non_enum_variant().fields.get(field) else {
990+
fail_out_of_bounds(self, location, field, current_ty);
991+
return;
992+
};
993+
994+
let f_ty = field.ty(self.tcx, args);
995+
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
996+
}
997+
(ty::Adt(adt_def, args), Variant(variant)) if adt_def.is_enum() => {
998+
let Some(Field(field)) = indices.next() else {
987999
self.fail(
9881000
location,
989-
format!("Cannot get field offset from enum {current_ty:?}"),
1001+
format!("enum variant must be followed by field index in offset_of; in {current_ty:?}"),
9901002
);
9911003
return;
992-
}
993-
994-
let Some(field) = adt_def.non_enum_variant().fields.get(field) else {
1004+
};
1005+
let Some(field) = adt_def.variant(variant).fields.get(field) else {
9951006
fail_out_of_bounds(self, location, field, current_ty);
9961007
return;
9971008
};
@@ -1002,7 +1013,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
10021013
_ => {
10031014
self.fail(
10041015
location,
1005-
format!("Cannot get field offset from non-adt type {current_ty:?}"),
1016+
format!("Cannot get offset {index:?} from type {current_ty:?}"),
10061017
);
10071018
return;
10081019
}

compiler/rustc_error_codes/src/error_codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ E0791: include_str!("./error_codes/E0791.md"),
514514
E0792: include_str!("./error_codes/E0792.md"),
515515
E0793: include_str!("./error_codes/E0793.md"),
516516
E0794: include_str!("./error_codes/E0794.md"),
517+
E0795: include_str!("./error_codes/E0795.md"),
517518
}
518519

519520
// Undocumented removed error codes. Note that many removed error codes are documented.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Invalid argument for the `offset_of!` macro.
2+
3+
Erroneous code example:
4+
5+
```compile_fail,E0795
6+
#![feature(offset_of)]
7+
8+
let x = std::mem::offset_of!(Option<u8>, Some);
9+
```
10+
11+
The `offset_of!` macro gives the offset of a field within a type. It can
12+
navigate through enum variants, but the final component of its second argument
13+
must be a field and not a variant.
14+
15+
The offset of the contained `u8` in the `Option<u8>` can be found by specifying
16+
the field name `0`:
17+
18+
```
19+
#![feature(offset_of)]
20+
21+
let x: usize = std::mem::offset_of!(Option<u8>, Some.0);
22+
```
23+
24+
The discriminant of an enumeration may be read with `core::mem::discriminant`,
25+
but this is not always a value physically present within the enum.
26+
27+
Further information about enum layout may be found at
28+
https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html.

compiler/rustc_hir_typeck/src/expr.rs

+56-4
Original file line numberDiff line numberDiff line change
@@ -3128,16 +3128,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31283128
fields: &[Ident],
31293129
expr: &'tcx hir::Expr<'tcx>,
31303130
) -> Ty<'tcx> {
3131+
use rustc_target::abi::OffsetOfIdx::*;
3132+
31313133
let container = self.to_ty(container).normalized;
31323134

31333135
let mut field_indices = Vec::with_capacity(fields.len());
31343136
let mut current_container = container;
3137+
let mut fields = fields.into_iter();
31353138

3136-
for &field in fields {
3139+
while let Some(&field) = fields.next() {
31373140
let container = self.structurally_resolve_type(expr.span, current_container);
31383141

31393142
match container.kind() {
3140-
ty::Adt(container_def, args) if !container_def.is_enum() => {
3143+
ty::Adt(container_def, args) if container_def.is_enum() => {
3144+
let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
3145+
let (ident, _def_scope) =
3146+
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
3147+
3148+
if let Some((index, variant)) = container_def.variants()
3149+
.iter_enumerated()
3150+
.find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident)
3151+
{
3152+
let Some(&subfield) = fields.next() else {
3153+
let mut err = type_error_struct!(
3154+
self.tcx().sess,
3155+
ident.span,
3156+
container,
3157+
E0795,
3158+
"`{ident}` is an enum variant; expected field at end of `offset_of`",
3159+
);
3160+
err.span_label(field.span, "enum variant");
3161+
err.emit();
3162+
break;
3163+
};
3164+
let (subident, sub_def_scope) =
3165+
self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block);
3166+
3167+
if let Some((subindex, field)) = variant.fields
3168+
.iter_enumerated()
3169+
.find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident)
3170+
{
3171+
let field_ty = self.field_ty(expr.span, field, args);
3172+
3173+
// FIXME: DSTs with static alignment should be allowed
3174+
self.require_type_is_sized(field_ty, expr.span, traits::MiscObligation);
3175+
3176+
if field.vis.is_accessible_from(sub_def_scope, self.tcx) {
3177+
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
3178+
} else {
3179+
self.private_field_err(ident, container_def.did()).emit();
3180+
}
3181+
3182+
// Save the index of all fields regardless of their visibility in case
3183+
// of error recovery.
3184+
field_indices.push(Variant(index));
3185+
field_indices.push(Field(subindex));
3186+
current_container = field_ty;
3187+
3188+
continue;
3189+
}
3190+
}
3191+
}
3192+
ty::Adt(container_def, args) => {
31413193
let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
31423194
let (ident, def_scope) =
31433195
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
@@ -3160,7 +3212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31603212

31613213
// Save the index of all fields regardless of their visibility in case
31623214
// of error recovery.
3163-
field_indices.push(index);
3215+
field_indices.push(Field(index));
31643216
current_container = field_ty;
31653217

31663218
continue;
@@ -3174,7 +3226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31743226
self.require_type_is_sized(ty, expr.span, traits::MiscObligation);
31753227
}
31763228
if let Some(&field_ty) = tys.get(index) {
3177-
field_indices.push(index.into());
3229+
field_indices.push(Field(index.into()));
31783230
current_container = field_ty;
31793231

31803232
continue;

compiler/rustc_middle/src/mir/syntax.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir::def_id::DefId;
1717
use rustc_hir::{self as hir};
1818
use rustc_hir::{self, GeneratorKind};
1919
use rustc_index::IndexVec;
20-
use rustc_target::abi::{FieldIdx, VariantIdx};
20+
use rustc_target::abi::{FieldIdx, OffsetOfIdx, VariantIdx};
2121

2222
use rustc_ast::Mutability;
2323
use rustc_span::def_id::LocalDefId;
@@ -1281,7 +1281,7 @@ pub enum NullOp<'tcx> {
12811281
/// Returns the minimum alignment of a type
12821282
AlignOf,
12831283
/// Returns the offset of a field
1284-
OffsetOf(&'tcx List<FieldIdx>),
1284+
OffsetOf(&'tcx List<OffsetOfIdx>),
12851285
}
12861286

12871287
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]

compiler/rustc_middle/src/thir.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_middle::ty::{self, AdtDef, FnSig, List, Ty, UpvarArgs};
2424
use rustc_middle::ty::{CanonicalUserType, CanonicalUserTypeAnnotation};
2525
use rustc_span::def_id::LocalDefId;
2626
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
27-
use rustc_target::abi::{FieldIdx, VariantIdx};
27+
use rustc_target::abi::{FieldIdx, OffsetOfIdx, VariantIdx};
2828
use rustc_target::asm::InlineAsmRegOrRegClass;
2929
use std::fmt;
3030
use std::ops::Index;
@@ -488,7 +488,7 @@ pub enum ExprKind<'tcx> {
488488
/// Field offset (`offset_of!`)
489489
OffsetOf {
490490
container: Ty<'tcx>,
491-
fields: &'tcx List<FieldIdx>,
491+
fields: &'tcx List<OffsetOfIdx>,
492492
},
493493
/// An expression taking a reference to a thread local.
494494
ThreadLocalRef(DefId),

compiler/rustc_middle/src/ty/codec.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_data_structures::fx::FxHashMap;
1919
use rustc_middle::ty::TyCtxt;
2020
use rustc_serialize::{Decodable, Encodable};
2121
use rustc_span::Span;
22-
use rustc_target::abi::FieldIdx;
22+
use rustc_target::abi::{FieldIdx, OffsetOfIdx};
2323
pub use rustc_type_ir::{TyDecoder, TyEncoder};
2424
use std::hash::Hash;
2525
use std::intrinsics;
@@ -412,6 +412,15 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<Fi
412412
}
413413
}
414414

415+
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<OffsetOfIdx> {
416+
fn decode(decoder: &mut D) -> &'tcx Self {
417+
let len = decoder.read_usize();
418+
decoder
419+
.interner()
420+
.mk_offset_of_from_iter((0..len).map::<OffsetOfIdx, _>(|_| Decodable::decode(decoder)))
421+
}
422+
}
423+
415424
impl_decodable_via_ref! {
416425
&'tcx ty::TypeckResults<'tcx>,
417426
&'tcx ty::List<Ty<'tcx>>,
@@ -424,6 +433,7 @@ impl_decodable_via_ref! {
424433
&'tcx ty::List<ty::BoundVariableKind>,
425434
&'tcx ty::List<ty::Clause<'tcx>>,
426435
&'tcx ty::List<FieldIdx>,
436+
&'tcx ty::List<OffsetOfIdx>,
427437
}
428438

429439
#[macro_export]

compiler/rustc_middle/src/ty/context.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ use rustc_session::{Limit, MetadataKind, Session};
6363
use rustc_span::def_id::{DefPathHash, StableCrateId};
6464
use rustc_span::symbol::{kw, sym, Ident, Symbol};
6565
use rustc_span::{Span, DUMMY_SP};
66-
use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
66+
use rustc_target::abi::{FieldIdx, Layout, LayoutS, OffsetOfIdx, TargetDataLayout, VariantIdx};
6767
use rustc_target::spec::abi;
6868
use rustc_type_ir::sty::TyKind::*;
6969
use rustc_type_ir::WithCachedTypeInfo;
@@ -156,6 +156,7 @@ pub struct CtxtInterners<'tcx> {
156156
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
157157
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
158158
fields: InternedSet<'tcx, List<FieldIdx>>,
159+
offset_of: InternedSet<'tcx, List<OffsetOfIdx>>,
159160
}
160161

161162
impl<'tcx> CtxtInterners<'tcx> {
@@ -181,6 +182,7 @@ impl<'tcx> CtxtInterners<'tcx> {
181182
external_constraints: Default::default(),
182183
predefined_opaques_in_body: Default::default(),
183184
fields: Default::default(),
185+
offset_of: Default::default(),
184186
}
185187
}
186188

@@ -1536,6 +1538,7 @@ slice_interners!(
15361538
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
15371539
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
15381540
fields: pub mk_fields(FieldIdx),
1541+
offset_of: pub mk_offset_of(OffsetOfIdx),
15391542
);
15401543

15411544
impl<'tcx> TyCtxt<'tcx> {
@@ -1857,6 +1860,14 @@ impl<'tcx> TyCtxt<'tcx> {
18571860
T::collect_and_apply(iter, |xs| self.mk_fields(xs))
18581861
}
18591862

1863+
pub fn mk_offset_of_from_iter<I, T>(self, iter: I) -> T::Output
1864+
where
1865+
I: Iterator<Item = T>,
1866+
T: CollectAndApply<OffsetOfIdx, &'tcx List<OffsetOfIdx>>,
1867+
{
1868+
T::collect_and_apply(iter, |xs| self.mk_offset_of(xs))
1869+
}
1870+
18601871
pub fn mk_args_trait(
18611872
self,
18621873
self_ty: Ty<'tcx>,

compiler/rustc_middle/src/ty/typeck_results.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_macros::HashStable;
2424
use rustc_middle::mir::FakeReadCause;
2525
use rustc_session::Session;
2626
use rustc_span::Span;
27-
use rustc_target::abi::FieldIdx;
27+
use rustc_target::abi::{FieldIdx, OffsetOfIdx};
2828
use std::{collections::hash_map::Entry, hash::Hash, iter};
2929

3030
use super::RvalueScopes;
@@ -209,7 +209,7 @@ pub struct TypeckResults<'tcx> {
209209
pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,
210210

211211
/// Container types and field indices of `offset_of!` expressions
212-
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<FieldIdx>)>,
212+
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<OffsetOfIdx>)>,
213213
}
214214

215215
/// Whenever a value may be live across a generator yield, the type of that value winds up in the
@@ -534,11 +534,13 @@ impl<'tcx> TypeckResults<'tcx> {
534534
&self.coercion_casts
535535
}
536536

537-
pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
537+
pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<OffsetOfIdx>)> {
538538
LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data }
539539
}
540540

541-
pub fn offset_of_data_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
541+
pub fn offset_of_data_mut(
542+
&mut self,
543+
) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<OffsetOfIdx>)> {
542544
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data }
543545
}
544546
}

compiler/rustc_mir_build/src/thir/cx/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ impl<'tcx> Cx<'tcx> {
678678
hir::ExprKind::OffsetOf(_, _) => {
679679
let data = self.typeck_results.offset_of_data();
680680
let &(container, ref indices) = data.get(expr.hir_id).unwrap();
681-
let fields = tcx.mk_fields_from_iter(indices.iter().copied());
681+
let fields = tcx.mk_offset_of_from_iter(indices.iter().copied());
682682

683683
ExprKind::OffsetOf { container, fields }
684684
}

0 commit comments

Comments
 (0)