about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-11-01 14:17:56 +0000
committerbors <bors@rust-lang.org>2023-11-01 14:17:56 +0000
commit146dafa26277c873172f555017b970ef006d302a (patch)
tree876ed8c65789f5eaeb01332ce8f2a6d034a471df
parent11cd1f00268061dff447bc0df3d679b35d460875 (diff)
parente742f809f68d571a28b7c9153366194e47714b65 (diff)
downloadrust-146dafa26277c873172f555017b970ef006d302a.tar.gz
rust-146dafa26277c873172f555017b970ef006d302a.zip
Auto merge of #114208 - GKFX:offset_of_enum, r=wesleywiser
Support enum variants in offset_of!

This MR implements support for navigating through enum variants in `offset_of!`, placing the enum variant name in the second argument to `offset_of!`. The RFC placed it in the first argument, but I think it interacts better with nested field access in the second, as you can then write things like

```rust
offset_of!(Type, field.Variant.field)
```

Alternatively, a syntactic distinction could be made between variants and fields (e.g. `field::Variant.field`) but I'm not convinced this would be helpful.

[RFC 3308 # Enum Support](https://rust-lang.github.io/rfcs/3308-offset_of.html#enum-support-offset_ofsomeenumstructvariant-field_on_variant)
Tracking Issue #106655.
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs23
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0795.md28
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs79
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs2
-rw-r--r--compiler/rustc_middle/src/thir.rs2
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs14
-rw-r--r--compiler/rustc_middle/src/ty/context.rs11
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs6
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs6
-rw-r--r--compiler/rustc_passes/src/dead.rs6
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs7
-rw-r--r--compiler/rustc_target/src/abi/mod.rs7
-rw-r--r--compiler/stable_mir/src/mir/body.rs2
-rw-r--r--library/core/src/mem/mod.rs11
-rw-r--r--tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff56
-rw-r--r--tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff56
-rw-r--r--tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff50
-rw-r--r--tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff50
-rw-r--r--tests/mir-opt/const_prop/offset_of.rs17
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff8
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff8
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff8
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff8
-rw-r--r--tests/ui/offset-of/offset-of-enum.rs7
-rw-r--r--tests/ui/offset-of/offset-of-enum.stderr40
-rw-r--r--tests/ui/offset-of/offset-of-private.rs11
-rw-r--r--tests/ui/offset-of/offset-of-private.stderr22
33 files changed, 477 insertions, 89 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 80e7c5bd9ed..91b1547cb6e 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -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(
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index f591afaaaf4..b0f757898e3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -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);
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 8c34d05042b..b6993d939c5 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -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)?;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index b70a342aa15..4d71f763667 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -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;
@@ -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 {
                                 fail_out_of_bounds(self, location, field, current_ty);
                                 return;
                             };
@@ -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;
                         }
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 89c44c6ec8a..6680e8875c3 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -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
diff --git a/compiler/rustc_error_codes/src/error_codes/E0795.md b/compiler/rustc_error_codes/src/error_codes/E0795.md
new file mode 100644
index 00000000000..8b4b2dc87fd
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0795.md
@@ -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.
+
+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.
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index b1d6e4f0523..bdac886bf9e 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -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;
@@ -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);
@@ -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;
@@ -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;
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index b89c7bc512e..b6543affc6d 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -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)]
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 9457b62f22c..3086082fe8d 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -492,7 +492,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),
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index d52a717b6b0..8b67e39667b 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -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;
@@ -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>>,
@@ -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]
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 0b3b6700cec..68812bba42f 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -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> {
@@ -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(),
         }
     }
 
@@ -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> {
@@ -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>,
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 58ad1eb900f..e9240d1b268 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -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;
@@ -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> {
@@ -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 }
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 6c3564a20f6..dfd39b512e2 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -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 }
             }
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index fd067cb234b..81d2bba989a 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -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))
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index de0dc25808b..dce298e92e1 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -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)?;
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 87d4850b475..2e8c58b0241 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -257,10 +257,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
 
         let mut current_ty = container;
 
