From e936416a8d3cfb501504f00d7250d5b595fed244 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Tue, 15 Aug 2023 20:10:45 +0100 Subject: [PATCH] Support enum variants in offset_of! --- compiler/rustc_abi/src/lib.rs | 33 ++++++++++ compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 2 +- .../src/transform/validate.rs | 33 ++++++---- compiler/rustc_error_codes/src/error_codes.rs | 1 + .../src/error_codes/E0795.md | 28 +++++++++ compiler/rustc_hir_typeck/src/expr.rs | 60 +++++++++++++++++-- compiler/rustc_middle/src/mir/syntax.rs | 4 +- compiler/rustc_middle/src/thir.rs | 5 +- compiler/rustc_middle/src/ty/codec.rs | 12 +++- compiler/rustc_middle/src/ty/context.rs | 13 +++- .../rustc_middle/src/ty/typeck_results.rs | 10 ++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 2 +- .../src/dataflow_const_prop.rs | 2 +- compiler/rustc_passes/src/dead.rs | 29 ++++++--- compiler/rustc_smir/src/rustc_smir/mod.rs | 12 +++- compiler/rustc_target/src/abi/mod.rs | 24 +++++--- ...set_of.concrete.ConstProp.panic-abort.diff | 56 +++++++++++++++-- ...et_of.concrete.ConstProp.panic-unwind.diff | 56 +++++++++++++++-- ...fset_of.generic.ConstProp.panic-abort.diff | 50 ++++++++++++++-- ...set_of.generic.ConstProp.panic-unwind.diff | 50 ++++++++++++++-- tests/mir-opt/const_prop/offset_of.rs | 17 ++++++ tests/ui/offset-of/offset-of-enum.rs | 3 +- tests/ui/offset-of/offset-of-enum.stderr | 8 +-- tests/ui/offset-of/offset-of-private.rs | 11 ++++ tests/ui/offset-of/offset-of-private.stderr | 22 ++++--- 27 files changed, 472 insertions(+), 75 deletions(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0795.md diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 8e7aa59ee34..0654bfd11c5 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1107,6 +1107,39 @@ impl Scalar { } // NOTE: This struct is generic over the FieldIdx for rust-analyzer usage. +rustc_index::newtype_index! { + /// The *source-order* index of a field in a variant. + /// + /// This is how most code after type checking refers to fields, rather than + /// using names (as names have hygiene complications and more complex lookup). + /// + /// Particularly for `repr(Rust)` types, this may not be the same as *layout* order. + /// (It is for `repr(C)` `struct`s, however.) + /// + /// For example, in the following types, + /// ```rust + /// # enum Never {} + /// # #[repr(u16)] + /// enum Demo1 { + /// Variant0 { a: Never, b: i32 } = 100, + /// Variant1 { c: u8, d: u64 } = 10, + /// } + /// struct Demo2 { e: u8, f: u16, g: u8 } + /// ``` + /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, + /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and + /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. + #[derive(HashStable_Generic)] + pub struct FieldIdx {} +} + +/// `offset_of` can traverse fields and enum variants and should keep track of which is which. +#[derive(Copy, Clone, Debug, Eq, Hash, HashStable_Generic, PartialEq, Encodable, Decodable)] +pub enum OffsetOfIdx { + Field(FieldIdx), + Variant(VariantIdx), +} + /// Describes how the fields of a type are located in memory. #[derive(PartialEq, Eq, Hash, Clone, Debug)] #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] 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..bdccd9b0edb 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1056,16 +1056,19 @@ 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; + let mut indices = indices.into_iter(); - for field in fields.iter() { - match current_ty.kind() { - ty::Tuple(fields) => { + use rustc_target::abi::OffsetOfIdx::*; + + while let Some(index) = indices.next() { + match (current_ty.kind(), index) { + (ty::Tuple(fields), Field(field)) => { let Some(&f_ty) = fields.get(field.as_usize()) else { fail_out_of_bounds(self, location, field, current_ty); return; @@ -1073,16 +1076,24 @@ 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() { + (ty::Adt(adt_def, args), Field(field)) if !adt_def.is_enum() => { + let Some(field) = adt_def.non_enum_variant().fields.get(field) else { + fail_out_of_bounds(self, location, field, current_ty); + return; + }; + + let f_ty = field.ty(self.tcx, args); + current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty); + } + (ty::Adt(adt_def, args), Variant(variant)) if adt_def.is_enum() => { + let Some(Field(field)) = indices.next() else { self.fail( location, - format!("Cannot get field offset from enum {current_ty:?}"), + format!("enum variant must be followed by field index in offset_of; in {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 +1104,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 {index:?} 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, 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` can be found by specifying +the field name `0`: + +``` +#![feature(offset_of)] + +let x: usize = std::mem::offset_of!(Option, 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..394a9033765 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3103,16 +3103,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[Ident], expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { + use rustc_target::abi::OffsetOfIdx::*; + let container = self.to_ty(container).normalized; 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); + + if let Some((index, variant)) = container_def.variants() + .iter_enumerated() + .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) + { + 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); + + if let Some((subindex, field)) = variant.fields + .iter_enumerated() + .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident) + { + 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(Variant(index)); + field_indices.push(Field(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 +3187,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(Field(index)); current_container = field_ty; continue; @@ -3149,7 +3201,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(Field(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..d0094a81634 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -17,7 +17,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{self as hir}; use rustc_hir::{self, CoroutineKind}; use rustc_index::IndexVec; -use rustc_target::abi::{FieldIdx, VariantIdx}; +use rustc_target::abi::{FieldIdx, OffsetOfIdx, VariantIdx}; use rustc_ast::Mutability; use rustc_span::def_id::LocalDefId; @@ -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), + OffsetOf(&'tcx List), } #[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 f1747356139..2ee726c76bf 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -25,7 +25,8 @@ use rustc_middle::ty::{ }; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, ErrorGuaranteed, Span, Symbol, DUMMY_SP}; -use rustc_target::abi::{FieldIdx, VariantIdx}; +use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_target::abi::{FieldIdx, OffsetOfIdx, VariantIdx}; use rustc_target::asm::InlineAsmRegOrRegClass; use std::fmt; use std::ops::Index; @@ -490,7 +491,7 @@ pub enum ExprKind<'tcx> { /// Field offset (`offset_of!`) OffsetOf { container: Ty<'tcx>, - fields: &'tcx List, + fields: &'tcx List, }, /// 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..ce5198ee3ba 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, OffsetOfIdx}; pub use rustc_type_ir::{TyDecoder, TyEncoder}; use std::hash::Hash; use std::intrinsics; @@ -414,6 +414,15 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> RefDecodable<'tcx, D> for ty::List { + fn decode(decoder: &mut D) -> &'tcx Self { + let len = decoder.read_usize(); + decoder + .interner() + .mk_offset_of_from_iter((0..len).map::(|_| Decodable::decode(decoder))) + } +} + impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, @@ -426,6 +435,7 @@ impl_decodable_via_ref! { &'tcx ty::List, &'tcx ty::List>, &'tcx ty::List, + &'tcx ty::List, } #[macro_export] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0b3b6700cec..c89ae1e661e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -63,7 +63,7 @@ use rustc_session::{Limit, MetadataKind, Session}; use rustc_span::def_id::{DefPathHash, StableCrateId}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; +use rustc_target::abi::{FieldIdx, Layout, LayoutS, OffsetOfIdx, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; use rustc_type_ir::TyKind::*; use rustc_type_ir::WithCachedTypeInfo; @@ -163,6 +163,7 @@ pub struct CtxtInterners<'tcx> { predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>, fields: InternedSet<'tcx, List>, local_def_ids: InternedSet<'tcx, List>, + offset_of: InternedSet<'tcx, List>, } 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(OffsetOfIdx), ); 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(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply>, + { + 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..623d8d61d2d 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, OffsetOfIdx}; 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>, /// Container types and field indices of `offset_of!` expressions - offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec)>, + offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec)>, } impl<'tcx> TypeckResults<'tcx> { @@ -464,11 +464,13 @@ impl<'tcx> TypeckResults<'tcx> { &self.coercion_casts } - pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec)> { + pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec)> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data } } - pub fn offset_of_data_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec)> { + pub fn offset_of_data_mut( + &mut self, + ) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec)> { 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..ca10dc48eb8 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -287,7 +287,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { 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())) + .offset_of_subfield(&self.ecx, fields.iter()) .bytes(), _ => return ValueOrPlace::Value(FlatSet::Top), }; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 87d4850b475..3b4f6a618c8 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -248,6 +248,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) { + use rustc_target::abi::OffsetOfIdx::*; + let data = self.typeck_results().offset_of_data(); let &(container, ref indices) = data.get(expr.hir_id).expect("no offset_of_data for offset_of"); @@ -256,11 +258,23 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { let param_env = self.tcx.param_env(body_did); let mut current_ty = container; + let mut indices = indices.into_iter(); - for &index in indices { - match current_ty.kind() { - ty::Adt(def, subst) => { - let field = &def.non_enum_variant().fields[index]; + while let Some(&index) = indices.next() { + match (current_ty.kind(), index) { + (ty::Adt(def, subst), Field(field)) if !def.is_enum() => { + let field = &def.non_enum_variant().fields[field]; + + self.insert_def_id(field.did); + let field_ty = field.ty(self.tcx, subst); + + current_ty = self.tcx.normalize_erasing_regions(param_env, field_ty); + } + (ty::Adt(def, subst), Variant(variant)) if def.is_enum() => { + let Some(&Field(field)) = indices.next() else { + span_bug!(expr.span, "variant must be followed by field in offset_of") + }; + let field = &def.variant(variant).fields[field]; self.insert_def_id(field.did); let field_ty = field.ty(self.tcx, subst); @@ -269,11 +283,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } // we don't need to mark tuple fields as live, // but we may need to mark subfields - ty::Tuple(tys) => { + (ty::Tuple(tys), Field(field)) => { 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"), + (_, Field(_)) => span_bug!(expr.span, "named field access on non-ADT"), + (_, Variant(_)) => span_bug!(expr.span, "enum variant access on non-enum"), } } } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 5ab5a048ffa..e64afa5f8d9 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::interpret::{alloc_range, AllocId}; use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt, Variance}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_target::abi::FieldIdx; +use rustc_target::abi::{FieldIdx, OffsetOfIdx}; use stable_mir::mir::mono::InstanceDef; use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx}; use stable_mir::ty::{ @@ -643,6 +643,16 @@ impl<'tcx> Stable<'tcx> for FieldIdx { } } +impl<'tcx> Stable<'tcx> for OffsetOfIdx { + type T = usize; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + OffsetOfIdx::Field(f) => f.as_usize(), + OffsetOfIdx::Variant(v) => v.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..a37a5bb2689 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -250,7 +250,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_transparent(self) } - pub fn offset_of_subfield(self, cx: &C, indices: impl Iterator) -> Size + pub fn offset_of_subfield(self, cx: &C, indices: impl Iterator) -> Size where Ty: TyAbiInterface<'a, C>, { @@ -258,13 +258,21 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { let mut offset = Size::ZERO; for index in indices { - offset += layout.fields.offset(index); - layout = layout.field(cx, index); - assert!( - layout.is_sized(), - "offset of unsized field (type {:?}) cannot be computed statically", - layout.ty - ); + match index { + OffsetOfIdx::Field(field) => { + let index = field.index(); + offset += layout.fields.offset(index); + layout = layout.field(cx, index); + assert!( + layout.is_sized(), + "offset of unsized field (type {:?}) cannot be computed statically", + layout.ty + ); + } + OffsetOfIdx::Variant(variant) => { + layout = layout.for_variant(cx, variant); + } + } } offset 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..e460c08f9bb 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, [Field(0)]); - _1 = must_use::(move _2) -> [return: bb1, unwind unreachable]; + _2 = const 4_usize; + _1 = must_use::(const 4_usize) -> [return: bb1, unwind unreachable]; @@ -37,7 +52,7 @@ StorageDead(_2); StorageLive(_3); StorageLive(_4); -- _4 = OffsetOf(Alpha, [1]); +- _4 = OffsetOf(Alpha, [Field(1)]); - _3 = must_use::(move _4) -> [return: bb2, unwind unreachable]; + _4 = const 0_usize; + _3 = must_use::(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, [Field(2), Field(0)]); - _5 = must_use::(move _6) -> [return: bb3, unwind unreachable]; + _6 = const 2_usize; + _5 = must_use::(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, [Field(2), Field(1)]); - _7 = must_use::(move _8) -> [return: bb4, unwind unreachable]; + _8 = const 3_usize; + _7 = must_use::(const 3_usize) -> [return: bb4, unwind unreachable]; @@ -65,7 +80,40 @@ bb4: { StorageDead(_8); + StorageLive(_9); + StorageLive(_10); +- _10 = OffsetOf(Epsilon, [Variant(0), Field(0)]); +- _9 = must_use::(move _10) -> [return: bb5, unwind unreachable]; ++ _10 = const 1_usize; ++ _9 = must_use::(const 1_usize) -> [return: bb5, unwind unreachable]; + } + + bb5: { + StorageDead(_10); + StorageLive(_11); + StorageLive(_12); +- _12 = OffsetOf(Epsilon, [Variant(0), Field(1)]); +- _11 = must_use::(move _12) -> [return: bb6, unwind unreachable]; ++ _12 = const 2_usize; ++ _11 = must_use::(const 2_usize) -> [return: bb6, unwind unreachable]; + } + + bb6: { + StorageDead(_12); + StorageLive(_13); + StorageLive(_14); +- _14 = OffsetOf(Epsilon, [Variant(2), Field(0)]); +- _13 = must_use::(move _14) -> [return: bb7, unwind unreachable]; ++ _14 = const 4_usize; ++ _13 = must_use::(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..3b8d0d75101 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, [Field(0)]); - _1 = must_use::(move _2) -> [return: bb1, unwind continue]; + _2 = const 4_usize; + _1 = must_use::(const 4_usize) -> [return: bb1, unwind continue]; @@ -37,7 +52,7 @@ StorageDead(_2); StorageLive(_3); StorageLive(_4); -- _4 = OffsetOf(Alpha, [1]); +- _4 = OffsetOf(Alpha, [Field(1)]); - _3 = must_use::(move _4) -> [return: bb2, unwind continue]; + _4 = const 0_usize; + _3 = must_use::(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, [Field(2), Field(0)]); - _5 = must_use::(move _6) -> [return: bb3, unwind continue]; + _6 = const 2_usize; + _5 = must_use::(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, [Field(2), Field(1)]); - _7 = must_use::(move _8) -> [return: bb4, unwind continue]; + _8 = const 3_usize; + _7 = must_use::(const 3_usize) -> [return: bb4, unwind continue]; @@ -65,7 +80,40 @@ bb4: { StorageDead(_8); + StorageLive(_9); + StorageLive(_10); +- _10 = OffsetOf(Epsilon, [Variant(0), Field(0)]); +- _9 = must_use::(move _10) -> [return: bb5, unwind continue]; ++ _10 = const 1_usize; ++ _9 = must_use::(const 1_usize) -> [return: bb5, unwind continue]; + } + + bb5: { + StorageDead(_10); + StorageLive(_11); + StorageLive(_12); +- _12 = OffsetOf(Epsilon, [Variant(0), Field(1)]); +- _11 = must_use::(move _12) -> [return: bb6, unwind continue]; ++ _12 = const 2_usize; ++ _11 = must_use::(const 2_usize) -> [return: bb6, unwind continue]; + } + + bb6: { + StorageDead(_12); + StorageLive(_13); + StorageLive(_14); +- _14 = OffsetOf(Epsilon, [Variant(2), Field(0)]); +- _13 = must_use::(move _14) -> [return: bb7, unwind continue]; ++ _14 = const 4_usize; ++ _13 = must_use::(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..f26b104e065 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, [0]); + _2 = OffsetOf(Gamma, [Field(0)]); _1 = must_use::(move _2) -> [return: bb1, unwind unreachable]; } @@ -35,7 +50,7 @@ StorageDead(_2); StorageLive(_3); StorageLive(_4); - _4 = OffsetOf(Gamma, [1]); + _4 = OffsetOf(Gamma, [Field(1)]); _3 = must_use::(move _4) -> [return: bb2, unwind unreachable]; } @@ -43,7 +58,7 @@ StorageDead(_4); StorageLive(_5); StorageLive(_6); - _6 = OffsetOf(Delta, [1]); + _6 = OffsetOf(Delta, [Field(1)]); _5 = must_use::(move _6) -> [return: bb3, unwind unreachable]; } @@ -51,13 +66,40 @@ StorageDead(_6); StorageLive(_7); StorageLive(_8); - _8 = OffsetOf(Delta, [2]); + _8 = OffsetOf(Delta, [Field(2)]); _7 = must_use::(move _8) -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_8); + StorageLive(_9); + StorageLive(_10); + _10 = OffsetOf(Zeta, [Variant(0), Field(0)]); + _9 = must_use::(move _10) -> [return: bb5, unwind unreachable]; + } + + bb5: { + StorageDead(_10); + StorageLive(_11); + StorageLive(_12); + _12 = OffsetOf(Zeta, [Variant(0), Field(1)]); + _11 = must_use::(move _12) -> [return: bb6, unwind unreachable]; + } + + bb6: { + StorageDead(_12); + StorageLive(_13); + StorageLive(_14); + _14 = OffsetOf(Zeta, [Variant(1), Field(0)]); + _13 = must_use::(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..0f8750fce46 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, [0]); + _2 = OffsetOf(Gamma, [Field(0)]); _1 = must_use::(move _2) -> [return: bb1, unwind continue]; } @@ -35,7 +50,7 @@ StorageDead(_2); StorageLive(_3); StorageLive(_4); - _4 = OffsetOf(Gamma, [1]); + _4 = OffsetOf(Gamma, [Field(1)]); _3 = must_use::(move _4) -> [return: bb2, unwind continue]; } @@ -43,7 +58,7 @@ StorageDead(_4); StorageLive(_5); StorageLive(_6); - _6 = OffsetOf(Delta, [1]); + _6 = OffsetOf(Delta, [Field(1)]); _5 = must_use::(move _6) -> [return: bb3, unwind continue]; } @@ -51,13 +66,40 @@ StorageDead(_6); StorageLive(_7); StorageLive(_8); - _8 = OffsetOf(Delta, [2]); + _8 = OffsetOf(Delta, [Field(2)]); _7 = must_use::(move _8) -> [return: bb4, unwind continue]; } bb4: { StorageDead(_8); + StorageLive(_9); + StorageLive(_10); + _10 = OffsetOf(Zeta, [Variant(0), Field(0)]); + _9 = must_use::(move _10) -> [return: bb5, unwind continue]; + } + + bb5: { + StorageDead(_10); + StorageLive(_11); + StorageLive(_12); + _12 = OffsetOf(Zeta, [Variant(0), Field(1)]); + _11 = must_use::(move _12) -> [return: bb6, unwind continue]; + } + + bb6: { + StorageDead(_12); + StorageLive(_13); + StorageLive(_14); + _14 = OffsetOf(Zeta, [Variant(1), Field(0)]); + _13 = must_use::(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 { y: u16, } +enum Epsilon { + A(u8, u16), + B, + C { c: u32 } +} + +enum Zeta { + 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() { let gy = offset_of!(Gamma, y); let dx = offset_of!(Delta, x); let dy = offset_of!(Delta, y); + let zA0 = offset_of!(Zeta, A.0); + let zA1 = offset_of!(Zeta, A.1); + let zB = offset_of!(Zeta, B.0); } fn main() { diff --git a/tests/ui/offset-of/offset-of-enum.rs b/tests/ui/offset-of/offset-of-enum.rs index d73505821ff..92d34e67c71 100644 --- a/tests/ui/offset-of/offset-of-enum.rs +++ b/tests/ui/offset-of/offset-of-enum.rs @@ -9,5 +9,6 @@ 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); } diff --git a/tests/ui/offset-of/offset-of-enum.stderr b/tests/ui/offset-of/offset-of-enum.stderr index 6958d199fbd..1d849e682e6 100644 --- a/tests/ui/offset-of/offset-of-enum.stderr +++ b/tests/ui/offset-of/offset-of-enum.stderr @@ -7,13 +7,13 @@ 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[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: aborting due to 2 previous errors -Some errors have detailed explanations: E0573, E0609. +Some errors have detailed explanations: E0573, E0795. For more information about an error, try `rustc --explain E0573`. 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`.