Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support enum variants in offset_of! #114208

Merged
merged 4 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ fn codegen_stmt<'tcx>(
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.abi.bytes(),
NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(fx, fields.iter().map(|f| f.index())).bytes()
layout.offset_of_subfield(fx, fields.iter()).bytes()
}
};
let val = CValue::by_val(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
layout.align.abi.bytes()
}
mir::NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes()
layout.offset_of_subfield(bx.cx(), fields.iter()).bytes()
}
};
let val = bx.cx().const_usize(val);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
mir::NullOp::SizeOf => layout.size.bytes(),
mir::NullOp::AlignOf => layout.align.abi.bytes(),
mir::NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(self, fields.iter().map(|f| f.index())).bytes()
layout.offset_of_subfield(self, fields.iter()).bytes()
}
};
self.write_scalar(Scalar::from_target_usize(val, self), &dest)?;
Expand Down
23 changes: 11 additions & 12 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,16 +1056,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
}
Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => {
let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
};

let mut current_ty = *container;

for field in fields.iter() {
for (variant, field) in indices.iter() {
match current_ty.kind() {
ty::Tuple(fields) => {
if variant != FIRST_VARIANT {
self.fail(
location,
format!("tried to get variant {variant:?} of tuple"),
);
return;
}
let Some(&f_ty) = fields.get(field.as_usize()) else {
fail_out_of_bounds(self, location, field, current_ty);
return;
Expand All @@ -1074,15 +1081,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
}
ty::Adt(adt_def, args) => {
if adt_def.is_enum() {
self.fail(
location,
format!("Cannot get field offset from enum {current_ty:?}"),
);
return;
}

let Some(field) = adt_def.non_enum_variant().fields.get(field) else {
let Some(field) = adt_def.variant(variant).fields.get(field) else {
GKFX marked this conversation as resolved.
Show resolved Hide resolved
fail_out_of_bounds(self, location, field, current_ty);
return;
};
Expand All @@ -1093,7 +1092,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
_ => {
self.fail(
location,
format!("Cannot get field offset from non-adt type {current_ty:?}"),
format!("Cannot get offset ({variant:?}, {field:?}) from type {current_ty:?}"),
);
return;
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ E0791: include_str!("./error_codes/E0791.md"),
E0792: include_str!("./error_codes/E0792.md"),
E0793: include_str!("./error_codes/E0793.md"),
E0794: include_str!("./error_codes/E0794.md"),
E0795: include_str!("./error_codes/E0795.md"),
}

// Undocumented removed error codes. Note that many removed error codes are kept in the list above
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0795.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Invalid argument for the `offset_of!` macro.

Erroneous code example:

```compile_fail,E0795
#![feature(offset_of)]
let x = std::mem::offset_of!(Option<u8>, Some);
```

The `offset_of!` macro gives the offset of a field within a type. It can
navigate through enum variants, but the final component of its second argument
must be a field and not a variant.
GKFX marked this conversation as resolved.
Show resolved Hide resolved

The offset of the contained `u8` in the `Option<u8>` can be found by specifying
the field name `0`:

```
#![feature(offset_of)]
let x: usize = std::mem::offset_of!(Option<u8>, Some.0);
```

The discriminant of an enumeration may be read with `core::mem::discriminant`,
but this is not always a value physically present within the enum.

Further information about enum layout may be found at
https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html.
79 changes: 74 additions & 5 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_target::abi::FieldIdx;
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
use rustc_target::spec::abi::Abi::RustIntrinsic;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
Expand Down Expand Up @@ -3107,12 +3107,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let mut field_indices = Vec::with_capacity(fields.len());
let mut current_container = container;
let mut fields = fields.into_iter();

for &field in fields {
while let Some(&field) = fields.next() {
let container = self.structurally_resolve_type(expr.span, current_container);

match container.kind() {
ty::Adt(container_def, args) if !container_def.is_enum() => {
ty::Adt(container_def, args) if container_def.is_enum() => {
let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
let (ident, _def_scope) =
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);

let Some((index, variant)) = container_def.variants()
.iter_enumerated()
.find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) else {
let mut err = type_error_struct!(
self.tcx().sess,
ident.span,
container,
E0599,
"no variant named `{ident}` found for enum `{container}`",
);
err.span_label(field.span, "variant not found");
err.emit();
break;
};
let Some(&subfield) = fields.next() else {
let mut err = type_error_struct!(
self.tcx().sess,
ident.span,
container,
E0795,
"`{ident}` is an enum variant; expected field at end of `offset_of`",
);
err.span_label(field.span, "enum variant");
err.emit();
break;
};
let (subident, sub_def_scope) =
self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block);

let Some((subindex, field)) = variant.fields
.iter_enumerated()
.find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident) else {
let mut err = type_error_struct!(
self.tcx().sess,
ident.span,
container,
E0609,
"no field named `{subfield}` on enum variant `{container}::{ident}`",
);
err.span_label(field.span, "this enum variant...");
err.span_label(subident.span, "...does not have this field");
err.emit();
break;
};

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

// FIXME: DSTs with static alignment should be allowed
self.require_type_is_sized(field_ty, expr.span, traits::MiscObligation);

if field.vis.is_accessible_from(sub_def_scope, self.tcx) {
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
} else {
self.private_field_err(ident, container_def.did()).emit();
}

// Save the index of all fields regardless of their visibility in case
// of error recovery.
field_indices.push((index, subindex));
current_container = field_ty;

continue;
}
ty::Adt(container_def, args) => {
let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
Expand All @@ -3135,7 +3204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Save the index of all fields regardless of their visibility in case
// of error recovery.
field_indices.push(index);
field_indices.push((FIRST_VARIANT, index));
current_container = field_ty;

continue;
Expand All @@ -3149,7 +3218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.require_type_is_sized(ty, expr.span, traits::MiscObligation);
}
if let Some(&field_ty) = tys.get(index) {
field_indices.push(index.into());
field_indices.push((FIRST_VARIANT, index.into()));
current_container = field_ty;

continue;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1354,7 +1354,7 @@ pub enum NullOp<'tcx> {
/// Returns the minimum alignment of a type
AlignOf,
/// Returns the offset of a field
OffsetOf(&'tcx List<FieldIdx>),
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ pub enum ExprKind<'tcx> {
/// Field offset (`offset_of!`)
OffsetOf {
container: Ty<'tcx>,
fields: &'tcx List<FieldIdx>,
fields: &'tcx List<(VariantIdx, FieldIdx)>,
},
/// An expression taking a reference to a thread local.
ThreadLocalRef(DefId),
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_middle/src/ty/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::TyCtxt;
use rustc_serialize::{Decodable, Encodable};
use rustc_span::Span;
use rustc_target::abi::FieldIdx;
use rustc_target::abi::{FieldIdx, VariantIdx};
pub use rustc_type_ir::{TyDecoder, TyEncoder};
use std::hash::Hash;
use std::intrinsics;
Expand Down Expand Up @@ -414,6 +414,17 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<Fi
}
}

impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for ty::List<(VariantIdx, FieldIdx)>
{
fn decode(decoder: &mut D) -> &'tcx Self {
let len = decoder.read_usize();
decoder.interner().mk_offset_of_from_iter(
(0..len).map::<(VariantIdx, FieldIdx), _>(|_| Decodable::decode(decoder)),
)
}
}

impl_decodable_via_ref! {
&'tcx ty::TypeckResults<'tcx>,
&'tcx ty::List<Ty<'tcx>>,
Expand All @@ -426,6 +437,7 @@ impl_decodable_via_ref! {
&'tcx ty::List<ty::BoundVariableKind>,
&'tcx ty::List<ty::Clause<'tcx>>,
&'tcx ty::List<FieldIdx>,
&'tcx ty::List<(VariantIdx, FieldIdx)>,
}

#[macro_export]
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ pub struct CtxtInterners<'tcx> {
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
fields: InternedSet<'tcx, List<FieldIdx>>,
local_def_ids: InternedSet<'tcx, List<LocalDefId>>,
offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>,
}

impl<'tcx> CtxtInterners<'tcx> {
Expand All @@ -189,6 +190,7 @@ impl<'tcx> CtxtInterners<'tcx> {
predefined_opaques_in_body: Default::default(),
fields: Default::default(),
local_def_ids: Default::default(),
offset_of: Default::default(),
}
}

Expand Down Expand Up @@ -1587,6 +1589,7 @@ slice_interners!(
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
fields: pub mk_fields(FieldIdx),
local_def_ids: intern_local_def_ids(LocalDefId),
offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
);

impl<'tcx> TyCtxt<'tcx> {
Expand Down Expand Up @@ -1914,6 +1917,14 @@ impl<'tcx> TyCtxt<'tcx> {
T::collect_and_apply(iter, |xs| self.mk_fields(xs))
}

pub fn mk_offset_of_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: CollectAndApply<(VariantIdx, FieldIdx), &'tcx List<(VariantIdx, FieldIdx)>>,
{
T::collect_and_apply(iter, |xs| self.mk_offset_of(xs))
}

pub fn mk_args_trait(
self,
self_ty: Ty<'tcx>,
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use rustc_macros::HashStable;
use rustc_middle::mir::FakeReadCause;
use rustc_session::Session;
use rustc_span::Span;
use rustc_target::abi::FieldIdx;
use rustc_target::abi::{FieldIdx, VariantIdx};
use std::{collections::hash_map::Entry, hash::Hash, iter};

use super::RvalueScopes;
Expand Down Expand Up @@ -205,7 +205,7 @@ pub struct TypeckResults<'tcx> {
pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,

/// Container types and field indices of `offset_of!` expressions
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<FieldIdx>)>,
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>,
}

impl<'tcx> TypeckResults<'tcx> {
Expand Down Expand Up @@ -464,11 +464,15 @@ impl<'tcx> TypeckResults<'tcx> {
&self.coercion_casts
}

pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
pub fn offset_of_data(
&self,
) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data }
}

pub fn offset_of_data_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
pub fn offset_of_data_mut(
&mut self,
) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data }
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ impl<'tcx> Cx<'tcx> {
hir::ExprKind::OffsetOf(_, _) => {
let data = self.typeck_results.offset_of_data();
let &(container, ref indices) = data.get(expr.hir_id).unwrap();
let fields = tcx.mk_fields_from_iter(indices.iter().copied());
let fields = tcx.mk_offset_of_from_iter(indices.iter().copied());

ExprKind::OffsetOf { container, fields }
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir_transform/src/dataflow_const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,9 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
let val = match null_op {
NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
NullOp::OffsetOf(fields) => layout
.offset_of_subfield(&self.ecx, fields.iter().map(|f| f.index()))
.bytes(),
NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
}
_ => return ValueOrPlace::Value(FlatSet::Top),
};
FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
let val = match null_op {
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.abi.bytes(),
NullOp::OffsetOf(fields) => layout
.offset_of_subfield(&self.ecx, fields.iter().map(|f| f.index()))
.bytes(),
NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
}
};
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
let imm = ImmTy::try_from_uint(val, usize_layout)?;
Expand Down
Loading
Loading