-        for &index in indices {
+        for &(variant, field) in indices {
             match current_ty.kind() {
                 ty::Adt(def, subst) => {
-                    let field = &def.non_enum_variant().fields[index];
+                    let field = &def.variant(variant).fields[field];
 
                     self.insert_def_id(field.did);
                     let field_ty = field.ty(self.tcx, subst);
@@ -271,7 +271,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 // but we may need to mark subfields
                 ty::Tuple(tys) => {
                     current_ty =
-                        self.tcx.normalize_erasing_regions(param_env, tys[index.as_usize()]);
+                        self.tcx.normalize_erasing_regions(param_env, tys[field.as_usize()]);
                 }
                 _ => span_bug!(expr.span, "named field access on non-ADT"),
             }
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index b619ce6e35f..25bd82bf1ef 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -649,6 +649,13 @@ impl<'tcx> Stable<'tcx> for FieldIdx {
     }
 }
 
+impl<'tcx> Stable<'tcx> for (rustc_target::abi::VariantIdx, FieldIdx) {
+    type T = (usize, usize);
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        (self.0.as_usize(), self.1.as_usize())
+    }
+}
+
 impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
     type T = stable_mir::mir::Operand;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index f6f8b53d130..b00567e87c6 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -250,14 +250,17 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         Ty::is_transparent(self)
     }
 
-    pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
+    pub fn offset_of_subfield<C, I>(self, cx: &C, indices: I) -> Size
     where
         Ty: TyAbiInterface<'a, C>,
+        I: Iterator<Item = (VariantIdx, FieldIdx)>,
     {
         let mut layout = self;
         let mut offset = Size::ZERO;
 
-        for index in indices {
+        for (variant, field) in indices {
+            layout = layout.for_variant(cx, variant);
+            let index = field.index();
             offset += layout.fields.offset(index);
             layout = layout.field(cx, index);
             assert!(
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 804f5e3d9d0..4818bceadef 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -516,7 +516,7 @@ pub enum NullOp {
     /// Returns the minimum alignment of a type.
     AlignOf,
     /// Returns the offset of a field.
-    OffsetOf(Vec<FieldIdx>),
+    OffsetOf(Vec<(VariantIdx, FieldIdx)>),
 }
 
 impl Operand {
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index df79c3a338a..8792134cdc8 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -1292,11 +1292,15 @@ impl<T> SizedTypeProperties for T {}
 
 /// Expands to the offset in bytes of a field from the beginning of the given type.
 ///
-/// Only structs, unions and tuples are supported.
+/// Structs, enums, unions and tuples are supported.
 ///
 /// Nested field accesses may be used, but not array indexes like in `C`'s `offsetof`.
 ///
-/// Note that the output of this macro is not stable, except for `#[repr(C)]` types.
+/// Enum variants may be traversed as if they were fields. Variants themselves do
+/// not have an offset.
+///
+/// Note that type layout is, in general, [subject to change and
+/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html).
 ///
 /// # Examples
 ///
@@ -1324,6 +1328,9 @@ impl<T> SizedTypeProperties for T {}
 /// struct NestedB(u8);
 ///
 /// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
+///
+/// # #[cfg(not(bootstrap))]
+/// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);
 /// ```
 #[unstable(feature = "offset_of", issue = "106655")]
 #[allow_internal_unstable(builtin_syntax, hint_must_use)]
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
index c73d217aeec..711db3d21dd 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug x => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug z1 => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug eA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug eA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug eC => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
--         _2 = OffsetOf(Alpha, [0]);
+-         _2 = OffsetOf(Alpha, [(0, 0)]);
 -         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
 +         _2 = const 4_usize;
 +         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind unreachable];
@@ -37,7 +52,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
--         _4 = OffsetOf(Alpha, [1]);
+-         _4 = OffsetOf(Alpha, [(0, 1)]);
 -         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
 +         _4 = const 0_usize;
 +         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind unreachable];
@@ -47,7 +62,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Alpha, [2, 0]);
+-         _6 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
 +         _6 = const 2_usize;
 +         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind unreachable];
@@ -57,7 +72,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Alpha, [2, 1]);
+-         _8 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
 +         _8 = const 3_usize;
 +         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind unreachable];
@@ -65,7 +80,40 @@
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+-         _10 = OffsetOf(Epsilon, [(0, 0)]);
+-         _9 = must_use::<usize>(move _10) -> [return: bb5, unwind unreachable];
++         _10 = const 1_usize;
++         _9 = must_use::<usize>(const 1_usize) -> [return: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+-         _12 = OffsetOf(Epsilon, [(0, 1)]);
+-         _11 = must_use::<usize>(move _12) -> [return: bb6, unwind unreachable];
++         _12 = const 2_usize;
++         _11 = must_use::<usize>(const 2_usize) -> [return: bb6, unwind unreachable];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+-         _14 = OffsetOf(Epsilon, [(2, 0)]);
+-         _13 = must_use::<usize>(move _14) -> [return: bb7, unwind unreachable];
++         _14 = const 4_usize;
++         _13 = must_use::<usize>(const 4_usize) -> [return: bb7, unwind unreachable];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
index 913ffca4ae9..49458145415 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug x => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug z1 => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug eA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug eA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug eC => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
--         _2 = OffsetOf(Alpha, [0]);
+-         _2 = OffsetOf(Alpha, [(0, 0)]);
 -         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
 +         _2 = const 4_usize;
 +         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind continue];
@@ -37,7 +52,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
--         _4 = OffsetOf(Alpha, [1]);
+-         _4 = OffsetOf(Alpha, [(0, 1)]);
 -         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
 +         _4 = const 0_usize;
 +         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind continue];
@@ -47,7 +62,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Alpha, [2, 0]);
+-         _6 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
 +         _6 = const 2_usize;
 +         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind continue];
@@ -57,7 +72,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Alpha, [2, 1]);
+-         _8 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
 +         _8 = const 3_usize;
 +         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind continue];
@@ -65,7 +80,40 @@
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+-         _10 = OffsetOf(Epsilon, [(0, 0)]);
+-         _9 = must_use::<usize>(move _10) -> [return: bb5, unwind continue];
++         _10 = const 1_usize;
++         _9 = must_use::<usize>(const 1_usize) -> [return: bb5, unwind continue];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+-         _12 = OffsetOf(Epsilon, [(0, 1)]);
+-         _11 = must_use::<usize>(move _12) -> [return: bb6, unwind continue];
++         _12 = const 2_usize;
++         _11 = must_use::<usize>(const 2_usize) -> [return: bb6, unwind continue];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+-         _14 = OffsetOf(Epsilon, [(2, 0)]);
+-         _13 = must_use::<usize>(move _14) -> [return: bb7, unwind continue];
++         _14 = const 4_usize;
++         _13 = must_use::<usize>(const 4_usize) -> [return: bb7, unwind continue];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff
index 7519331f6d7..768970a7250 100644
--- a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug gx => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug dy => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug zA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug zA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug zB => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
-          _2 = OffsetOf(Gamma<T>, [0]);
+          _2 = OffsetOf(Gamma<T>, [(0, 0)]);
           _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
       }
   
@@ -35,7 +50,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
-          _4 = OffsetOf(Gamma<T>, [1]);
+          _4 = OffsetOf(Gamma<T>, [(0, 1)]);
           _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
       }
   
@@ -43,7 +58,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = OffsetOf(Delta<T>, [1]);
+          _6 = OffsetOf(Delta<T>, [(0, 1)]);
           _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
       }
   
@@ -51,13 +66,40 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
-          _8 = OffsetOf(Delta<T>, [2]);
+          _8 = OffsetOf(Delta<T>, [(0, 2)]);
           _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = OffsetOf(Zeta<T>, [(0, 0)]);
+          _9 = must_use::<usize>(move _10) -> [return: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+          _12 = OffsetOf(Zeta<T>, [(0, 1)]);
+          _11 = must_use::<usize>(move _12) -> [return: bb6, unwind unreachable];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = OffsetOf(Zeta<T>, [(1, 0)]);
+          _13 = must_use::<usize>(move _14) -> [return: bb7, unwind unreachable];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff
index fd5206e460c..04ccd2b36e0 100644
--- a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug gx => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug dy => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug zA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug zA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug zB => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
-          _2 = OffsetOf(Gamma<T>, [0]);
+          _2 = OffsetOf(Gamma<T>, [(0, 0)]);
           _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
       }
   
@@ -35,7 +50,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
-          _4 = OffsetOf(Gamma<T>, [1]);
+          _4 = OffsetOf(Gamma<T>, [(0, 1)]);
           _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
       }
   
@@ -43,7 +58,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = OffsetOf(Delta<T>, [1]);
+          _6 = OffsetOf(Delta<T>, [(0, 1)]);
           _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
       }
   
@@ -51,13 +66,40 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
-          _8 = OffsetOf(Delta<T>, [2]);
+          _8 = OffsetOf(Delta<T>, [(0, 2)]);
           _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
       }
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = OffsetOf(Zeta<T>, [(0, 0)]);
+          _9 = must_use::<usize>(move _10) -> [return: bb5, unwind continue];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+          _12 = OffsetOf(Zeta<T>, [(0, 1)]);
+          _11 = must_use::<usize>(move _12) -> [return: bb6, unwind continue];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = OffsetOf(Zeta<T>, [(1, 0)]);
+          _13 = must_use::<usize>(move _14) -> [return: bb7, unwind continue];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs
index 21911f8dbb5..8a5289d5899 100644
--- a/tests/mir-opt/const_prop/offset_of.rs
+++ b/tests/mir-opt/const_prop/offset_of.rs
@@ -28,12 +28,26 @@ struct Delta<T> {
     y: u16,
 }
 
+enum Epsilon {
+    A(u8, u16),
+    B,
+    C { c: u32 }
+}
+
+enum Zeta<T> {
+    A(T, bool),
+    B(char),
+}
+
 // EMIT_MIR offset_of.concrete.ConstProp.diff
 fn concrete() {
     let x = offset_of!(Alpha, x);
     let y = offset_of!(Alpha, y);
     let z0 = offset_of!(Alpha, z.0);
     let z1 = offset_of!(Alpha, z.1);
+    let eA0 = offset_of!(Epsilon, A.0);
+    let eA1 = offset_of!(Epsilon, A.1);
+    let eC = offset_of!(Epsilon, C.c);
 }
 
 // EMIT_MIR offset_of.generic.ConstProp.diff
@@ -42,6 +56,9 @@ fn generic<T>() {
     let gy = offset_of!(Gamma<T>, y);
     let dx = offset_of!(Delta<T>, x);
     let dy = offset_of!(Delta<T>, y);
+    let zA0 = offset_of!(Zeta<T>, A.0);
+    let zA1 = offset_of!(Zeta<T>, A.1);
+    let zB = offset_of!(Zeta<T>, B.0);
 }
 
 fn main() {
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
index c61414b6541..f8f89175033 100644
--- a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
@@ -27,7 +27,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
--         _2 = OffsetOf(Alpha, [0]);
+-         _2 = OffsetOf(Alpha, [(0, 0)]);
 -         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
 +         _2 = const 4_usize;
 +         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind unreachable];
@@ -37,7 +37,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
--         _4 = OffsetOf(Alpha, [1]);
+-         _4 = OffsetOf(Alpha, [(0, 1)]);
 -         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
 +         _4 = const 0_usize;
 +         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind unreachable];
@@ -47,7 +47,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Alpha, [2, 0]);
+-         _6 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
 +         _6 = const 2_usize;
 +         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind unreachable];
@@ -57,7 +57,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Alpha, [2, 1]);
+-         _8 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
 +         _8 = const 3_usize;
 +         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind unreachable];
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
index 0c3939a3456..d4f8cb66704 100644
--- a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
@@ -27,7 +27,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
--         _2 = OffsetOf(Alpha, [0]);
+-         _2 = OffsetOf(Alpha, [(0, 0)]);
 -         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
 +         _2 = const 4_usize;
 +         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind continue];
@@ -37,7 +37,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
--         _4 = OffsetOf(Alpha, [1]);
+-         _4 = OffsetOf(Alpha, [(0, 1)]);
 -         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
 +         _4 = const 0_usize;
 +         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind continue];
@@ -47,7 +47,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Alpha, [2, 0]);
+-         _6 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
 +         _6 = const 2_usize;
 +         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind continue];
@@ -57,7 +57,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Alpha, [2, 1]);
+-         _8 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
 +         _8 = const 3_usize;
 +         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind continue];
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
index d54d4687060..7f166e4fa35 100644
--- a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
@@ -27,7 +27,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
-          _2 = OffsetOf(Gamma<T>, [0]);
+          _2 = OffsetOf(Gamma<T>, [(0, 0)]);
           _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
       }
   
@@ -35,7 +35,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
-          _4 = OffsetOf(Gamma<T>, [1]);
+          _4 = OffsetOf(Gamma<T>, [(0, 1)]);
           _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
       }
   
@@ -43,7 +43,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Delta<T>, [1]);
+-         _6 = OffsetOf(Delta<T>, [(0, 1)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
 +         _6 = const 0_usize;
 +         _5 = must_use::<usize>(const 0_usize) -> [return: bb3, unwind unreachable];
@@ -53,7 +53,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Delta<T>, [2]);
+-         _8 = OffsetOf(Delta<T>, [(0, 2)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
 +         _8 = const 2_usize;
 +         _7 = must_use::<usize>(const 2_usize) -> [return: bb4, unwind unreachable];
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
index 6032a2274ef..38ad6f79801 100644
--- a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
@@ -27,7 +27,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
-          _2 = OffsetOf(Gamma<T>, [0]);
+          _2 = OffsetOf(Gamma<T>, [(0, 0)]);
           _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
       }
   
@@ -35,7 +35,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
-          _4 = OffsetOf(Gamma<T>, [1]);
+          _4 = OffsetOf(Gamma<T>, [(0, 1)]);
           _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
       }
   
@@ -43,7 +43,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Delta<T>, [1]);
+-         _6 = OffsetOf(Delta<T>, [(0, 1)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
 +         _6 = const 0_usize;
 +         _5 = must_use::<usize>(const 0_usize) -> [return: bb3, unwind continue];
@@ -53,7 +53,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Delta<T>, [2]);
+-         _8 = OffsetOf(Delta<T>, [(0, 2)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
 +         _8 = const 2_usize;
 +         _7 = must_use::<usize>(const 2_usize) -> [return: bb4, unwind continue];
diff --git a/tests/ui/offset-of/offset-of-enum.rs b/tests/ui/offset-of/offset-of-enum.rs
index d73505821ff..e8b5a08377b 100644
--- a/tests/ui/offset-of/offset-of-enum.rs
+++ b/tests/ui/offset-of/offset-of-enum.rs
@@ -9,5 +9,10 @@ enum Alpha {
 
 fn main() {
     offset_of!(Alpha::One, 0); //~ ERROR expected type, found variant `Alpha::One`
-    offset_of!(Alpha, Two.0); //~ ERROR no field `Two` on type `Alpha`
+    offset_of!(Alpha, One); //~ ERROR `One` is an enum variant; expected field at end of `offset_of`
+    offset_of!(Alpha, Two.0);
+    offset_of!(Alpha, Two.1); //~ ERROR no field named `1` on enum variant `Alpha::Two`
+    offset_of!(Alpha, Two.foo); //~ ERROR no field named `foo` on enum variant `Alpha::Two`
+    offset_of!(Alpha, NonExistent); //~ ERROR no variant named `NonExistent` found for enum `Alpha`
+    offset_of!(Beta, One); //~ ERROR cannot find type `Beta` in this scope
 }
diff --git a/tests/ui/offset-of/offset-of-enum.stderr b/tests/ui/offset-of/offset-of-enum.stderr
index 6958d199fbd..7e7ad41f5b6 100644
--- a/tests/ui/offset-of/offset-of-enum.stderr
+++ b/tests/ui/offset-of/offset-of-enum.stderr
@@ -7,13 +7,41 @@ LL |     offset_of!(Alpha::One, 0);
    |                not a type
    |                help: try using the variant's enum: `Alpha`
 
-error[E0609]: no field `Two` on type `Alpha`
+error[E0412]: cannot find type `Beta` in this scope
+  --> $DIR/offset-of-enum.rs:17:16
+   |
+LL |     offset_of!(Beta, One);
+   |                ^^^^ not found in this scope
+
+error[E0795]: `One` is an enum variant; expected field at end of `offset_of`
   --> $DIR/offset-of-enum.rs:12:23
    |
-LL |     offset_of!(Alpha, Two.0);
-   |                       ^^^
+LL |     offset_of!(Alpha, One);
+   |                       ^^^ enum variant
+
+error[E0609]: no field named `1` on enum variant `Alpha::Two`
+  --> $DIR/offset-of-enum.rs:14:23
+   |
+LL |     offset_of!(Alpha, Two.1);
+   |                       ^^^ - ...does not have this field
+   |                       |
+   |                       this enum variant...
+
+error[E0609]: no field named `foo` on enum variant `Alpha::Two`
+  --> $DIR/offset-of-enum.rs:15:23
+   |
+LL |     offset_of!(Alpha, Two.foo);
+   |                       ^^^ --- ...does not have this field
+   |                       |
+   |                       this enum variant...
+
+error[E0599]: no variant named `NonExistent` found for enum `Alpha`
+  --> $DIR/offset-of-enum.rs:16:23
+   |
+LL |     offset_of!(Alpha, NonExistent);
+   |                       ^^^^^^^^^^^ variant not found
 
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
 
-Some errors have detailed explanations: E0573, E0609.
-For more information about an error, try `rustc --explain E0573`.
+Some errors have detailed explanations: E0412, E0573, E0599, E0609, E0795.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/tests/ui/offset-of/offset-of-private.rs b/tests/ui/offset-of/offset-of-private.rs
index 6b1a16ba62b..6fa30d63fb8 100644
--- a/tests/ui/offset-of/offset-of-private.rs
+++ b/tests/ui/offset-of/offset-of-private.rs
@@ -8,13 +8,20 @@ mod m {
         pub public: u8,
         private: u8,
     }
+
     #[repr(C)]
     pub struct FooTuple(pub u8, u8);
+
     #[repr(C)]
     struct Bar {
         pub public: u8,
         private: u8,
     }
+
+    pub enum Baz {
+        Var1(Foo),
+        Var2(u64),
+    }
 }
 
 fn main() {
@@ -25,4 +32,8 @@ fn main() {
     offset_of!(m::Bar, public); //~ ERROR struct `Bar` is private
     offset_of!(m::Bar, private); //~ ERROR struct `Bar` is private
     //~| ERROR field `private` of struct `Bar` is private
+
+    offset_of!(m::Baz, Var1.0.public);
+    offset_of!(m::Baz, Var1.0.private); //~ ERROR field `private` of struct `Foo` is private
+    offset_of!(m::Baz, Var2.0);
 }
diff --git a/tests/ui/offset-of/offset-of-private.stderr b/tests/ui/offset-of/offset-of-private.stderr
index 0674b58f860..930e30e6390 100644
--- a/tests/ui/offset-of/offset-of-private.stderr
+++ b/tests/ui/offset-of/offset-of-private.stderr
@@ -1,46 +1,52 @@
 error[E0603]: struct `Bar` is private
-  --> $DIR/offset-of-private.rs:25:19
+  --> $DIR/offset-of-private.rs:32:19
    |
 LL |     offset_of!(m::Bar, public);
    |                   ^^^ private struct
    |
 note: the struct `Bar` is defined here
-  --> $DIR/offset-of-private.rs:14:5
+  --> $DIR/offset-of-private.rs:16:5
    |
 LL |     struct Bar {
    |     ^^^^^^^^^^
 
 error[E0603]: struct `Bar` is private
-  --> $DIR/offset-of-private.rs:26:19
+  --> $DIR/offset-of-private.rs:33:19
    |
 LL |     offset_of!(m::Bar, private);
    |                   ^^^ private struct
    |
 note: the struct `Bar` is defined here
-  --> $DIR/offset-of-private.rs:14:5
+  --> $DIR/offset-of-private.rs:16:5
    |
 LL |     struct Bar {
    |     ^^^^^^^^^^
 
 error[E0616]: field `private` of struct `Foo` is private
-  --> $DIR/offset-of-private.rs:22:24
+  --> $DIR/offset-of-private.rs:29:24
    |
 LL |     offset_of!(m::Foo, private);
    |                        ^^^^^^^ private field
 
 error[E0616]: field `1` of struct `FooTuple` is private
-  --> $DIR/offset-of-private.rs:24:29
+  --> $DIR/offset-of-private.rs:31:29
    |
 LL |     offset_of!(m::FooTuple, 1);
    |                             ^ private field
 
 error[E0616]: field `private` of struct `Bar` is private
-  --> $DIR/offset-of-private.rs:26:24
+  --> $DIR/offset-of-private.rs:33:24
    |
 LL |     offset_of!(m::Bar, private);
    |                        ^^^^^^^ private field
 
-error: aborting due to 5 previous errors
+error[E0616]: field `private` of struct `Foo` is private
+  --> $DIR/offset-of-private.rs:37:31
+   |
+LL |     offset_of!(m::Baz, Var1.0.private);
+   |                               ^^^^^^^ private field
+
+error: aborting due to 6 previous errors
 
 Some errors have detailed explanations: E0603, E0616.
 For more information about an error, try `rustc --explain E0603`.