diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 082d5e88ac754..1feaf8b59000d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2488,6 +2488,11 @@ pub enum TyKind { ImplTrait(NodeId, #[visitable(extra = BoundKind::Impl)] GenericBounds), /// No-op; kept solely so that we can pretty-print faithfully. Paren(Box), + /// A `field_of` expression (e.g., `builtin # field_of(Struct, fields)`). + /// + /// Usually not written directly in user code but + /// indirectly via the macro `core::field::field_of!(...)`. + FieldOf(Box, Vec), /// Unused for now. Typeof(AnonConst), /// This means the type should be inferred instead of it having been diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index f7daec4b0648c..c65d1f501ab8e 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -294,6 +294,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::Never | ast::TyKind::Tup(..) | ast::TyKind::Paren(..) + | ast::TyKind::FieldOf(..) | ast::TyKind::Typeof(..) | ast::TyKind::Infer | ast::TyKind::ImplicitSelf diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4e2243e87873c..86c4978b22d5d 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1354,6 +1354,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_ty(ty, itctx), self.lower_array_length_to_const_arg(length), ), + TyKind::FieldOf(container, fields) => hir::TyKind::FieldOf( + self.lower_ty(container, itctx), + self.arena.alloc_from_iter(fields.iter().map(|field| self.lower_ident(*field))), + ), TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const_to_anon_const(expr)), TyKind::TraitObject(bounds, kind) => { let mut lifetime_bound = None; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 41b520b04c993..88ddbf46ec0cf 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1324,6 +1324,25 @@ impl<'a> State<'a> { self.print_expr(&length.value, FixupContext::default()); self.word("]"); } + ast::TyKind::FieldOf(container, fields) => { + self.word("builtin # field_of"); + self.popen(); + let ib = self.ibox(0); + self.print_type(container); + self.word(","); + self.space(); + + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); + + for &field in rest { + self.word("."); + self.print_ident(field); + } + } + self.end(ib); + self.pclose(); + } ast::TyKind::Typeof(e) => { self.word("typeof("); self.print_expr(&e.value, FixupContext::default()); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a85dcf64d8d46..0050c40d1b0af 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1892,6 +1892,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } ty::Bool + | ty::Field(..) | ty::Char | ty::Int(_) | ty::Uint(_) @@ -1936,6 +1937,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Coroutine(_, _) | ty::Tuple(_) => (), ty::Bool + | ty::Field(..) | ty::Char | ty::Int(_) | ty::Uint(_) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index bc20c75941349..449138cc64303 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -482,6 +482,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), + ty::Field(..) => build_field_type_di_node(cx, unique_type_id), _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), }; @@ -1273,6 +1274,32 @@ fn build_closure_env_di_node<'ll, 'tcx>( ) } +fn build_field_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let ty = unique_type_id.expect_ty(); + let ty::Field(_, _) = ty.kind() else { + bug!("build_field_type_di_node() called with non-field-type: {ty:?}") + }; + let type_name = compute_debuginfo_type_name(cx.tcx, ty, false); + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + None, + cx.size_and_align_of(ty), + None, + DIFlags::FlagZero, + ), + |_, _| smallvec![], + NO_GENERICS, + ) +} + /// Build the debuginfo node for a Rust `union` type. fn build_union_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 18279a4d05fe0..cd7d4a770033a 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -119,6 +119,19 @@ fn push_debuginfo_type_name<'tcx>( push_generic_params_internal(tcx, args, output, visited); } } + ty::Field(container, field_path) => { + output.push_str("field_of!("); + push_debuginfo_type_name(tcx, container, qualified, output, visited); + output.push_str(", "); + field_path.walk(tcx, container, |_, name, _, last| { + output.push_str(name.as_str()); + if !last { + output.push('.'); + } + std::ops::ControlFlow::<()>::Continue(()) + }); + output.push(')'); + } ty::Tuple(component_types) => { if cpp_like_debuginfo { output.push_str("tuple$<"); diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 7c41258ebfe5f..f010010cd4398 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -162,7 +162,8 @@ fn const_to_valtree_inner<'tcx>( branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) } - ty::Never + ty::Field(..) + | ty::Never | ty::Error(_) | ty::Foreign(..) | ty::Infer(ty::FreshIntTy(_)) @@ -325,7 +326,9 @@ pub fn valtree_to_const_value<'tcx>( op_to_const(&ecx, &place.into(), /* for diagnostics */ false) } - ty::Never + + ty::Field(..) + | ty::Never | ty::Error(_) | ty::Foreign(..) | ty::Infer(ty::FreshIntTy(_)) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 785978b4d7111..05fe0c14168a1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -7,10 +7,10 @@ use std::assert_matches::assert_matches; use rustc_abi::{FieldIdx, HasDataLayout, Size}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; -use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; +use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic, NullOp}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -188,6 +188,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::CoroutineWitness(..) | ty::UnsafeBinder(_) | ty::Never + | ty::Field(..) | ty::Tuple(_) | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), }; @@ -645,6 +646,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::fmuladdf64 => self.float_muladd_intrinsic::(args, dest)?, sym::fmuladdf128 => self.float_muladd_intrinsic::(args, dest)?, + sym::field_offset => self.field_offset(instance, dest)?, + // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), } @@ -654,6 +657,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(true) } + fn field_offset( + &mut self, + instance: ty::Instance<'tcx>, + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> { + assert_eq!(instance.args.len(), 1); + let ty = instance.args.type_at(0); + match ty.kind() { + &ty::Field(container, field_path) => { + let offset = self.nullary_op(NullOp::OffsetOf(field_path), container)?; + self.write_immediate(*offset, dest) + } + ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) | ty::Infer(..) => { + // This can happen in code which is generic over the field type. + throw_inval!(TooGeneric) + } + _ => span_bug!(self.cur_span(), "expected field representing type, found {ty}"), + } + } + pub(super) fn eval_nondiverging_intrinsic( &mut self, intrinsic: &NonDivergingIntrinsic<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 1c1c59da9d886..ef1fac1be4e68 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -507,6 +507,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::Field(..) | ty::Error(_) => true, ty::Str | ty::Slice(_) | ty::Dynamic(_, _) | ty::Foreign(..) => false, diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5f088fe37e80b..ef6a7fe0a14d9 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -778,6 +778,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { interp_ok(true) } ty::Never => throw_validation_failure!(self.path, NeverVal), + ty::Field(..) => throw_validation_failure!(self.path, NeverVal), ty::Foreign(..) | ty::FnDef(..) => { // Nothing to check. interp_ok(true) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index db651811551f3..53cdac721e85c 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::ops::ControlFlow; use rustc_data_structures::intern::Interned; use rustc_hir::def_id::{CrateNum, DefId}; @@ -58,6 +59,29 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::CoroutineClosure(def_id, args) | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args), ty::Foreign(def_id) => self.print_def_path(def_id, &[]), + ty::Field(container, field_path) => { + write!(self, "field_of!(")?; + self.print_type(container)?; + write!(self, ", ")?; + field_path + .walk(self.tcx, container, |_, name, _, last| { + match write!(self, "{name}") { + Ok(()) => ControlFlow::Continue(()), + Err(err) => ControlFlow::Break(Err(err)), + }?; + if !last { + match write!(self, ".") { + Ok(()) => ControlFlow::Continue(()), + Err(err) => ControlFlow::Break(Err(err)), + } + } else { + ControlFlow::Continue(()) + } + }) + .unwrap_or(Ok(()))?; + write!(self, ")")?; + Ok(()) + } ty::Alias(ty::Free, _) => bug!("type_name: unexpected free alias"), ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e63f29a9570de..d65cb241b9dea 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -500,6 +500,8 @@ declare_features! ( (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. (unstable, ffi_pure, "1.45.0", Some(58329)), + /// Experimental field projections. + (incomplete, field_projections, "CURRENT_RUSTC_VERSION", Some(145383)), /// Controlling the behavior of fmt::Debug (unstable, fmt_debug, "1.82.0", Some(129709)), /// Allows using `#[align(...)]` on function items diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index bc1c47e95c3ae..d11ae7599fdd6 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3736,6 +3736,8 @@ pub enum TyKind<'hir, Unambig = ()> { /// We use pointer tagging to represent a `&'hir Lifetime` and `TraitObjectSyntax` pair /// as otherwise this type being `repr(C)` would result in `TyKind` increasing in size. TraitObject(&'hir [PolyTraitRef<'hir>], TaggedRef<'hir, Lifetime, TraitObjectSyntax>), + /// Field representing type (`field_of!`) + FieldOf(&'hir Ty<'hir>, &'hir [Ident]), /// Unused for now. Typeof(&'hir AnonConst), /// Placeholder for a type that has failed to be defined. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index eb682f32111a5..c555dee5610f7 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1026,6 +1026,12 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) - } try_visit!(visitor.visit_lifetime(lifetime)); } + TyKind::FieldOf(ty, fields) => { + try_visit!(visitor.visit_ty_unambig(ty)); + for field in fields { + try_visit!(visitor.visit_ident(*field)); + } + } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), TyKind::InferDelegation(..) | TyKind::Err(_) => {} TyKind::Pat(ty, pat) => { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 311cf8f995c82..27ae6923d9c5a 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -441,6 +441,12 @@ language_item_table! { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); + + // Experimental lang items for field projections. + Field, sym::Field, field_trait, Target::Trait, GenericRequirement::None; + FieldBase, sym::FieldBase, field_base, Target::AssocTy, GenericRequirement::None; + FieldType, sym::FieldType, field_type, Target::AssocTy, GenericRequirement::None; + FieldOffset, sym::FieldOffset, field_offset, Target::AssocConst, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 06f2ec512ab1f..7621497693aa6 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -612,3 +612,4 @@ hir_analysis_wrong_number_of_generic_arguments_to_intrinsic = [one] parameter *[other] parameters } +hir_analysis_no_field_on_type = no field `{$field}` on type `{$ty}` diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bc3448be5823e..536b5b54515c2 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -116,6 +116,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fabsf128 | sym::fadd_algebraic | sym::fdiv_algebraic + | sym::field_offset | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -749,6 +750,8 @@ pub(crate) fn check_intrinsic_type( | sym::atomic_xor => (2, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(1)], param(0)), sym::atomic_fence | sym::atomic_singlethreadfence => (0, 1, Vec::new(), tcx.types.unit), + sym::field_offset => (1, 0, Vec::new(), tcx.types.usize), + other => { tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); return; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0a555c7f6e9df..2f8d104a738a8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -259,6 +259,17 @@ pub(super) fn check_item<'tcx>( .with_span_label(sp, "auto trait") .emit()); } + if is_auto && let rustc_hir::TyKind::FieldOf(..) = impl_.self_ty.kind { + res = res.and(Err(tcx + .dcx() + .struct_span_err( + item.span, + "impls of auto traits for field representing types not supported", + ) + .with_span_label(impl_.self_ty.span, "field representing type") + .with_span_label(of_trait.trait_ref.path.span, "auto trait") + .emit())); + } match header.polarity { ty::ImplPolarity::Positive => { res = res.and(check_impl(tcx, item, impl_)); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index b069a74bf5adc..fbff3e7e7db28 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -7,6 +7,8 @@ //! `tcx.inherent_impls(def_id)`). That value, however, //! is computed by selecting an idea from this table. +use std::ops::ControlFlow; + use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; @@ -14,7 +16,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type}; -use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; +use rustc_middle::ty::{self, CrateInherentImpls, FieldPath, Ty, TyCtxt}; use rustc_span::{ErrorGuaranteed, sym}; use crate::errors; @@ -112,6 +114,40 @@ impl<'tcx> InherentCollect<'tcx> { } } + fn check_field_type( + &mut self, + impl_def_id: LocalDefId, + container: Ty<'tcx>, + field_path: FieldPath<'tcx>, + ) -> Result<(), ErrorGuaranteed> { + if !matches!(container.kind(), ty::Adt(..)) { + return Err(self + .tcx + .dcx() + .emit_err(errors::InherentTyOutsideNew { span: self.tcx.def_span(impl_def_id) })); + } + if field_path + .walk(self.tcx, container, |base, _, _, _| { + if let ty::Adt(def, _) = base.kind() + && !def.did().is_local() + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_none() + { + self.impls_map + .incoherent_impls + .entry(SimplifiedType::Field) + .or_default() + .push(impl_def_id); + } + + Ok(()) + } + fn check_primitive_impl( &mut self, impl_def_id: LocalDefId, @@ -166,6 +202,7 @@ impl<'tcx> InherentCollect<'tcx> { } match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), + ty::Field(container, field_path) => self.check_field_type(id, container, field_path), ty::Foreign(did) => self.check_def_id(id, self_ty, did), ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { self.check_def_id(id, self_ty, data.principal_def_id().unwrap()) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5a61248cab8fe..163523f42b1ef 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -1,6 +1,8 @@ //! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. +use std::ops::ControlFlow; + use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; @@ -151,6 +153,34 @@ pub(crate) fn orphan_check_impl( }, ), + ty::Field(container, field_path) => { + let non_local_impl = if !matches!(container.kind(), ty::Adt(..)) { + NonlocalImpl::DisallowBecauseNonlocal + } else { + field_path + .walk(tcx, *container, |base, _, _, _| match base.kind() { + ty::Adt(def, _) => { + // We don't check the field type for locality, since having a non-local + // type as a field in a struct shouldn't make the field type non-local. + if !def.did().is_local() { + ControlFlow::Break(NonlocalImpl::DisallowBecauseNonlocal) + } else { + ControlFlow::Continue(()) + } + } + // Tuples are the only exception. + ty::Tuple(..) => ControlFlow::Continue(()), + _ => { + panic!( + "only adts & tuples are supported by `field_of!` but found: {base}" + ) + } + }) + .unwrap_or(NonlocalImpl::Allow) + }; + (LocalImpl::Allow, non_local_impl) + } + // extern { type OpaqueType; } // impl AutoTrait for OpaqueType {} ty::Foreign(did) => ( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 02baaec37138c..5bb88147e76b5 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,16 +15,17 @@ //! crate as a kind of pass. This should eventually be factored away. use std::assert_matches::assert_matches; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::iter; use std::ops::Bound; -use rustc_abi::ExternAbi; +use rustc_abi::{ExternAbi, FIRST_VARIANT}; use rustc_ast::Recovered; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordMap; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, E0228, E0616, ErrorGuaranteed, StashKey, + struct_span_code_err, }; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; @@ -32,22 +33,25 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt}; use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind, find_attr}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause}; +use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause, TraitEngine}; use rustc_middle::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ - self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, TypingMode, fold_regions, + self, AdtKind, Const, FieldPath, FieldPathKind, IsSuggestable, ParamEnv, Ty, TyCtxt, + TypeVisitableExt, TypingMode, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ - FulfillmentError, ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations, + FulfillmentError, ObligationCtxt, StructurallyNormalizeExt, TraitEngineExt, + hir_ty_lowering_dyn_compatibility_violations, }; use tracing::{debug, instrument}; -use crate::errors; +use crate::errors::{self, NoFieldOnType}; use crate::hir_ty_lowering::{ FeedConstTy, HirTyLowerer, InherentAssocCandidate, RegionInferReason, }; @@ -346,6 +350,94 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { ty::Const::new_error_with_message(self.tcx(), span, "bad placeholder constant") } + fn lower_field_path( + &self, + container: &rustc_hir::Ty<'tcx>, + fields: &[Ident], + span: Span, + hir_id: HirId, + field_path_kind: FieldPathKind, + ) -> Result<(Ty<'tcx>, FieldPath<'tcx>), ErrorGuaranteed> { + assert_eq!(field_path_kind, FieldPathKind::FieldOf); + let container = self.lower_ty(container); + + let mut field_indices = Vec::with_capacity(fields.len()); + let mut current_container = container; + let mut fields = fields.into_iter().peekable(); + let mut infcx = None; + let infcx = self.infcx().unwrap_or_else(|| { + assert!(!container.has_infer()); + infcx = Some(self.tcx().infer_ctxt().build(ty::TypingMode::non_body_analysis())); + infcx.as_ref().unwrap() + }); + let cause = ObligationCause::misc(span, self.item_def_id); + let at = infcx.at(&cause, ParamEnv::empty()); + let ty_ch_cx = RefCell::new(>>::new(&infcx)); + + while let Some(&field) = fields.next() { + let last = fields.peek().is_none(); + let result = + at.structurally_normalize_ty(current_container, &mut **ty_ch_cx.borrow_mut()); + let container = match result { + Ok(normalized_ty) => normalized_ty, + Err(errors) => return Err(infcx.err_ctxt().report_fulfillment_errors(errors)), + }; + match container.kind() { + ty::Adt(def, args) if !def.is_enum() => { + let block = self.tcx.local_def_id_to_hir_id(self.item_def_id); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, def.did(), block); + + let fields = &def.non_enum_variant().fields; + if let Some((index, field)) = fields + .iter_enumerated() + .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident) + { + if field.vis.is_accessible_from(def_scope, self.tcx) { + self.tcx.check_stability(field.did, Some(hir_id), span, None); + } else { + let base_did = def.did(); + let struct_path = self.tcx().def_path_str(base_did); + let kind_name = self.tcx().def_descr(base_did); + struct_span_code_err!( + self.dcx(), + ident.span, + E0616, + "field `{ident}` of {kind_name} `{struct_path}` is private", + ) + .with_span_label(ident.span, "private field") + .emit(); + } + + field_indices.push((FIRST_VARIANT, index)); + if field_path_kind == FieldPathKind::OffsetOf || !last { + let field_ty = field.ty(self.tcx, args); + current_container = field_ty; + } + + continue; + } + } + ty::Tuple(tys) => { + if let Ok(index) = field.as_str().parse::() + && field.name == sym::integer(index) + && let Some(&field_ty) = tys.get(index) + { + field_indices.push((FIRST_VARIANT, index.into())); + current_container = field_ty; + continue; + } + } + _ => {} + } + return Err(self + .dcx() + .create_err(NoFieldOnType { span: field.span, ty: container, field }) + .emit()); + } + Ok((container, self.tcx.mk_field_path_from_iter(field_indices.into_iter()))) + } + fn register_trait_ascription_bounds( &self, _: Vec<(ty::Clause<'tcx>, Span)>, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 3b6367219b7ff..6d82229dfeafc 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1707,3 +1707,12 @@ pub(crate) struct AsyncDropWithoutSyncDrop { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_no_field_on_type, code = E0609)] +pub struct NoFieldOnType<'tcx> { + #[primary_span] + pub span: Span, + pub ty: Ty<'tcx>, + pub field: Ident, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index eb660804c2b52..3087a70c5f555 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -37,8 +37,8 @@ use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ - self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, - TypingMode, Upcast, fold_regions, + self, Const, FieldPath, FieldPathKind, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, + TyCtxt, TypeVisitableExt, TypingMode, Upcast, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; @@ -133,6 +133,15 @@ pub trait HirTyLowerer<'tcx> { span: Span, ); + fn lower_field_path( + &self, + container: &hir::Ty<'tcx>, + fields: &[Ident], + span: Span, + hir_id: HirId, + field_path_kind: FieldPathKind, + ) -> Result<(Ty<'tcx>, FieldPath<'tcx>), ErrorGuaranteed>; + /// Probe bounds in scope where the bounded type coincides with the given type parameter. /// /// Rephrased, this returns bounds of the form `T: Trait`, where `T` is a type parameter @@ -2550,6 +2559,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let length = self.lower_const_arg(length, FeedConstTy::No); Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } + hir::TyKind::FieldOf(container, fields) => { + self.lower_field_of(hir_ty, container, fields) + } hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), hir::TyKind::Infer(()) => { // Infer also appears as the type of arguments or return @@ -2716,6 +2728,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn_ptr_ty } + fn lower_field_of( + &self, + hir_ty: &hir::Ty<'tcx>, + container: &hir::Ty<'tcx>, + fields: &[Ident], + ) -> Ty<'tcx> { + match self.lower_field_path( + container, + fields, + hir_ty.span, + hir_ty.hir_id, + FieldPathKind::FieldOf, + ) { + Ok((container, field_path)) => Ty::new_field_type(self.tcx(), container, field_path), + Err(err) => Ty::new_error(self.tcx(), err), + } + } + /// Given a fn_hir_id for a impl function, suggest the type that is found on the /// corresponding function in the trait that the impl implements, if it exists. /// If arg_idx is Some, then it corresponds to an input type index, otherwise it diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index be841675821c1..e5ee4189a1781 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -273,6 +273,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, def.did(), args, variance); } + ty::Field(ty, _) => { + let invar = self.invariant(variance); + self.add_constraints_from_ty(current, ty, invar); + } + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, ref data) => { self.add_constraints_from_invariant_args(current, data.args, variance); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index b9d8eed54a9e3..2c53dcb91fc0b 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -445,6 +445,23 @@ impl<'a> State<'a> { self.print_const_arg(length); self.word("]"); } + hir::TyKind::FieldOf(container, fields) => { + self.word("field_of!("); + self.print_type(container); + self.word(","); + self.space(); + + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); + + for &field in rest { + self.word("."); + self.print_ident(field); + } + } + + self.word(")"); + } hir::TyKind::Typeof(ref e) => { self.word("typeof("); self.print_anon_const(e); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 1ed0756fdd6a7..689fd79deff16 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -189,8 +189,6 @@ hir_typeck_no_associated_item = no {$item_kind} named `{$item_ident}` found for *[other] {" "}in the current scope } -hir_typeck_no_field_on_type = no field `{$field}` on type `{$ty}` - hir_typeck_no_field_on_variant = no field named `{$field}` on enum variant `{$container}::{$ident}` hir_typeck_no_field_on_variant_enum = this enum variant... hir_typeck_no_field_on_variant_field = ...does not have this field diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 40b21c45bc564..f3d366a065222 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -142,6 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::Adt(..) + | ty::Field(..) | ty::Never | ty::Error(_) => { let guar = self diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index d15d092b7d3da..57b3e9fc0a6b2 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -449,15 +449,6 @@ impl HelpUseLatestEdition { } } -#[derive(Diagnostic)] -#[diag(hir_typeck_no_field_on_type, code = E0609)] -pub(crate) struct NoFieldOnType<'tcx> { - #[primary_span] - pub(crate) span: Span, - pub(crate) ty: Ty<'tcx>, - pub(crate) field: Ident, -} - #[derive(Diagnostic)] #[diag(hir_typeck_no_field_on_variant, code = E0609)] pub(crate) struct NoFieldOnVariant<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d1ce0afddf91c..d96492e2254a8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -5,7 +5,7 @@ //! //! See [`rustc_hir_analysis::check`] for more context on type checking in general. -use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_abi::FieldIdx; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -20,7 +20,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath, find_attr}; -use rustc_hir_analysis::NoVariantNamed; +use rustc_hir_analysis::errors::NoFieldOnType; use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; @@ -44,8 +44,8 @@ use crate::coercion::{CoerceMany, DynamicCoerceMany}; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, - NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, + FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, + ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; use crate::{ @@ -3081,7 +3081,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", ident, base, expr, base_ty ); - let mut err = self.no_such_field_err(ident, base_ty, expr); + let mut err = self.no_such_field_err(ident, base_ty, expr.span, expr.hir_id); match *base_ty.peel_refs().kind() { ty::Array(_, len) => { @@ -3294,12 +3294,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - fn no_such_field_err( + pub(crate) fn no_such_field_err( &self, field: Ident, base_ty: Ty<'tcx>, - expr: &hir::Expr<'tcx>, + span: Span, + hir_id: HirId, ) -> Diag<'_> { + let total_span = span; let span = field.span; debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, base_ty); @@ -3308,12 +3310,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.downgrade_to_delayed_bug(); } - if let Some(within_macro_span) = span.within_macro(expr.span, self.tcx.sess.source_map()) { + if let Some(within_macro_span) = span.within_macro(total_span, self.tcx.sess.source_map()) { err.span_label(within_macro_span, "due to this macro variable"); } // try to add a suggestion in case the field is a nested field of a field of the Adt - let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); + let mod_id = self.tcx.parent_module(hir_id).to_def_id(); let (ty, unwrap) = if let ty::Adt(def, args) = base_ty.kind() && (self.tcx.is_diagnostic_item(sym::Result, def.did()) || self.tcx.is_diagnostic_item(sym::Option, def.did())) @@ -3325,7 +3327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (base_ty, "") }; for found_fields in - self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id) + self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, hir_id) { let field_names = found_fields.iter().map(|field| field.0.name).collect::>(); let mut candidate_fields: Vec<_> = found_fields @@ -3337,7 +3339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidate_field, vec![], mod_id, - expr.hir_id, + hir_id, ) }) .map(|mut field_path| { @@ -3350,7 +3352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = candidate_fields.len(); // Don't suggest `.field` if the base expr is from a different // syntax context than the field. - if len > 0 && expr.span.eq_ctxt(field.span) { + if len > 0 && total_span.eq_ctxt(field.span) { err.span_suggestions( field.span.shrink_to_lo(), format!( @@ -3382,7 +3384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } - fn private_field_err(&self, field: Ident, base_did: DefId) -> Diag<'_> { + pub(crate) fn private_field_err(&self, field: Ident, base_did: DefId) -> Diag<'_> { let struct_path = self.tcx().def_path_str(base_did); let kind_name = self.tcx().def_descr(base_did); struct_span_code_err!( @@ -3842,172 +3844,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[Ident], expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let container = self.lower_ty(container).normalized; - - let mut field_indices = Vec::with_capacity(fields.len()); - let mut current_container = container; - let mut fields = fields.into_iter(); - - 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() => { - let block = self.tcx.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 !self.tcx.features().offset_of_enum() { - rustc_session::parse::feature_err( - &self.tcx.sess, - sym::offset_of_enum, - ident.span, - "using enums in offset_of is experimental", - ) - .emit(); - } - - let Some((index, variant)) = container_def - .variants() - .iter_enumerated() - .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) - else { - self.dcx() - .create_err(NoVariantNamed { span: ident.span, ident, ty: container }) - .with_span_label(field.span, "variant not found") - .emit_unless_delay(container.references_error()); - break; - }; - let Some(&subfield) = fields.next() else { - type_error_struct!( - self.dcx(), - ident.span, - container, - E0795, - "`{ident}` is an enum variant; expected field at end of `offset_of`", - ) - .with_span_label(field.span, "enum variant") - .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 { - self.dcx() - .create_err(NoFieldOnVariant { - span: ident.span, - container, - ident, - field: subfield, - enum_span: field.span, - field_span: subident.span, - }) - .emit_unless_delay(container.references_error()); - break; - }; - - let field_ty = self.field_ty(expr.span, field, args); - - // Enums are anyway always sized. But just to safeguard against future - // language extensions, let's double-check. - self.require_type_is_sized( - field_ty, - expr.span, - ObligationCauseCode::FieldSized { - adt_kind: AdtKind::Enum, - span: self.tcx.def_span(field.did), - last: false, - }, - ); - - 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.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 fields = &container_def.non_enum_variant().fields; - if let Some((index, field)) = fields - .iter_enumerated() - .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident) - { - let field_ty = self.field_ty(expr.span, field, args); - - if self.tcx.features().offset_of_slice() { - self.require_type_has_static_alignment(field_ty, expr.span); - } else { - self.require_type_is_sized( - field_ty, - expr.span, - ObligationCauseCode::Misc, - ); - } - - if field.vis.is_accessible_from(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((FIRST_VARIANT, index)); - current_container = field_ty; - - continue; - } - } - ty::Tuple(tys) => { - if let Ok(index) = field.as_str().parse::() - && field.name == sym::integer(index) - { - if let Some(&field_ty) = tys.get(index) { - if self.tcx.features().offset_of_slice() { - self.require_type_has_static_alignment(field_ty, expr.span); - } else { - self.require_type_is_sized( - field_ty, - expr.span, - ObligationCauseCode::Misc, - ); - } - - field_indices.push((FIRST_VARIANT, index.into())); - current_container = field_ty; - - continue; - } - } - } - _ => (), - }; - - self.no_such_field_err(field, container, expr).emit(); - - break; - } + let result = self.lower_field_path( + container, + fields, + expr.span, + expr.hir_id, + ty::FieldPathKind::OffsetOf, + ); - self.typeck_results - .borrow_mut() - .offset_of_data_mut() - .insert(expr.hir_id, (container, field_indices)); + self.typeck_results.borrow_mut().offset_of_data_mut().insert(expr.hir_id, result); self.tcx.types.usize } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 7a060cafeab1c..debfc458ba288 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -9,15 +9,19 @@ use std::cell::{Cell, RefCell}; use std::ops::Deref; use hir::def_id::CRATE_DEF_ID; -use rustc_errors::DiagCtxtHandle; +use rustc_abi::FIRST_VARIANT; +use rustc_errors::{DiagCtxtHandle, E0795}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, HirId, ItemLocalMap}; +use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::hir_ty_lowering::{ HirTyLowerer, InherentAssocCandidate, RegionInferReason, }; use rustc_infer::infer::{self, RegionVariableOrigin}; use rustc_infer::traits::{DynCompatibilityViolation, Obligation}; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, AdtKind, Const, FieldPath, FieldPathKind, Ty, TyCtxt, TypeVisitableExt, +}; use rustc_session::Session; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym}; use rustc_trait_selection::error_reporting::TypeErrCtxt; @@ -26,9 +30,10 @@ use rustc_trait_selection::traits::{ }; use crate::coercion::DynamicCoerceMany; +use crate::errors::NoFieldOnVariant; use crate::fallback::DivergingFallbackBehavior; use crate::fn_ctxt::checks::DivergingBlockBehavior; -use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; +use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt, type_error_struct}; /// The `FnCtxt` stores type-checking context needed to type-check bodies of /// functions, closures, and `const`s, including performing type inference @@ -265,6 +270,202 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { } } + fn lower_field_path( + &self, + container: &hir::Ty<'tcx>, + fields: &[Ident], + span: Span, + hir_id: HirId, + field_path_kind: FieldPathKind, + ) -> Result<(Ty<'tcx>, FieldPath<'tcx>), ErrorGuaranteed> { + let container = self.lower_ty(container).normalized; + + let mut field_indices = Vec::with_capacity(fields.len()); + let mut current_container = container; + let mut fields = fields.into_iter().peekable(); + + while let Some(&field) = fields.next() { + let last = fields.peek().is_none(); + let container = self.structurally_resolve_type(span, current_container); + + match container.kind() { + ty::Adt(container_def, args) if container_def.is_enum() => { + let block = self.tcx.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); + + match field_path_kind { + FieldPathKind::OffsetOf => { + if !self.tcx.features().offset_of_enum() { + rustc_session::parse::feature_err( + &self.tcx.sess, + sym::offset_of_enum, + ident.span, + "using enums in offset_of is experimental", + ) + .emit(); + } + } + FieldPathKind::FieldOf => {} + } + + let Some((index, variant)) = container_def + .variants() + .iter_enumerated() + .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) + else { + return Err(self + .dcx() + .create_err(NoVariantNamed { span: ident.span, ident, ty: container }) + .with_span_label(field.span, "variant not found") + .emit_unless_delay(container.references_error())); + }; + let Some(&subfield) = fields.next() else { + return Err(type_error_struct!( + self.dcx(), + ident.span, + container, + E0795, + "`{ident}` is an enum variant; expected field at end of `offset_of`", + ) + .with_span_label(field.span, "enum variant") + .emit()); + }; + 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 { + return Err(self + .dcx() + .create_err(NoFieldOnVariant { + span: ident.span, + container, + ident, + field: subfield, + enum_span: field.span, + field_span: subident.span, + }) + .emit_unless_delay(container.references_error())); + }; + + // Save the index of all fields regardless of their visibility in case + // of error recovery. + field_indices.push((index, subindex)); + + if field.vis.is_accessible_from(sub_def_scope, self.tcx) { + self.tcx.check_stability(field.did, Some(hir_id), span, None); + } else { + self.private_field_err(ident, container_def.did()).emit(); + } + + if field_path_kind == FieldPathKind::OffsetOf || !last { + let field_ty = self.field_ty(span, field, args); + + // Enums are anyway always sized. But just to safeguard against future + // language extensions, let's double-check. + self.require_type_is_sized( + field_ty, + span, + ObligationCauseCode::FieldSized { + adt_kind: AdtKind::Enum, + span: self.tcx.def_span(field.did), + last: false, + }, + ); + + current_container = field_ty; + } + + continue; + } + ty::Adt(container_def, args) => { + let block = self.tcx.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 fields = &container_def.non_enum_variant().fields; + if let Some((index, field)) = fields + .iter_enumerated() + .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident) + { + // Save the index of all fields regardless of their visibility in case + // of error recovery. + field_indices.push((FIRST_VARIANT, index)); + + if field.vis.is_accessible_from(def_scope, self.tcx) { + self.tcx.check_stability(field.did, Some(hir_id), span, None); + } else { + self.private_field_err(ident, container_def.did()).emit(); + } + + if field_path_kind == FieldPathKind::OffsetOf || !last { + let field_ty = self.field_ty(span, field, args); + + match field_path_kind { + FieldPathKind::OffsetOf => { + if self.tcx.features().offset_of_slice() { + self.require_type_has_static_alignment(field_ty, span); + } else { + self.require_type_is_sized( + field_ty, + span, + ObligationCauseCode::Misc, + ); + } + } + FieldPathKind::FieldOf => { + // A field type always exists regardless of weather it is aligned or + // not. + } + } + current_container = field_ty; + } + + continue; + } + } + ty::Tuple(tys) => { + if let Ok(index) = field.as_str().parse::() + && field.name == sym::integer(index) + { + if let Some(&field_ty) = tys.get(index) { + match field_path_kind { + FieldPathKind::OffsetOf => { + if self.tcx.features().offset_of_slice() { + self.require_type_has_static_alignment(field_ty, span); + } else { + self.require_type_is_sized( + field_ty, + span, + ObligationCauseCode::Misc, + ); + } + } + FieldPathKind::FieldOf => { + // A field type always exists regardless of weather it is aligned or + // not. + } + } + + field_indices.push((FIRST_VARIANT, index.into())); + current_container = field_ty; + + continue; + } + } + } + _ => (), + }; + + return Err(self.no_such_field_err(field, container, span, hir_id).emit()); + } + Ok((container, self.tcx.mk_field_path_from_iter(field_indices.into_iter()))) + } + fn register_trait_ascription_bounds( &self, bounds: Vec<(ty::Clause<'tcx>, Span)>, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 12f80a197b1b8..ec2010e0d6812 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -851,10 +851,25 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never + | ty::Field(..) | ty::Tuple(..) => { self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps) } - _ => {} + + ty::Alias(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Error(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Infer(..) + | ty::Pat(..) + | ty::Placeholder(..) + | ty::UnsafeBinder(..) => {} } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 697029e55f7cb..90775b6cc2c9a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -775,12 +775,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); let common_hir_owner = fcx_typeck_results.hir_owner; - for (local_id, &(container, ref indices)) in - fcx_typeck_results.offset_of_data().items_in_stable_order() - { + for (local_id, &result) in fcx_typeck_results.offset_of_data().items_in_stable_order() { let hir_id = HirId { owner: common_hir_owner, local_id }; - let container = self.resolve(container, &hir_id); - self.typeck_results.offset_of_data_mut().insert(hir_id, (container, indices.clone())); + let result = result + .map(|(container, field_path)| (self.resolve(container, &hir_id), field_path)); + self.typeck_results.offset_of_data_mut().insert(hir_id, result); } } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index e445def4faa73..c01adda1aa22f 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -405,6 +405,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { | ty::Uint(..) | ty::Float(..) | ty::Adt(..) + | ty::Field(..) | ty::Str | ty::Error(_) | ty::Array(..) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5b98c9fafaad4..3fddcaf5a2e41 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -356,6 +356,7 @@ lint_improper_ctypes_enum_repr_help = consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum lint_improper_ctypes_enum_repr_reason = enum has no representation hint +lint_improper_ctypes_field_representing_type_reason = field representing types have no C equivalent lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 7ca57b0094ee3..ebd7ac26ff63c 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -569,6 +569,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } + ty::Field(..) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_field_representing_type_reason, + help: None, + }, + ty::Char => FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_char_reason, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index a823c365394f7..f0ce82567b8b0 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -19,7 +19,7 @@ use smallvec::SmallVec; use super::{BasicBlock, Const, Local, UserTypeProjection}; use crate::mir::coverage::CoverageKind; use crate::ty::adjustment::PointerCoercion; -use crate::ty::{self, GenericArgsRef, List, Region, Ty, UserTypeAnnotationIndex}; +use crate::ty::{self, FieldPath, GenericArgsRef, List, Region, Ty, UserTypeAnnotationIndex}; /// Represents the "flavors" of MIR. /// @@ -1566,7 +1566,7 @@ pub enum NullOp<'tcx> { /// Returns the minimum alignment of a type AlignOf, /// Returns the offset of a field - OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), + OffsetOf(FieldPath<'tcx>), /// Returns whether we should perform some UB-checking at runtime. /// See the `ub_checks` intrinsic docs for details. UbChecks, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index f1d19800a7891..97725180c67c6 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -32,8 +32,8 @@ use crate::thir::visit::for_each_immediate_subpat; use crate::ty::adjustment::PointerCoercion; use crate::ty::layout::IntegerExt; use crate::ty::{ - self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FnSig, GenericArgsRef, List, Ty, - TyCtxt, UpvarArgs, + self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FieldPath, FnSig, GenericArgsRef, + Ty, TyCtxt, UpvarArgs, }; pub mod visit; @@ -563,7 +563,7 @@ pub enum ExprKind<'tcx> { /// Field offset (`offset_of!`) OffsetOf { container: Ty<'tcx>, - fields: &'tcx List<(VariantIdx, FieldIdx)>, + fields: FieldPath<'tcx>, }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index df82c7a826be9..b6b2f6cc8acbf 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -247,6 +247,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { hir::Constness::NotConst => AdtDestructorKind::NotConst, }) } + + fn is_packed(self) -> bool { + self.repr().packed() + } } #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 3f37595d0eef0..0981e32eb0f27 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -489,10 +489,10 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for &'tcx ty::List { } } -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<(VariantIdx, FieldIdx)> { - fn decode(decoder: &mut D) -> &'tcx Self { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::FieldPath<'tcx> { + fn decode(decoder: &mut D) -> Self { let len = decoder.read_usize(); - decoder.interner().mk_offset_of_from_iter( + decoder.interner().mk_field_path_from_iter( (0..len).map::<(VariantIdx, FieldIdx), _>(|_| Decodable::decode(decoder)), ) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1b89a49cf9893..b96167b3cea2b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -79,9 +79,9 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, - GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, ParamTy, - Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, FieldPath, GenericArg, + GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, + ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, ValTree, ValTreeKind, Visibility, }; @@ -157,6 +157,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type PatList = &'tcx List>; type Safety = hir::Safety; type Abi = ExternAbi; + type FieldPath = FieldPath<'tcx>; type Const = ty::Const<'tcx>; type PlaceholderConst = ty::PlaceholderConst; @@ -565,6 +566,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) @@ -805,6 +807,8 @@ bidirectional_lang_item_map! { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -836,6 +840,7 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, @@ -945,7 +950,7 @@ pub struct CtxtInterners<'tcx> { fields: InternedSet<'tcx, List>, local_def_ids: InternedSet<'tcx, List>, captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, - offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, + field_paths: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>, patterns: InternedSet<'tcx, List>>, outlives: InternedSet<'tcx, List>>, @@ -983,7 +988,7 @@ impl<'tcx> CtxtInterners<'tcx> { fields: InternedSet::with_capacity(N * 4), local_def_ids: InternedSet::with_capacity(N), captures: InternedSet::with_capacity(N), - offset_of: InternedSet::with_capacity(N), + field_paths: InternedSet::with_capacity(N), valtree: InternedSet::with_capacity(N), patterns: InternedSet::with_capacity(N), outlives: InternedSet::with_capacity(N), @@ -2617,6 +2622,7 @@ impl<'tcx> TyCtxt<'tcx> { fmt, self, Adt, + Field, Array, Slice, RawPtr, @@ -2820,7 +2826,7 @@ slice_interners!( fields: pub mk_fields(FieldIdx), local_def_ids: intern_local_def_ids(LocalDefId), captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), - offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), + field_paths: pub mk_field_path((VariantIdx, FieldIdx)), patterns: pub mk_patterns(Pattern<'tcx>), outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>), predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)), @@ -3223,12 +3229,12 @@ 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 + pub fn mk_field_path_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: CollectAndApply<(VariantIdx, FieldIdx), &'tcx List<(VariantIdx, FieldIdx)>>, + T: CollectAndApply<(VariantIdx, FieldIdx), FieldPath<'tcx>>, { - T::collect_and_apply(iter, |xs| self.mk_offset_of(xs)) + T::collect_and_apply(iter, |xs| FieldPath(self.mk_field_path(xs))) } pub fn mk_args_trait( @@ -3473,6 +3479,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn intrinsic(self, def_id: impl IntoQueryParam + Copy) -> Option { match self.def_kind(def_id) { DefKind::Fn | DefKind::AssocFn => {} + DefKind::AssocConst + if self.is_lang_item(def_id.into_query_param(), LangItem::UnalignedFieldOFFSET) => { + } _ => return None, } self.intrinsic_raw(def_id) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 66542525d2841..941838934bf37 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -181,6 +181,7 @@ impl<'tcx> Ty<'tcx> { | ty::Float(_) | ty::Str | ty::Never => "type".into(), + ty::Field(..) => "field representing type".into(), ty::Tuple(tys) if tys.is_empty() => "unit type".into(), ty::Adt(def, _) => def.descr().into(), ty::Foreign(_) => "extern type".into(), diff --git a/compiler/rustc_middle/src/ty/field_path.rs b/compiler/rustc_middle/src/ty/field_path.rs new file mode 100644 index 0000000000000..daa4be507d9f6 --- /dev/null +++ b/compiler/rustc_middle/src/ty/field_path.rs @@ -0,0 +1,94 @@ +use std::ops::ControlFlow; + +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_macros::{HashStable, TyEncodable}; +use rustc_span::{Symbol, sym}; + +use crate::ty::{self, List, Ty, TyCtxt}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FieldPathKind { + OffsetOf, + FieldOf, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash, TyEncodable)] +pub struct FieldPath<'tcx>(pub &'tcx List<(VariantIdx, FieldIdx)>); + +impl<'tcx> IntoIterator for FieldPath<'tcx> { + type Item = <&'tcx List<(VariantIdx, FieldIdx)> as IntoIterator>::Item; + + type IntoIter = <&'tcx List<(VariantIdx, FieldIdx)> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'tcx> IntoIterator for &FieldPath<'tcx> { + type Item = <&'tcx List<(VariantIdx, FieldIdx)> as IntoIterator>::Item; + + type IntoIter = <&'tcx List<(VariantIdx, FieldIdx)> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'tcx> FieldPath<'tcx> { + pub fn iter(self) -> ::IntoIter { + self.into_iter() + } + + pub fn walk( + self, + tcx: TyCtxt<'tcx>, + container: Ty<'tcx>, + mut walker: impl FnMut(Ty<'tcx>, Symbol, Ty<'tcx>, bool) -> ControlFlow, + ) -> Option { + let mut cur = container; + for (i, (variant, field)) in self.iter().enumerate() { + let last = i == self.0.len() - 1; + let (name, field_ty) = match cur.kind() { + ty::Adt(def, args) => { + let variant = def.variant(variant); + let field = &variant.fields[field]; + let field_ty = field.ty(tcx, args); + (field.name, field_ty) + } + ty::Tuple(tys) => { + assert_eq!(FIRST_VARIANT, variant); + (sym::integer(field.index()), tys[field.index()]) + } + _ => bug!("only ADTs and tuples are supported by `field_of!`, found {cur}"), + }; + match walker(cur, name, field_ty, last) { + ControlFlow::Break(val) => return Some(val), + ControlFlow::Continue(()) => cur = field_ty, + } + } + None + } + + pub fn field_ty(self, tcx: TyCtxt<'tcx>, container: Ty<'tcx>) -> Ty<'tcx> { + self.walk(tcx, container, |_, _, ty, last| { + if last { ControlFlow::Break(ty) } else { ControlFlow::Continue(()) } + }) + .expect("field path to have a last segment") + } +} + +impl<'tcx> rustc_type_ir::inherent::FieldPath> for FieldPath<'tcx> { + fn walk( + self, + interner: TyCtxt<'tcx>, + container: Ty<'tcx>, + walker: impl FnMut(Ty<'tcx>, Symbol, Ty<'tcx>, bool) -> ControlFlow, + ) -> Option { + self.walk(interner, container, walker) + } + + fn field_ty(self, interner: TyCtxt<'tcx>, container: Ty<'tcx>) -> Ty<'tcx> { + self.field_ty(interner, container) + } +} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 507a25a432594..e0e62bb866a69 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -836,6 +836,7 @@ where | ty::Float(_) | ty::FnPtr(..) | ty::Never + | ty::Field(..) | ty::FnDef(..) | ty::CoroutineWitness(..) | ty::Foreign(..) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ce4de6b95e0bb..c5173049d2262 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -81,6 +81,7 @@ pub use self::context::{ CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, }; +pub use self::field_path::{FieldPath, FieldPathKind}; pub use self::fold::*; pub use self::instance::{Instance, InstanceKind, ReifyReason, UnusedGenericParams}; pub use self::list::{List, ListWithCachedTypeInfo}; @@ -148,6 +149,7 @@ mod context; mod diagnostics; mod elaborate_impl; mod erase_regions; +mod field_path; mod fold; mod generic_args; mod generics; diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index d636e8ef31ff5..0285a57d20369 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -289,6 +289,7 @@ fn characteristic_def_id_of_type_cached<'a>( ) -> Option { match *ty.kind() { ty::Adt(adt_def, _) => Some(adt_def.did()), + ty::Field(ty, _) => characteristic_def_id_of_type_cached(ty, visited), ty::Dynamic(data, ..) => data.principal_def_id(), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 4d1fcaeda5e26..f4a5756394a4e 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use std::fmt::{self, Write as _}; use std::iter; -use std::ops::{Deref, DerefMut}; +use std::ops::{ControlFlow, Deref, DerefMut}; use rustc_abi::{ExternAbi, Size}; use rustc_apfloat::Float; @@ -784,6 +784,28 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }, }, ty::Adt(def, args) => self.print_def_path(def.did(), args)?, + ty::Field(container, field_path) => { + write!(self, "field_of!(")?; + self.pretty_print_type(container)?; + write!(self, ", ")?; + field_path + .walk(self.tcx(), container, |_, name, _, last| { + match write!(self, "{name}") { + Err(err) => ControlFlow::Break(Err(err)), + Ok(()) => ControlFlow::Continue(()), + }?; + if !last { + match write!(self, ".") { + Err(err) => ControlFlow::Break(Err(err)), + Ok(()) => ControlFlow::Continue(()), + } + } else { + ControlFlow::Continue(()) + } + }) + .unwrap_or(Ok(()))?; + write!(self, ")")?; + } ty::Dynamic(data, r) => { let print_r = self.should_print_optional_region(r); if print_r { diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index f1aa7076d98ac..c858347b3aa50 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -128,6 +128,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::Error(_) | ty::Str | ty::Never + | ty::Field(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnPtr(_, _) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8dc4dfd677ba8..4721c72e67d35 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -390,6 +390,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?), ty::Slice(typ) => ty::Slice(typ.try_fold_with(folder)?), ty::Adt(tid, args) => ty::Adt(tid, args.try_fold_with(folder)?), + ty::Field(ty, field_path) => ty::Field(ty.try_fold_with(folder)?, field_path), ty::Dynamic(trait_ty, region) => { ty::Dynamic(trait_ty.try_fold_with(folder)?, region.try_fold_with(folder)?) } @@ -435,6 +436,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), ty::Adt(tid, args) => ty::Adt(tid, args.fold_with(folder)), + ty::Field(ty, field_path) => ty::Field(ty.fold_with(folder), field_path), ty::Dynamic(trait_ty, region) => { ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) } @@ -479,6 +481,7 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { } ty::Slice(typ) => typ.visit_with(visitor), ty::Adt(_, args) => args.visit_with(visitor), + ty::Field(ty, _) => ty.visit_with(visitor), ty::Dynamic(trait_ty, reg) => { try_visit!(trait_ty.visit_with(visitor)); reg.visit_with(visitor) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a3fdd4e35b6aa..45aca97e62ea6 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -26,8 +26,9 @@ use crate::infer::canonical::Canonical; use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, BoundRegionKind, Discr, FieldPath, GenericArg, GenericArgs, GenericArgsRef, List, + ParamEnv, Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -684,6 +685,15 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, Adt(def, args)) } + pub fn new_field_type( + tcx: TyCtxt<'tcx>, + container: Ty<'tcx>, + field_path: FieldPath<'tcx>, + ) -> Ty<'tcx> { + assert!(!field_path.0.is_empty()); + Ty::new(tcx, Field(container, field_path)) + } + #[inline] pub fn new_foreign(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> { Ty::new(tcx, Foreign(def_id)) @@ -992,6 +1002,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_adt(interner, adt_def, args) } + fn new_field_type( + interner: TyCtxt<'tcx>, + container: Ty<'tcx>, + field_path: FieldPath<'tcx>, + ) -> Self { + Ty::new_field_type(interner, container, field_path) + } + fn new_foreign(interner: TyCtxt<'tcx>, def_id: DefId) -> Self { Ty::new_foreign(interner, def_id) } @@ -1626,6 +1644,7 @@ impl<'tcx> Ty<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(..) + | ty::Field(..) | ty::Foreign(_) | ty::Str | ty::Array(..) @@ -1684,6 +1703,7 @@ impl<'tcx> Ty<'tcx> { // If returned by `struct_tail_raw` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) + | ty::Field(..) // If returned by `struct_tail_raw` this is the empty tuple, // a.k.a. unit type, which is Sized | ty::Tuple(..) => Ok(tcx.types.unit), @@ -1870,6 +1890,7 @@ impl<'tcx> Ty<'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::Field(..) | ty::Error(_) => true, ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => match sizedness { @@ -1943,7 +1964,7 @@ impl<'tcx> Ty<'tcx> { ty::Coroutine(..) | ty::CoroutineWitness(..) => false, // Might be, but not "trivial" so just giving the safe answer. - ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) => false, + ty::Adt(..) | ty::Field(..) | ty::Closure(..) | ty::CoroutineClosure(..) => false, ty::UnsafeBinder(_) => false, @@ -1986,6 +2007,7 @@ impl<'tcx> Ty<'tcx> { }, ty::Adt(_, _) + | ty::Field(_, _) | ty::Tuple(_) | ty::Array(..) | ty::Foreign(_) diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index b276b993ec924..e800e8d975557 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use std::hash::Hash; use std::iter; -use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet}; use rustc_errors::ErrorGuaranteed; @@ -23,8 +23,8 @@ use crate::infer::canonical::Canonical; use crate::mir::FakeReadCause; use crate::traits::ObligationCause; use crate::ty::{ - self, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind, GenericArgs, - GenericArgsRef, Ty, UserArgs, tls, + self, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, FieldPath, GenericArgKind, + GenericArgs, GenericArgsRef, Ty, UserArgs, tls, }; #[derive(TyEncodable, TyDecodable, Debug, HashStable)] @@ -226,7 +226,7 @@ pub struct TypeckResults<'tcx> { pub transmutes_to_check: Vec<(Ty<'tcx>, Ty<'tcx>, HirId)>, /// Container types and field indices of `offset_of!` expressions - offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>, + offset_of_data: ItemLocalMap, FieldPath<'tcx>), ErrorGuaranteed>>, } impl<'tcx> TypeckResults<'tcx> { @@ -561,13 +561,13 @@ impl<'tcx> TypeckResults<'tcx> { pub fn offset_of_data( &self, - ) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)> { + ) -> LocalTableInContext<'_, Result<(Ty<'tcx>, FieldPath<'tcx>), ErrorGuaranteed>> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data } } pub fn offset_of_data_mut( &mut self, - ) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)> { + ) -> LocalTableInContextMut<'_, Result<(Ty<'tcx>, FieldPath<'tcx>), ErrorGuaranteed>> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data } } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a4422abc6883a..17a3a23cd43e6 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1173,7 +1173,8 @@ impl<'tcx> Ty<'tcx> { | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) - | ty::FnPtr(..) => true, + | ty::FnPtr(..) + | ty::Field(..) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_freeze), ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_freeze(), ty::Adt(..) @@ -1218,6 +1219,7 @@ impl<'tcx> Ty<'tcx> { ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unpin), ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unpin(), ty::Adt(..) + | ty::Field(..) | ty::Bound(..) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -1265,6 +1267,7 @@ impl<'tcx> Ty<'tcx> { | ty::RawPtr(..) | ty::FnDef(..) | ty::Error(_) + | ty::Field(..) | ty::FnPtr(..) => true, // FIXME(unsafe_binders): ty::UnsafeBinder(_) => todo!(), @@ -1416,7 +1419,7 @@ impl<'tcx> Ty<'tcx> { pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { // Look for an impl of `StructuralPartialEq`. - ty::Adt(..) => tcx.has_structural_eq_impl(self), + ty::Adt(..) | ty::Field(..) => tcx.has_structural_eq_impl(self), // Primitive types that satisfy `Eq`. ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => true, @@ -1512,6 +1515,7 @@ pub fn needs_drop_components_with_async<'tcx>( | ty::FnDef(..) | ty::FnPtr(..) | ty::Char + | ty::Field(..) | ty::RawPtr(_, _) | ty::Ref(..) | ty::Str => Ok(SmallVec::new()), diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 0b9bc018a09b3..303ffbe19b1f4 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -820,10 +820,15 @@ impl<'tcx> ThirBuildCx<'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_offset_of_from_iter(indices.iter().copied()); - - ExprKind::OffsetOf { container, fields } + match data.get(expr.hir_id).unwrap() { + &Ok((container, fields)) => ExprKind::OffsetOf { container, fields }, + Err(err) => { + // FIXME(field_projections): this `bug!` never seems to be reached, since + // apparently this code is not invoked when computing `typeck_results` + // produced an error. + bug!("could not lower `offset_of!`: {err:?}") + } + } } hir::ExprKind::ConstBlock(ref anon_const) => { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 434f106302f5d..849aca771baf8 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -165,6 +165,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Bound(_, _) | ty::Infer(_) | ty::Error(_) + | ty::Field(..) | ty::Placeholder(_) => { bug!("When Place is Deref it's type shouldn't be {place_ty:#?}") } @@ -183,6 +184,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Coroutine(_, _) | ty::Tuple(_) => (), ty::Bool + | ty::Field(..) | ty::Char | ty::Int(_) | ty::Uint(_) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index e970f7ff81adb..bc961c6d2005c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -933,6 +933,7 @@ fn try_write_constant<'tcx>( // Unsupported for now. ty::Array(_, _) | ty::Pat(_, _) + | ty::Field(..) // Do not attempt to support indirection in constants. | ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Str | ty::Slice(_) diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 9162284422d0d..1738c0dbecc8a 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -371,6 +371,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index b949deb119269..6a0f251cd1a6a 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -419,6 +419,30 @@ where self.found_non_local_ty(ty) } } + ty::Field(container, field_path) => { + if !matches!(container.kind(), ty::Adt(..)) { + self.found_non_local_ty(container) + } else { + field_path + .walk(self.infcx.cx(), container, |base, _, _, _| match base.kind() { + ty::Adt(def, _) => { + // We don't check the field type for locality, since having a non-local + // type as a field in a struct shouldn't make the field type non-local. + if !self.def_id_is_local(def.def_id()) { + ControlFlow::Break(self.found_non_local_ty(base)) + } else { + ControlFlow::Continue(()) + } + } + // Tuples are the only exception. + ty::Tuple(..) => ControlFlow::Continue(()), + _ => { + unreachable!("field_path should only consist of tuples and structs for `ty::Field`, but found {base:?}") + } + }) + .unwrap_or(ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(container))) + } + } ty::Foreign(def_id) => { if self.def_id_is_local(def_id) { ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index d58c264841c85..f72d3b93c350a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -351,6 +351,11 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Vec>; + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; } /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit @@ -609,6 +614,7 @@ where Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } + Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal), _ => Err(NoSolution), } }; @@ -678,6 +684,7 @@ where | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) @@ -796,6 +803,7 @@ where | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 9b3dc1f691fb3..5e70b1638b932 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -37,7 +37,8 @@ where | ty::FnPtr(..) | ty::Error(_) | ty::Never - | ty::Char => Ok(ty::Binder::dummy(vec![])), + | ty::Char + | ty::Field(..) => Ok(ty::Binder::dummy(vec![])), // This branch is only for `experimental_default_bounds`. // Other foreign types were rejected earlier in @@ -118,6 +119,7 @@ where // impl {Meta,}Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char // impl {Meta,}Sized for &mut? T, [T; N], dyn* Trait, !, Coroutine, CoroutineWitness // impl {Meta,}Sized for Closure, CoroutineClosure + // impl {Meta,}Sized for Field ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -134,6 +136,7 @@ where | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) + | ty::Field(..) | ty::Never | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), @@ -218,6 +221,7 @@ where | ty::Foreign(..) | ty::Ref(_, _, Mutability::Mut) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => Err(NoSolution), @@ -377,6 +381,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) @@ -783,6 +790,7 @@ pub(in crate::solve) fn const_conditions_for_destruct( | ty::FnDef(..) | ty::FnPtr(..) | ty::Never + | ty::Field(_, _) | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) | ty::Error(_) => Ok(vec![]), diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 65a5edf6b7250..23fd7c171ebc6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -382,6 +382,13 @@ where ) -> Vec> { unreachable!("Unsize is not const") } + + fn consider_builtin_field_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal, + ) -> Result, NoSolution> { + unreachable!("Field is not const") + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 0674b3d42ab4d..f0e45ea64bade 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -638,7 +638,8 @@ where | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Never - | ty::Foreign(..) => Ty::new_unit(cx), + | ty::Foreign(..) + | ty::Field(..) => Ty::new_unit(cx), ty::Error(e) => Ty::new_error(cx, e), @@ -893,6 +894,7 @@ where | ty::Never | ty::Foreign(..) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Str | ty::Slice(_) | ty::Dynamic(_, _) @@ -947,6 +949,26 @@ where ) -> Result, NoSolution> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<::Interner, Self>, + ) -> Result, NoSolution> { + let cx = ecx.cx(); + let ty::Field(container, field_path) = goal.predicate.self_ty().kind() else { + panic!("only `field_of!()` can implement `Field`") + }; + let ty = if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldBase) { + container + } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldType) { + field_path.field_ty(cx, container) + } else { + panic!("unexpected associated type {:?} in `Field`", goal.predicate) + }; + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index e790ecd595be7..8e4fc5cb4af42 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1,5 +1,7 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. +use std::ops::ControlFlow; + use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; @@ -740,6 +742,7 @@ where | ty::Slice(_) | ty::Foreign(..) | ty::Adt(..) + | ty::Field(..) | ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) @@ -841,6 +844,36 @@ where } }) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + match goal.predicate.self_ty().kind() { + ty::Field(container, field_path) + if field_path + .walk(ecx.cx(), container, |base, _, _, _| { + if let ty::Adt(def, _) = base.kind() + && def.is_packed() + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_none() => + { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + _ => Err(NoSolution), + } + } } /// Small helper function to change the `def_id` of a trait predicate - this is not normally @@ -1253,6 +1286,7 @@ where | ty::Never | ty::Tuple(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::UnsafeBinder(_) => check_impls(), ty::Error(_) => None, } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 81a5d48d94e8c..2ee3ce8a1ee29 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1132,7 +1132,7 @@ impl<'a> Parser<'a> { /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. - fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { + pub(super) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { let mut fields = Vec::new(); let mut trailing_dot = None; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 65347496599d7..47e73c9a7b982 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -330,6 +330,8 @@ impl<'a> Parser<'a> { self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty()? + } else if self.is_builtin() { + self.parse_builtin_ty()? } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer @@ -772,6 +774,42 @@ impl<'a> Parser<'a> { Ok(TyKind::Typeof(expr)) } + fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { + self.parse_builtin(|this, lo, ident| { + Ok(match ident.name { + sym::field_of => Some(this.parse_ty_field_of(lo)?), + _ => None, + }) + }) + } + + /// Built-in macro for `field_of!` expressions. + pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { + let container = self.parse_ty()?; + self.expect(exp!(Comma))?; + + let fields = self.parse_floating_field_access()?; + let trailing_comma = self.eat_noexpect(&TokenKind::Comma); + + if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { + if trailing_comma { + e.note("unexpected third argument to field_of"); + } else { + e.note("field_of expects dot-separated field and variant names"); + } + e.emit(); + } + + // Eat tokens until the macro call ends. + if self.may_recover() { + while !self.token.kind.is_close_delim_or_eof() { + self.bump(); + } + } + + Ok(TyKind::FieldOf(container, fields)) + } + /// Parses a function pointer type (`TyKind::FnPtr`). /// ```ignore (illustrative) /// [unsafe] [extern "ABI"] fn (S) -> T diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index fee920221e1d1..c49409a5f16fb 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -292,6 +292,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool | ty::Char | ty::Error(_) => {} ty::Array(_, _) + | ty::Field(..) | ty::Ref(_, _, _) | ty::Param(_) | ty::Closure(_, _) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 3c2c9683a4d19..53596abe224c4 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -289,15 +289,18 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) { 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"); + let &Ok((container, ref indices)) = + data.get(expr.hir_id).expect("no offset_of_data for offset_of") + else { + return; + }; let body_did = self.typeck_results().hir_owner.to_def_id(); let typing_env = ty::TypingEnv::non_body_analysis(self.tcx, body_did); let mut current_ty = container; - for &(variant, field) in indices { + for (variant, field) in indices { match current_ty.kind() { ty::Adt(def, args) => { let field = &def.variant(variant).fields[field]; diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index c83610da1aad2..6c4bf5310cc14 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -408,6 +408,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { OpaqueDef, TraitAscription, TraitObject, + FieldOf, Typeof, Infer, Pat, @@ -683,6 +684,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { TraitObject, ImplTrait, Paren, + FieldOf, Typeof, Infer, ImplicitSelf, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 0652461e97501..e3692d00c75ba 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -414,6 +414,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::UnsafeBinder(_) | ty::Alias(_, _) | ty::Param(_) + | ty::Field(..) | ty::Error(_) => ConstructorSet::Unlistable, ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => { bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}") diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index c9dbb204f61f7..8dfc0b81a42ec 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -273,6 +273,7 @@ where // These types don't have their own def-ids (but may have subcomponents // with def-ids that should be visited recursively). ty::Bool + | ty::Field(..) | ty::Char | ty::Int(..) | ty::Uint(..) diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 7b0882caf1b3e..ebc983e2041b4 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -8,7 +8,7 @@ use crate::compiler_interface::with; use crate::mir::FieldIdx; use crate::target::{MachineInfo, MachineSize as Size}; use crate::ty::{Align, Ty, VariantIdx}; -use crate::{Error, Opaque, error}; +use crate::{Error, IndexedVal, Opaque, error}; /// A function ABI definition. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] @@ -159,10 +159,13 @@ impl FieldsShape { pub fn fields_by_offset_order(&self) -> Vec { match self { FieldsShape::Primitive => vec![], - FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(), + FieldsShape::Union(_) | FieldsShape::Array { .. } => { + (0..self.count()).map(IndexedVal::to_val).collect() + } FieldsShape::Arbitrary { offsets, .. } => { - let mut indices = (0..offsets.len()).collect::>(); - indices.sort_by_key(|idx| offsets[*idx]); + let mut indices: Vec = + (0..offsets.len()).map(IndexedVal::to_val).collect::>(); + indices.sort_by_key(|idx| offsets[idx.to_index()]); indices } } @@ -195,7 +198,7 @@ pub enum VariantsShape { Multiple { tag: Scalar, tag_encoding: TagEncoding, - tag_field: usize, + tag_field: FieldIdx, variants: Vec, }, } diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 7bd06fee721ce..9991a766e8890 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -863,7 +863,24 @@ pub const RETURN_LOCAL: Local = 0; /// `b`'s `FieldIdx` is `1`, /// `c`'s `FieldIdx` is `0`, and /// `g`'s `FieldIdx` is `2`. -pub type FieldIdx = usize; +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct FieldIdx(pub usize); + +impl crate::IndexedVal for FieldIdx { + fn to_val(index: usize) -> Self { + FieldIdx(index) + } + + fn to_index(&self) -> usize { + self.0 + } +} + +impl From for rustc_abi::FieldIdx { + fn from(value: FieldIdx) -> Self { + value.0.into() + } +} type UserTypeAnnotationIndex = usize; diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 0afb94c18d7b9..b0ca878913aaf 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -8,6 +8,7 @@ use super::mir::{Body, Mutability, Safety}; use super::{DefId, Error, Symbol, with}; use crate::abi::{FnAbi, Layout}; use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType}; +use crate::mir::FieldIdx; use crate::mir::alloc::{AllocId, read_target_int, read_target_uint}; use crate::mir::mono::StaticDef; use crate::target::MachineInfo; @@ -550,6 +551,7 @@ pub enum RigidTy { Uint(UintTy), Float(FloatTy), Adt(AdtDef, GenericArgs), + Field(Ty, FieldPath), Foreign(ForeignDef), Str, Array(Ty, TyConst), @@ -1053,6 +1055,9 @@ impl GenericArgKind { } } +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub struct FieldPath(pub Vec<(VariantIdx, FieldIdx)>); + #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum TermKind { Type(Ty), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 064fb6c6803a5..478bb2bceb059 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -12,10 +12,10 @@ use crate::abi::Layout; use crate::compiler_interface::BridgeTys; use crate::mir::alloc::AllocId; use crate::mir::mono::{Instance, MonoItem, StaticDef}; -use crate::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; +use crate::mir::{BinOp, FieldIdx, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use crate::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, - ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, + ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FieldPath, FloatTy, FnSig, GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx, }; @@ -82,6 +82,21 @@ impl RustcInternal for GenericArgKind { } } +impl RustcInternal for FieldPath { + type T<'tcx> = rustc_ty::FieldPath<'tcx>; + fn internal<'tcx>( + &self, + tables: &mut Tables<'_, BridgeTys>, + tcx: impl InternalCx<'tcx>, + ) -> Self::T<'tcx> { + tcx.tcx().mk_field_path_from_iter( + self.0 + .iter() + .map(|(var, field)| (var.internal(tables, tcx), field.internal(tables, tcx))), + ) + } +} + impl RustcInternal for Region { type T<'tcx> = rustc_ty::Region<'tcx>; fn internal<'tcx>( @@ -156,6 +171,9 @@ impl RustcInternal for RigidTy { RigidTy::Adt(def, args) => { rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx)) } + RigidTy::Field(ty, field_path) => { + rustc_ty::TyKind::Field(ty.internal(tables, tcx), field_path.internal(tables, tcx)) + } RigidTy::Str => rustc_ty::TyKind::Str, RigidTy::Slice(ty) => rustc_ty::TyKind::Slice(ty.internal(tables, tcx)), RigidTy::RawPtr(ty, mutability) => { @@ -342,6 +360,18 @@ impl RustcInternal for VariantDef { } } +impl RustcInternal for FieldIdx { + type T<'tcx> = rustc_abi::FieldIdx; + + fn internal<'tcx>( + &self, + _tables: &mut Tables<'_, BridgeTys>, + _tcx: impl InternalCx<'tcx>, + ) -> Self::T<'tcx> { + rustc_abi::FieldIdx::from(self.to_index()) + } +} + impl RustcInternal for MirConst { type T<'tcx> = rustc_middle::mir::Const<'tcx>; fn internal<'tcx>( diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 62ab91d17baee..d50fe02faeebe 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -7,10 +7,10 @@ use rustc_public_bridge::{Tables, bridge}; use crate::compiler_interface::BridgeTys; use crate::mir::alloc::GlobalAlloc; -use crate::mir::{ConstOperand, Statement, UserTypeProjection, VarDebugInfoFragment}; +use crate::mir::{ConstOperand, FieldIdx, Statement, UserTypeProjection, VarDebugInfoFragment}; use crate::ty::{Allocation, ConstantKind, MirConst}; use crate::unstable::Stable; -use crate::{Error, alloc, opaque}; +use crate::{Error, IndexedVal, alloc, opaque}; impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { type T = crate::mir::Body; @@ -641,7 +641,7 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { var_idx.stable(tables, cx), generic_arg.stable(tables, cx), user_ty_index.map(|idx| idx.index()), - field_idx.map(|idx| idx.index()), + field_idx.map(|idx| FieldIdx::to_val(idx.index())), ) } mir::AggregateKind::Closure(def_id, generic_arg) => crate::mir::AggregateKind::Closure( diff --git a/compiler/rustc_public/src/unstable/convert/stable/mod.rs b/compiler/rustc_public/src/unstable/convert/stable/mod.rs index add52fc18caaa..5e46f10ffe562 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mod.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mod.rs @@ -5,6 +5,7 @@ use rustc_public_bridge::Tables; use rustc_public_bridge::context::CompilerCtxt; use super::Stable; +use crate::IndexedVal; use crate::compiler_interface::BridgeTys; mod abi; @@ -22,9 +23,9 @@ impl<'tcx> Stable<'tcx> for rustc_hir::Safety { } impl<'tcx> Stable<'tcx> for FieldIdx { - type T = usize; + type T = crate::mir::FieldIdx; fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - self.as_usize() + crate::mir::FieldIdx::to_val(self.as_usize()) } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 59440e5407f40..8bbee28ec580f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -403,6 +403,10 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { tables.adt_def(adt_def.did()), generic_args.stable(tables, cx), )), + ty::Field(ty, field_path) => TyKind::RigidTy(RigidTy::Field( + ty.stable(tables, cx), + field_path.stable(tables, cx), + )), ty::Foreign(def_id) => TyKind::RigidTy(RigidTy::Foreign(tables.foreign_def(*def_id))), ty::Str => TyKind::RigidTy(RigidTy::Str), ty::Array(ty, constant) => { @@ -676,6 +680,22 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDef { } } +impl<'tcx> Stable<'tcx> for rustc_middle::ty::FieldPath<'tcx> { + type T = crate::ty::FieldPath; + + fn stable<'cx>( + &self, + tables: &mut Tables<'cx, BridgeTys>, + cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + crate::ty::FieldPath( + self.into_iter() + .map(|(var, field)| (var.stable(tables, cx), field.stable(tables, cx))) + .collect(), + ) + } +} + impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> { type T = crate::ty::PredicateKind; diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index acc3334769613..6408593c41ccd 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -164,6 +164,10 @@ impl Visitable for RigidTy { reg.visit(visitor)?; ty.visit(visitor) } + RigidTy::Field(ty, _field_path) => { + ty.visit(visitor) + // FIXME(field_projections): also visit _field_path? + } RigidTy::Adt(_, args) | RigidTy::Closure(_, args) | RigidTy::Coroutine(_, args) diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 621cc0fb3ef16..73a536e482649 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -505,6 +505,10 @@ pub(crate) fn encode_ty<'tcx>( typeid.push_str(&s); } + ty::Field(..) => todo!( + "FIXME(field_projections): no idea what to do here, probably not supported in ABI?" + ), + ty::Foreign(def_id) => { // , where is let mut s = String::new(); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 82a2a64f23078..f98e598e82a4a 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -183,6 +183,10 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { } } + ty::Field(..) => todo!( + "FIXME(field_projections): no idea what to do here, probably don't need to handle field types here?" + ), + ty::Ref(..) => { if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { if t.is_mutable_ptr() { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b34a64108e3b4..994e80b353510 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -233,6 +233,10 @@ symbols! { Equal, Err, Error, + Field, + FieldBase, + FieldOffset, + FieldType, File, FileType, FmtArgumentsNew, @@ -1038,6 +1042,9 @@ symbols! { ffi_returns_twice, field, field_init_shorthand, + field_of, + field_offset, + field_projections, file, file_options, flags, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 3896e06a627b7..57f1016cdeba9 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -62,6 +62,8 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { ty::Uint(uint_ty) => uint_ty.name_str().abi_hash(tcx, hasher), ty::Float(float_ty) => float_ty.name_str().abi_hash(tcx, hasher), + ty::Field(..) => todo!("FIXME(field_projections): no idea what to do here"), + ty::Adt(adt_def, args) => { adt_def.is_struct().abi_hash(tcx, hasher); adt_def.is_enum().abi_hash(tcx, hasher); diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index aeac40a65bdfc..12ca23d914061 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -525,6 +525,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { self.push("E"); } + ty::Field(..) => todo!("FIXME(field_projections): no idea what to do here"), // Mangle all nominal types as paths. ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), args) | ty::FnDef(def_id, args) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index bec1275072818..31c0b15cd0f94 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1764,6 +1764,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::CoroutineClosure(..) => Some(21), ty::Pat(..) => Some(22), ty::UnsafeBinder(..) => Some(23), + ty::Field(..) => Some(24), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index d694a092853af..44dd93eb911df 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -366,6 +366,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( | ty::FnDef(..) | ty::FnPtr(..) | ty::Never + | ty::Field(_, _) | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) | ty::Error(_) => thin_vec![], diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 4c25882daa92f..3b4c22638c2e0 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -60,6 +60,9 @@ pub fn type_allowed_to_implement_copy<'tcx>( | ty::Ref(_, _, hir::Mutability::Not) | ty::Array(..) => return Ok(()), + // Field types are uninhabited and thus user-impls of `Copy` are allowed. + ty::Field(..) => return Ok(()), + &ty::Adt(adt, args) => (adt, args), _ => return Err(CopyImplementationError::NotAnAdt), diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 042d6def84c33..b25cc8e18deaf 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -996,7 +996,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | LangItem::FnOnce | LangItem::AsyncFn | LangItem::AsyncFnMut - | LangItem::AsyncFnOnce, + | LangItem::AsyncFnOnce + | LangItem::Field, ) => true, Some(LangItem::AsyncFnKindHelper) => { // FIXME(async_closures): Validity constraints here could be cleaned up. @@ -1023,6 +1024,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Uint(_) | ty::Float(_) | ty::Adt(..) + | ty::Field(..) | ty::Foreign(_) | ty::Str | ty::Array(..) @@ -1098,6 +1100,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // If returned by `struct_tail` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) + // field traits are always sized + | ty::Field(..) // If returned by `struct_tail` this is the empty tuple. | ty::Tuple(..) // Integers and floats are always Sized, and so have unit type metadata. @@ -1551,6 +1555,17 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } }); (metadata_ty.into(), obligations) + } else if tcx.is_lang_item(trait_def_id, LangItem::Field) { + let &ty::Field(container, field_path) = self_ty.kind() else { + bug!("only `field_of!()` can implement `Field`") + }; + if tcx.is_lang_item(item_def_id, LangItem::FieldBase) { + (container.into(), PredicateObligations::new()) + } else if tcx.is_lang_item(item_def_id, LangItem::FieldType) { + (field_path.field_ty(tcx, container).into(), PredicateObligations::new()) + } else { + bug!("unexpected associated type {:?} in `Field`", obligation.predicate); + } } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 945ca7c37758f..f639325f22177 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -42,6 +42,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Str + | ty::Field(..) | ty::Foreign(..) | ty::Error(_) => true, @@ -278,6 +279,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( | ty::Foreign(..) | ty::RawPtr(..) | ty::Ref(..) + | ty::Field(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::CoroutineWitness(..) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 60f1fcb26c004..b480fb6c4ff78 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -128,6 +128,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut candidates, ); } + Some(LangItem::Field) => { + self.assemble_builtin_candidates_for_field(obligation, &mut candidates); + } _ => { // We re-match here for traits that can have both builtin impls and user written impls. // After the builtin impls we need to also add user written impls, which we do not want to @@ -683,6 +686,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) @@ -863,6 +867,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Pat(_, _) | ty::Slice(_) | ty::Adt(..) + | ty::Field(..) | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) @@ -1204,7 +1209,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // Fallback to whatever user-defined impls or param-env clauses exist in this case. - ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} + ty::Adt(..) | ty::Field(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} ty::Infer(ty::TyVar(_)) => { candidates.ambiguous = true; @@ -1243,6 +1248,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::Field(..) | ty::Error(_) => { candidates.vec.push(SizedCandidate); } @@ -1317,6 +1323,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) @@ -1357,6 +1364,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(..) + | ty::Field(..) | ty::Foreign(..) | ty::Str | ty::Array(..) @@ -1398,6 +1406,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match obligation.predicate.self_ty().skip_binder().kind() { ty::Ref(..) | ty::Adt(..) + | ty::Field(..) | ty::Tuple(_) | ty::Array(..) | ty::FnDef(..) @@ -1433,4 +1442,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_builtin_candidates_for_field( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match obligation.predicate.self_ty().skip_binder().kind() { + ty::Field(container, field_path) => { + if field_path + .walk(self.infcx.tcx, *container, |base, _, _, _| { + if let ty::Adt(def, _) = base.kind() + && def.repr().packed() + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_none() + { + // we only implement `Field` if all base types are not `repr(packed)` + candidates.vec.push(BuiltinCandidate) + } + } + + _ => {} + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7ad65a1df8e9b..d9d9af38afb3e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -282,7 +282,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | LangItem::FnPtrTrait | LangItem::PointeeTrait | LangItem::Tuple - | LangItem::Unpin, + | LangItem::Unpin + | LangItem::Field, ) => ty::Binder::dummy(vec![]), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), }; @@ -1298,6 +1299,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Slice(_) | ty::Foreign(..) | ty::Adt(..) + | ty::Field(..) | ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index fb4f28412d428..397bf16bf9224 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2119,6 +2119,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::Field(..) | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { @@ -2221,6 +2222,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Slice(_) | ty::Dynamic(..) | ty::Adt(..) + | ty::Field(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) @@ -2263,6 +2265,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never + | ty::Field(..) | ty::Char => { ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] }) } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 45cbb56b1c2ef..a22026ff978aa 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -737,6 +737,11 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // WfScalar, WfParameter, etc } + ty::Field(..) => { + // Only need to make sure that the container is well-formed, which is done by the + // recursive call below. + } + // Can only infer to `ty::Int(_) | ty::Uint(_)`. ty::Infer(ty::IntVar(_)) => {} diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index e28ebaabc0a1f..3fa54152a2e58 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -256,7 +256,23 @@ fn resolve_associated_item<'tcx>( } } traits::ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, _) => { - if tcx.is_lang_item(trait_ref.def_id, LangItem::Clone) { + if tcx.is_lang_item(trait_ref.def_id, LangItem::Field) { + if tcx.is_lang_item(trait_item_id, LangItem::FieldOffset) { + let self_ty = trait_ref.self_ty(); + match self_ty.kind() { + ty::Field(_, _) => {} + _ => bug!("expected field representing type, found {self_ty}"), + } + Some(Instance { + def: ty::InstanceKind::Item( + tcx.lang_items().get(LangItem::FieldOffset).unwrap(), + ), + args: rcvr_args, + }) + } else { + bug!("unexpected associated associated item") + } + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::Clone) { // FIXME(eddyb) use lang items for methods instead of names. let name = tcx.item_name(trait_item_id); if name == sym::clone { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 317d101dafe05..e470c2e374070 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -696,6 +696,8 @@ fn layout_of_uncached<'tcx>( tcx.mk_layout(layout) } + ty::Field(..) => tcx.mk_layout(LayoutData::never_type(cx)), + ty::UnsafeBinder(bound_ty) => { let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into()); cx.layout_of(ty)?.layout diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 0ef435b1a0e21..b411772c485f8 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -293,6 +293,7 @@ where | ty::Tuple(_) | ty::Bound(..) | ty::Never + | ty::Field(..) | ty::Infer(_) | ty::Error(_) => { bug!("unexpected type returned by `needs_drop_components`: {component}") diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index bb25a14ef7443..c434409e20524 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -38,6 +38,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) + | ty::Field(..) | ty::Never => None, ty::Str | ty::Slice(..) | ty::Dynamic(_, _) => match sizedness { @@ -374,6 +375,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index ed6416a7f55f2..d0132ad2d4d0c 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -28,6 +28,7 @@ pub enum SimplifiedType { Uint(ty::UintTy), Float(ty::FloatTy), Adt(DefId), + Field, Foreign(DefId), Str, Array, @@ -123,6 +124,7 @@ pub fn simplify_type( ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)), ty::Float(float_type) => Some(SimplifiedType::Float(float_type)), ty::Adt(def, _) => Some(SimplifiedType::Adt(def.def_id().into())), + ty::Field(..) => Some(SimplifiedType::Field), ty::Str => Some(SimplifiedType::Str), ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), @@ -292,6 +294,7 @@ impl false, }, + ty::Field(lhs_ty, lhs_field_path) => match rhs.kind() { + ty::Field(rhs_ty, rhs_field_path) => { + self.types_may_unify_inner(lhs_ty, rhs_ty, depth) + && lhs_field_path == rhs_field_path + } + _ => false, + }, + // Depending on the value of const generics, we either treat generic parameters // like placeholders or like inference variables. ty::Param(lhs) => { diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 34b030ee768b6..fe532a7ba3482 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -285,6 +285,10 @@ impl FlagComputation { self.add_args(args.as_slice()); } + ty::Field(ty, _) => { + self.add_kind(&ty.kind()); + } + ty::Alias(kind, data) => { self.add_flags(match kind { ty::Projection => TypeFlags::HAS_TY_PROJECTION, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 75ba0231d98cb..8c5de52a01846 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; use std::hash::Hash; +use std::ops::ControlFlow; use rustc_ast_ir::Mutability; @@ -76,6 +77,8 @@ pub trait Ty>: fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self; + fn new_field_type(interner: I, container: I::Ty, field_path: I::FieldPath) -> Self; + fn new_foreign(interner: I, def_id: I::ForeignId) -> Self; fn new_dynamic(interner: I, preds: I::BoundExistentialPredicates, region: I::Region) -> Self; @@ -171,6 +174,7 @@ pub trait Ty>: | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) @@ -629,6 +633,19 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_fundamental(self) -> bool; fn destructor(self, interner: I) -> Option; + + fn is_packed(self) -> bool; +} + +pub trait FieldPath: Copy + Debug + Hash + Eq { + fn walk( + self, + interner: I, + container: I::Ty, + walker: impl FnMut(I::Ty, I::Symbol, I::Ty, bool) -> ControlFlow, + ) -> Option; + + fn field_ty(self, interner: I, container: I::Ty) -> I::Ty; } pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 886d1a78bcbfd..6cc6c6378087f 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -145,6 +145,8 @@ pub trait Interner: type Safety: Safety; type Abi: Abi; + type FieldPath: FieldPath; + // Kinds of consts type Const: Const; type ParamConst: Copy + Debug + Hash + Eq + ParamLike; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 5f503d8b912e0..f4ad27ea59df5 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -9,6 +9,8 @@ pub enum SolverLangItem { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -36,6 +38,7 @@ pub enum SolverTraitLangItem { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index c7dccea6adc12..977f716ee8648 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -195,6 +195,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { } ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 09add529286d9..25c6129683054 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -410,6 +410,22 @@ pub fn structurally_relate_tys>( }) } + (ty::Field(lcontainer, lpath), ty::Field(rcontainer, rpath)) => { + let t = relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + lcontainer, + rcontainer, + )?; + + if lpath == rpath { + Ok(Ty::new_field_type(cx, t, lpath)) + } else { + // FIXME(field_projections): improve error + Err(TypeError::Mismatch) + } + } + (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)), (ty::Dynamic(a_obj, a_region), ty::Dynamic(b_obj, b_region)) => Ok(Ty::new_dynamic( diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index bb80e2cf46d45..3cb8f632a92b1 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -86,6 +86,9 @@ pub enum TyKind { /// by using something like `adt_def.all_fields().map(|field| field.ty(interner, args))`. Adt(I::AdtDef, I::GenericArgs), + /// Field representing type. + Field(I::Ty, I::FieldPath), + /// An unsized FFI type that is opaque to Rust. Written as `extern type T`. Foreign(I::ForeignId), @@ -290,6 +293,7 @@ impl TyKind { | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) + | ty::Field(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) @@ -342,6 +346,7 @@ impl fmt::Debug for TyKind { write!(f, ">") } + Field(ty, field_path) => write!(f, "{ty:?}.{field_path:?}"), Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), Array(t, c) => write!(f, "[{t:?}; {c:?}]"), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 6d51817a7bf44..d8982fca49c3d 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -139,6 +139,9 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg | ty::FnDef(_, args) => { stack.extend(args.iter().rev()); } + ty::Field(ty, _) => { + stack.push(ty.into()); + } ty::Tuple(ts) => stack.extend(ts.iter().rev().map(|ty| ty.into())), ty::FnPtr(sig_tys, _hdr) => { stack.extend( diff --git a/library/core/src/field.rs b/library/core/src/field.rs new file mode 100644 index 0000000000000..5852dde975fea --- /dev/null +++ b/library/core/src/field.rs @@ -0,0 +1,37 @@ +//! Field Reflection + +/// Type representing a field of a `struct` or tuple. +/// +/// # Safety +/// +/// Given a valid value of type `Self::Base`, there exists a valid value of type `Self::Type` at +/// byte offset `OFFSET`. +#[lang = "Field"] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] +pub unsafe trait Field: Sized { + /// The type of the base where this field exists in. + #[lang = "FieldBase"] + type Base; + + /// The type of the field. + #[lang = "FieldType"] + type Type; + + /// The offset of the field in bytes. + #[lang = "FieldOffset"] + const OFFSET: usize = crate::intrinsics::field_offset::(); +} + +/// Expands to the field representing type of the given field. +/// +/// The container type may be a `struct` or a tuple. +/// +/// The field may be a nested field (`field1.field2`), but not an array index. +/// The field must be visible to the call site. +#[unstable(feature = "field_projections", issue = "145383")] +#[allow_internal_unstable(builtin_syntax)] +pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { + builtin # field_of($Container, $($fields)+) +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index cef700be9ea1f..5a9b28a9b71b6 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3314,3 +3314,16 @@ pub unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; #[rustc_intrinsic] #[rustc_nounwind] pub unsafe fn va_end(ap: &mut VaListImpl<'_>); + +/// `offset_of!` for field representing types. +/// +/// Returns the offset of the field represented by `F`. +/// +/// Expects the generic `F` to be a field representing type expressed using +/// [`field_of!`](core::field::field_of). +/// +/// This is a `const`-only intrinsic that doesn't have any codegen. +#[rustc_intrinsic] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_const_unstable(feature = "field_projections", issue = "145383")] +pub const fn field_offset() -> usize; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 54adf97f10020..4206dfa2c67d4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -153,6 +153,7 @@ #![feature(extern_types)] #![feature(f16)] #![feature(f128)] +#![feature(field_projections)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] @@ -307,6 +308,8 @@ pub mod bstr; pub mod cell; pub mod char; pub mod ffi; +#[unstable(feature = "field_projections", issue = "145383")] +pub mod field; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub mod io; pub mod iter; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index da41c1216c4d5..082056c6a8e2f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -503,6 +503,8 @@ pub use core::cmp; pub use core::convert; #[stable(feature = "rust1", since = "1.0.0")] pub use core::default; +#[unstable(feature = "field_projections", issue = "145383")] +pub use core::field; #[stable(feature = "futures_api", since = "1.36.0")] pub use core::future; #[stable(feature = "core_hint", since = "1.27.0")] diff --git a/library/std/tests/field-projections-basic.rs b/library/std/tests/field-projections-basic.rs new file mode 100644 index 0000000000000..31c8a617c179d --- /dev/null +++ b/library/std/tests/field-projections-basic.rs @@ -0,0 +1,40 @@ +//@ revisions: old next +//@ [next]: compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; +use std::mem::offset_of; +use std::ptr; + +pub struct Foo { + pub x: usize, + pub y: usize, + pub z: (usize, usize), +} + +/// # Safety +/// +/// All container types of `F` must be structs or tuples. +pub unsafe fn project_ref(r: &F::Base) -> &F::Type +where + F::Type: Sized, +{ + unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } +} + +#[test] +fn foo() { + let foo = Foo { x: 42, y: 24, z: (43, 44) }; + let x = unsafe { project_ref::(&foo) }; + let y = unsafe { project_ref::(&foo) }; + let z0 = unsafe { project_ref::(&foo) }; + let z1 = unsafe { project_ref::(&foo) }; + assert_eq!(*x, 42); + assert_eq!(*y, 24); + assert_eq!(::OFFSET, offset_of!(Foo, x)); + assert_eq!(*z0, 43); + assert_eq!(*z1, 44); +} + +fn main() {} diff --git a/library/std/tests/field-projections-clone-copy.rs b/library/std/tests/field-projections-clone-copy.rs new file mode 100644 index 0000000000000..56193258503bb --- /dev/null +++ b/library/std/tests/field-projections-clone-copy.rs @@ -0,0 +1,22 @@ +//@ revisions: old next +//@ [next]: compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +struct MyStruct(()); + +impl Clone for field_of!(MyStruct, 0) { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for field_of!(MyStruct, 0) {} + +fn assert_copy() {} + +fn main() { + assert_copy::(); +} diff --git a/library/std/tests/field-projections-cyclic.rs b/library/std/tests/field-projections-cyclic.rs new file mode 100644 index 0000000000000..5fedd541d5aa0 --- /dev/null +++ b/library/std/tests/field-projections-cyclic.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next]: compile-flags: -Znext-solver +#![allow(incomplete_features, dead_code)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; +use std::marker::PhantomData; + +struct Work { + _phantom: PhantomData, +} + +struct MyMultiWork { + a: Work, + b: Work, +} + +fn main() {} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c6339dd475593..82d1071439f3a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1831,6 +1831,9 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T TyKind::UnsafeBinder(unsafe_binder_ty) => { UnsafeBinder(Box::new(clean_unsafe_binder_ty(unsafe_binder_ty, cx))) } + TyKind::FieldOf(container, field_path) => Field(Box::new(clean_ty(container, cx)), { + field_path.iter().map(|field| field.as_str()).intersperse(".").collect() + }), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. TyKind::Infer(()) | TyKind::Err(_) @@ -2074,6 +2077,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let path = clean_middle_path(cx, did, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } + ty::Field(..) => todo!("FIXME(field_projections): no idea what to do here"), ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); let path = clean_middle_path( diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6e3dfa0ac221c..574a7ed8ec1a8 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -31,7 +31,7 @@ use {rustc_ast as ast, rustc_hir as hir}; pub(crate) use self::ItemKind::*; pub(crate) use self::Type::{ - Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, + Array, BareFunction, BorrowedRef, DynTrait, Field, Generic, ImplTrait, Infer, Primitive, QPath, RawPointer, SelfTy, Slice, Tuple, UnsafeBinder, }; use crate::clean::cfg::Cfg; @@ -1618,6 +1618,9 @@ pub(crate) enum Type { ImplTrait(Vec), UnsafeBinder(Box), + + /// Field representing type `field_of!(container, field_path)` + Field(Box, Box), } impl Type { @@ -1814,7 +1817,9 @@ impl Type { Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), - Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, + Field(..) | Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => { + return None; + } }; Primitive(t).def_id(cache) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 856e637a4587b..9923f7989329c 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -990,6 +990,13 @@ fn fmt_type( print_generic_bounds(bounds, cx).fmt(f) } clean::QPath(qpath) => qpath.print(cx).fmt(f), + clean::Field(container, field_path) => { + f.write_str("field_of!(")?; + fmt_type(&container, f, use_absolute, cx)?; + f.write_str(", ")?; + f.write_str(field_path)?; + f.write_str(")") + } } } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 7b21c68d2e949..d9784c3399fae 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2077,6 +2077,7 @@ fn get_index_type_id( } // Not supported yet clean::Type::Pat(..) + | clean::Field(..) | clean::Generic(_) | clean::SelfTy | clean::ImplTrait(_) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 779e26c7b0f7b..58d6e5efb7baa 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -554,7 +554,7 @@ impl FromClean for TraitBoundModifier { impl FromClean for Type { fn from_clean(ty: &clean::Type, renderer: &JsonRenderer<'_>) -> Self { use clean::Type::{ - Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath, + Array, BareFunction, BorrowedRef, Field, Generic, ImplTrait, Infer, Primitive, QPath, RawPointer, SelfTy, Slice, Tuple, UnsafeBinder, }; @@ -592,6 +592,8 @@ impl FromClean for Type { QPath(qpath) => qpath.into_json(renderer), // FIXME(unsafe_binder): Implement rustdoc-json. UnsafeBinder(_) => todo!(), + // FIXME(field_projections): Implement + Field(..) => todo!(), } } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 79d74c3c4eb90..19dfc76b5ac77 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -565,6 +565,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => { Res::from_def_id(self.cx.tcx, did) } + ty::Field(..) => bug!("FIXME(field_projections): what to do here?"), ty::Alias(..) | ty::Closure(..) | ty::CoroutineClosure(..) diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index a70105db19490..fb17b0f52eabd 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -817,6 +817,7 @@ impl TyCoercionStability { | TyKind::FnPtr(_) | TyKind::Pat(..) | TyKind::Never + | TyKind::FieldOf(..) | TyKind::Tup(_) | TyKind::Path(_) => Self::Deref, TyKind::OpaqueDef(..) @@ -875,6 +876,7 @@ impl TyCoercionStability { | ty::Str | ty::Slice(..) | ty::Adt(..) + | ty::Field(..) | ty::Foreign(_) | ty::FnDef(..) | ty::Coroutine(..) diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 948a7203402d5..eb07bc0e5144d 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -523,6 +523,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }; (start, Pat::Str("")) }, + TyKind::FieldOf(..) => (Pat::Str("field_of!"), Pat::Str("")), // implicit, so has no contents to match against TyKind::ImplicitSelf diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index b79e15cd7170b..634394fb27b65 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1306,6 +1306,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::TraitObject(_, lifetime) => { self.hash_lifetime(lifetime); }, + TyKind::FieldOf(container, fields) => { + self.hash_ty(container); + for field in *fields { + self.hash_name(field.name); + } + }, TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); }, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 5dce9a0c8d0f2..7c42e33537db5 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1031,6 +1031,8 @@ impl Rewrite for ast::Ty { } ast::TyKind::CVarArgs => Ok("...".to_owned()), ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), + // Builtin type expression, cannot be written by users. + ast::TyKind::FieldOf(..) => Err(RewriteError::Unknown), ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", diff --git a/tests/codegen-llvm/naked-fn/generics.rs b/tests/codegen-llvm/naked-fn/generics.rs index 865be00d91ee4..b1e33ac348c6b 100644 --- a/tests/codegen-llvm/naked-fn/generics.rs +++ b/tests/codegen-llvm/naked-fn/generics.rs @@ -20,11 +20,11 @@ fn test(x: u64) { } // CHECK: .balign 4 -// CHECK: add rax, 2 +// CHECK: add rax, 1 // CHECK: add rax, 42 // CHECK: .balign 4 -// CHECK: add rax, 1 +// CHECK: add rax, 2 // CHECK: add rax, 42 #[unsafe(naked)] diff --git a/tests/debuginfo/function-names.rs b/tests/debuginfo/function-names.rs index b5aec5c595e73..c6737f931a0c9 100644 --- a/tests/debuginfo/function-names.rs +++ b/tests/debuginfo/function-names.rs @@ -38,7 +38,7 @@ // Const generic parameter // gdb-command:info functions -q function_names::const_generic_fn.* // gdb-check:[...]static fn function_names::const_generic_fn_bool(); -// gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#ffa3db4ca1d52dce}>(); +// gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#5177fe61e1757625}>(); // gdb-check:[...]static fn function_names::const_generic_fn_signed_int<-7>(); // gdb-check:[...]static fn function_names::const_generic_fn_unsigned_int<14>(); diff --git a/tests/mir-opt/const_prop/offset_of.concrete.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.concrete.GVN.panic-unwind.diff index 77a2c5bcccc72..15fd294bedc08 100644 --- a/tests/mir-opt/const_prop/offset_of.concrete.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/offset_of.concrete.GVN.panic-unwind.diff @@ -34,25 +34,25 @@ bb0: { StorageLive(_1); -- _1 = OffsetOf(Alpha, [(0, 0)]); +- _1 = OffsetOf(Alpha, FieldPath([(0, 0)])); + _1 = const 4_usize; StorageLive(_2); -- _2 = OffsetOf(Alpha, [(0, 1)]); +- _2 = OffsetOf(Alpha, FieldPath([(0, 1)])); + _2 = const 0_usize; StorageLive(_3); -- _3 = OffsetOf(Alpha, [(0, 2), (0, 0)]); +- _3 = OffsetOf(Alpha, FieldPath([(0, 2), (0, 0)])); + _3 = const 2_usize; StorageLive(_4); -- _4 = OffsetOf(Alpha, [(0, 2), (0, 1)]); +- _4 = OffsetOf(Alpha, FieldPath([(0, 2), (0, 1)])); + _4 = const 3_usize; StorageLive(_5); -- _5 = OffsetOf(Epsilon, [(0, 0)]); +- _5 = OffsetOf(Epsilon, FieldPath([(0, 0)])); + _5 = const 1_usize; StorageLive(_6); -- _6 = OffsetOf(Epsilon, [(0, 1)]); +- _6 = OffsetOf(Epsilon, FieldPath([(0, 1)])); + _6 = const 2_usize; StorageLive(_7); -- _7 = OffsetOf(Epsilon, [(2, 0)]); +- _7 = OffsetOf(Epsilon, FieldPath([(2, 0)])); + _7 = const 4_usize; _0 = const (); StorageDead(_7); diff --git a/tests/mir-opt/const_prop/offset_of.generic.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.generic.GVN.panic-unwind.diff index 130c31eff8ccc..15d84bfb574b7 100644 --- a/tests/mir-opt/const_prop/offset_of.generic.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/offset_of.generic.GVN.panic-unwind.diff @@ -34,21 +34,21 @@ bb0: { StorageLive(_1); - _1 = OffsetOf(Gamma, [(0, 0)]); + _1 = OffsetOf(Gamma, FieldPath([(0, 0)])); StorageLive(_2); - _2 = OffsetOf(Gamma, [(0, 1)]); + _2 = OffsetOf(Gamma, FieldPath([(0, 1)])); StorageLive(_3); -- _3 = OffsetOf(Delta, [(0, 1)]); +- _3 = OffsetOf(Delta, FieldPath([(0, 1)])); + _3 = const 0_usize; StorageLive(_4); -- _4 = OffsetOf(Delta, [(0, 2)]); +- _4 = OffsetOf(Delta, FieldPath([(0, 2)])); + _4 = const 2_usize; StorageLive(_5); - _5 = OffsetOf(Zeta, [(0, 0)]); + _5 = OffsetOf(Zeta, FieldPath([(0, 0)])); StorageLive(_6); - _6 = OffsetOf(Zeta, [(0, 1)]); + _6 = OffsetOf(Zeta, FieldPath([(0, 1)])); StorageLive(_7); - _7 = OffsetOf(Zeta, [(1, 0)]); + _7 = OffsetOf(Zeta, FieldPath([(1, 0)])); _0 = const (); StorageDead(_7); StorageDead(_6); 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 92691d0f80703..e3a33593f53d0 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 @@ -22,16 +22,16 @@ bb0: { StorageLive(_1); -- _1 = OffsetOf(Alpha, [(0, 0)]); +- _1 = OffsetOf(Alpha, FieldPath([(0, 0)])); + _1 = const 4_usize; StorageLive(_2); -- _2 = OffsetOf(Alpha, [(0, 1)]); +- _2 = OffsetOf(Alpha, FieldPath([(0, 1)])); + _2 = const 0_usize; StorageLive(_3); -- _3 = OffsetOf(Alpha, [(0, 2), (0, 0)]); +- _3 = OffsetOf(Alpha, FieldPath([(0, 2), (0, 0)])); + _3 = const 2_usize; StorageLive(_4); -- _4 = OffsetOf(Alpha, [(0, 2), (0, 1)]); +- _4 = OffsetOf(Alpha, FieldPath([(0, 2), (0, 1)])); + _4 = const 3_usize; _0 = const (); StorageDead(_4); 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 c6908166defb8..1d695a88b558c 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 @@ -22,14 +22,14 @@ bb0: { StorageLive(_1); - _1 = OffsetOf(Gamma, [(0, 0)]); + _1 = OffsetOf(Gamma, FieldPath([(0, 0)])); StorageLive(_2); - _2 = OffsetOf(Gamma, [(0, 1)]); + _2 = OffsetOf(Gamma, FieldPath([(0, 1)])); StorageLive(_3); -- _3 = OffsetOf(Delta, [(0, 1)]); +- _3 = OffsetOf(Delta, FieldPath([(0, 1)])); + _3 = const 0_usize; StorageLive(_4); -- _4 = OffsetOf(Delta, [(0, 2)]); +- _4 = OffsetOf(Delta, FieldPath([(0, 2)])); + _4 = const 2_usize; _0 = const (); StorageDead(_4); diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.rs b/tests/mir-opt/dataflow-const-prop/offset_of.rs index bb4a74d3712a0..d2380560135c4 100644 --- a/tests/mir-opt/dataflow-const-prop/offset_of.rs +++ b/tests/mir-opt/dataflow-const-prop/offset_of.rs @@ -56,10 +56,10 @@ fn generic() { // CHECK: debug dx => [[dx:_.*]]; // CHECK: debug dy => [[dy:_.*]]; - // CHECK: [[gx]] = OffsetOf(Gamma, [(0, 0)]); + // CHECK: [[gx]] = OffsetOf(Gamma, FieldPath([(0, 0)])); let gx = offset_of!(Gamma, x); - // CHECK: [[gy]] = OffsetOf(Gamma, [(0, 1)]); + // CHECK: [[gy]] = OffsetOf(Gamma, FieldPath([(0, 1)])); let gy = offset_of!(Gamma, y); // CHECK: [[dx]] = const 0_usize diff --git a/tests/ui-fulldeps/rustc_public/check_ty_fold.rs b/tests/ui-fulldeps/rustc_public/check_ty_fold.rs index 93cd304934409..009ea6ccd5b24 100644 --- a/tests/ui-fulldeps/rustc_public/check_ty_fold.rs +++ b/tests/ui-fulldeps/rustc_public/check_ty_fold.rs @@ -49,7 +49,7 @@ fn check_tys(local_ty: Ty, idx: FieldIdx, expected_ty: Ty) { let TyKind::RigidTy(RigidTy::Adt(def, args)) = local_ty.kind() else { unreachable!() }; assert_eq!(def.ty_with_args(&args), local_ty); - let field_def = &def.variants_iter().next().unwrap().fields()[idx]; + let field_def = &def.variants_iter().next().unwrap().fields()[idx.0]; let field_ty = field_def.ty_with_args(&args); assert_eq!(field_ty, expected_ty); diff --git a/tests/ui-fulldeps/rustc_public/projections.rs b/tests/ui-fulldeps/rustc_public/projections.rs index e0213b4253c7f..fdee85a52f4ec 100644 --- a/tests/ui-fulldeps/rustc_public/projections.rs +++ b/tests/ui-fulldeps/rustc_public/projections.rs @@ -19,7 +19,7 @@ extern crate rustc_public; use rustc_public::ItemKind; use rustc_public::crate_def::CrateDef; -use rustc_public::mir::{ProjectionElem, Rvalue, StatementKind}; +use rustc_public::mir::{FieldIdx, ProjectionElem, Rvalue, StatementKind}; use rustc_public::ty::{RigidTy, TyKind, UintTy}; use std::assert_matches::assert_matches; use std::io::Write; @@ -45,7 +45,7 @@ fn test_place_projections() -> ControlFlow<()> { assert!(local_proj.is_empty()); match &r_proj[..] { // Similarly we can't match against a type, only against its kind. - [ProjectionElem::Deref, ProjectionElem::Field(2, ty)] => { + [ProjectionElem::Deref, ProjectionElem::Field(FieldIdx(2), ty)] => { assert_matches!( ty.kind(), TyKind::RigidTy(RigidTy::Uint(rustc_public::ty::UintTy::U8)) diff --git a/tests/ui/feature-gates/feature-gate-field-projections.rs b/tests/ui/feature-gates/feature-gate-field-projections.rs new file mode 100644 index 0000000000000..5fa6b1ea30810 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-projections.rs @@ -0,0 +1,21 @@ +#![allow(dead_code)] + +use std::field::{Field, field_of}; //~ ERROR: use of unstable library feature `field_projections` [E0658] +//~^ ERROR: use of unstable library feature `field_projections` [E0658] +use std::ptr; + +fn project_ref( + //~^ ERROR: use of unstable library feature `field_projections` [E0658] + r: &F::Base, //~ ERROR: use of unstable library feature `field_projections` [E0658] +) -> &F::Type +//~^ ERROR: use of unstable library feature `field_projections` [E0658] +where + F::Type: Sized, //~ ERROR: use of unstable library feature `field_projections` [E0658] +{ + unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } //~ ERROR: use of unstable library feature `field_projections` [E0658] +} + +fn main() { + struct Foo(()); + let _ = project_ref::(&Foo(())); //~ ERROR: use of unstable library feature `field_projections` [E0658] +} diff --git a/tests/ui/feature-gates/feature-gate-field-projections.stderr b/tests/ui/feature-gates/feature-gate-field-projections.stderr new file mode 100644 index 0000000000000..01074d49fa862 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-projections.stderr @@ -0,0 +1,83 @@ +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:20:27 + | +LL | let _ = project_ref::(&Foo(())); + | ^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:3:18 + | +LL | use std::field::{Field, field_of}; + | ^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:3:25 + | +LL | use std::field::{Field, field_of}; + | ^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:7:19 + | +LL | fn project_ref( + | ^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:13:5 + | +LL | F::Type: Sized, + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:9:9 + | +LL | r: &F::Base, + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:10:7 + | +LL | ) -> &F::Type + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:15:42 + | +LL | unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } + | ^^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/field_projections/auxiliary/extern-crate.rs b/tests/ui/field_projections/auxiliary/extern-crate.rs new file mode 100644 index 0000000000000..8e7e6d2679b44 --- /dev/null +++ b/tests/ui/field_projections/auxiliary/extern-crate.rs @@ -0,0 +1,6 @@ +pub struct Point { + pub x: usize, + pub y: usize, +} + +pub trait ForeignTrait {} diff --git a/tests/ui/field_projections/deny-drop-impl.next.stderr b/tests/ui/field_projections/deny-drop-impl.next.stderr new file mode 100644 index 0000000000000..e7461b1193de1 --- /dev/null +++ b/tests/ui/field_projections/deny-drop-impl.next.stderr @@ -0,0 +1,11 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/deny-drop-impl.rs:10:15 + | +LL | impl Drop for field_of!(MyStruct, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^ must be a struct, enum, or union in the current crate + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0120`. diff --git a/tests/ui/field_projections/deny-drop-impl.old.stderr b/tests/ui/field_projections/deny-drop-impl.old.stderr new file mode 100644 index 0000000000000..e7461b1193de1 --- /dev/null +++ b/tests/ui/field_projections/deny-drop-impl.old.stderr @@ -0,0 +1,11 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/deny-drop-impl.rs:10:15 + | +LL | impl Drop for field_of!(MyStruct, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^ must be a struct, enum, or union in the current crate + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0120`. diff --git a/tests/ui/field_projections/deny-drop-impl.rs b/tests/ui/field_projections/deny-drop-impl.rs new file mode 100644 index 0000000000000..a24b8cf912957 --- /dev/null +++ b/tests/ui/field_projections/deny-drop-impl.rs @@ -0,0 +1,15 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub struct MyStruct(()); + +impl Drop for field_of!(MyStruct, 0) { + //~^ ERROR: the `Drop` trait may only be implemented for local structs, enums, and unions [E0120] + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/field_projections/deny-manual-impl.next.stderr b/tests/ui/field_projections/deny-manual-impl.next.stderr new file mode 100644 index 0000000000000..a10c403667804 --- /dev/null +++ b/tests/ui/field_projections/deny-manual-impl.next.stderr @@ -0,0 +1,9 @@ +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/deny-manual-impl.rs:11:1 + | +LL | unsafe impl Field for MyStruct { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0322`. diff --git a/tests/ui/field_projections/deny-manual-impl.old.stderr b/tests/ui/field_projections/deny-manual-impl.old.stderr new file mode 100644 index 0000000000000..a10c403667804 --- /dev/null +++ b/tests/ui/field_projections/deny-manual-impl.old.stderr @@ -0,0 +1,9 @@ +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/deny-manual-impl.rs:11:1 + | +LL | unsafe impl Field for MyStruct { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0322`. diff --git a/tests/ui/field_projections/deny-manual-impl.rs b/tests/ui/field_projections/deny-manual-impl.rs new file mode 100644 index 0000000000000..16d82c1fab0c2 --- /dev/null +++ b/tests/ui/field_projections/deny-manual-impl.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::Field; + +#[repr(packed)] +pub struct MyStruct(usize); + +unsafe impl Field for MyStruct { + //~^ ERROR: explicit impls for the `Field` trait are not permitted [E0322] + type Base = (); + type Type = (); + const OFFSET: usize = 0; +} + +fn main() {} diff --git a/tests/ui/field_projections/deny-private-field.next.stderr b/tests/ui/field_projections/deny-private-field.next.stderr new file mode 100644 index 0000000000000..29e52d8aec9e8 --- /dev/null +++ b/tests/ui/field_projections/deny-private-field.next.stderr @@ -0,0 +1,39 @@ +error[E0616]: field `a` of struct `A` is private + --> $DIR/deny-private-field.rs:45:25 + | +LL | let _: field_of!(A, a); + | ^ private field + +error[E0616]: field `b` of struct `B` is private + --> $DIR/deny-private-field.rs:47:27 + | +LL | let _: field_of!(A, b.b); + | ^ private field + +error[E0616]: field `c` of struct `C` is private + --> $DIR/deny-private-field.rs:49:29 + | +LL | let _: field_of!(A, b.c.c); + | ^ private field + +error[E0616]: field `d` of struct `D` is private + --> $DIR/deny-private-field.rs:51:31 + | +LL | let _: field_of!(A, b.c.d.d); + | ^ private field + +error[E0616]: field `e` of struct `E` is private + --> $DIR/deny-private-field.rs:53:33 + | +LL | let _: field_of!(A, b.c.d.e.e); + | ^ private field + +error[E0616]: field `f` of struct `F` is private + --> $DIR/deny-private-field.rs:55:35 + | +LL | let _: field_of!(A, b.c.d.e.f.f); + | ^ private field + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_projections/deny-private-field.old.stderr b/tests/ui/field_projections/deny-private-field.old.stderr new file mode 100644 index 0000000000000..29e52d8aec9e8 --- /dev/null +++ b/tests/ui/field_projections/deny-private-field.old.stderr @@ -0,0 +1,39 @@ +error[E0616]: field `a` of struct `A` is private + --> $DIR/deny-private-field.rs:45:25 + | +LL | let _: field_of!(A, a); + | ^ private field + +error[E0616]: field `b` of struct `B` is private + --> $DIR/deny-private-field.rs:47:27 + | +LL | let _: field_of!(A, b.b); + | ^ private field + +error[E0616]: field `c` of struct `C` is private + --> $DIR/deny-private-field.rs:49:29 + | +LL | let _: field_of!(A, b.c.c); + | ^ private field + +error[E0616]: field `d` of struct `D` is private + --> $DIR/deny-private-field.rs:51:31 + | +LL | let _: field_of!(A, b.c.d.d); + | ^ private field + +error[E0616]: field `e` of struct `E` is private + --> $DIR/deny-private-field.rs:53:33 + | +LL | let _: field_of!(A, b.c.d.e.e); + | ^ private field + +error[E0616]: field `f` of struct `F` is private + --> $DIR/deny-private-field.rs:55:35 + | +LL | let _: field_of!(A, b.c.d.e.f.f); + | ^ private field + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_projections/deny-private-field.rs b/tests/ui/field_projections/deny-private-field.rs new file mode 100644 index 0000000000000..f67cb509e0a2d --- /dev/null +++ b/tests/ui/field_projections/deny-private-field.rs @@ -0,0 +1,57 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +mod foo { + pub struct A { + a: usize, + pub b: B, + } + + pub struct B { + b: usize, + pub(crate) c: C, + } + + pub struct C { + c: usize, + pub(super) d: bar::D, + } + + pub mod bar { + pub struct D { + d: usize, + pub e: E, + } + + pub struct E { + pub(super) e: usize, + pub(crate) f: F, + } + + pub struct F { + f: usize, + pub(in super::super) g: (), + } + } +} + +fn main() { + use foo::A; + + let _: field_of!(A, a); //~ ERROR: field `a` of struct `A` is private [E0616] + let _: field_of!(A, b); + let _: field_of!(A, b.b); //~ ERROR: field `b` of struct `B` is private [E0616] + let _: field_of!(A, b.c); + let _: field_of!(A, b.c.c); //~ ERROR: field `c` of struct `C` is private [E0616] + let _: field_of!(A, b.c.d); + let _: field_of!(A, b.c.d.d); //~ ERROR: field `d` of struct `D` is private [E0616] + let _: field_of!(A, b.c.d.e); + let _: field_of!(A, b.c.d.e.e); //~ ERROR: field `e` of struct `E` is private [E0616] + let _: field_of!(A, b.c.d.e.f); + let _: field_of!(A, b.c.d.e.f.f); //~ ERROR: field `f` of struct `F` is private [E0616] + let _: field_of!(A, b.c.d.e.f.g); +} diff --git a/tests/ui/field_projections/enum.rs b/tests/ui/field_projections/enum.rs new file mode 100644 index 0000000000000..544ad9000d594 --- /dev/null +++ b/tests/ui/field_projections/enum.rs @@ -0,0 +1,15 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-fail +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub enum Foo { + A { a: isize, b: usize }, +} + +fn main() { + assert_eq!(::OFFSET, ::OFFSET); +} diff --git a/tests/ui/field_projections/incoherent-impl.next.stderr b/tests/ui/field_projections/incoherent-impl.next.stderr new file mode 100644 index 0000000000000..8772b3050b987 --- /dev/null +++ b/tests/ui/field_projections/incoherent-impl.next.stderr @@ -0,0 +1,39 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:16:1 + | +LL | impl extern_crate::ForeignTrait for field_of!(Point, x) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------- + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:26:1 + | +LL | impl ForeignTrait for field_of!(Player, pos.x) {} + | ^^^^^^^^^^^^^^^^^^^^^^------------------------ + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:35:1 + | +LL | impl ForeignTrait for field_of!((usize, usize), 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^---------------------------- + | | + | this is not defined in the current crate because tuples are always foreign + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_projections/incoherent-impl.old.stderr b/tests/ui/field_projections/incoherent-impl.old.stderr new file mode 100644 index 0000000000000..8772b3050b987 --- /dev/null +++ b/tests/ui/field_projections/incoherent-impl.old.stderr @@ -0,0 +1,39 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:16:1 + | +LL | impl extern_crate::ForeignTrait for field_of!(Point, x) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------- + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:26:1 + | +LL | impl ForeignTrait for field_of!(Player, pos.x) {} + | ^^^^^^^^^^^^^^^^^^^^^^------------------------ + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:35:1 + | +LL | impl ForeignTrait for field_of!((usize, usize), 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^---------------------------- + | | + | this is not defined in the current crate because tuples are always foreign + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_projections/incoherent-impl.rs b/tests/ui/field_projections/incoherent-impl.rs new file mode 100644 index 0000000000000..b2a6dd5dfcf98 --- /dev/null +++ b/tests/ui/field_projections/incoherent-impl.rs @@ -0,0 +1,40 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ aux-build:extern-crate.rs +#![allow(incomplete_features)] +#![feature(field_projections)] +extern crate extern_crate; + +use std::field::field_of; + +use extern_crate::{ForeignTrait, Point}; + +pub trait MyTrait {} + +impl MyTrait for field_of!(Point, x) {} + +impl extern_crate::ForeignTrait for field_of!(Point, x) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] + +pub struct Player { + pos: Point, + hp: (u32, u32), +} + +impl ForeignTrait for field_of!(Player, pos) {} + +impl ForeignTrait for field_of!(Player, pos.x) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] + +impl MyTrait for field_of!(Player, pos) {} + +impl MyTrait for field_of!(Player, pos.x) {} + +impl MyTrait for field_of!((usize, usize), 0) {} + +impl ForeignTrait for field_of!((usize, usize), 0) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] + +impl ForeignTrait for field_of!(Player, hp.0) {} + +fn main() {} diff --git a/tests/ui/field_projections/not-field-if-packed.next.stderr b/tests/ui/field_projections/not-field-if-packed.next.stderr new file mode 100644 index 0000000000000..f46ba9875b136 --- /dev/null +++ b/tests/ui/field_projections/not-field-if-packed.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): Field` is not satisfied + --> $DIR/not-field-if-packed.rs:14:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-packed.rs:11:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_projections/not-field-if-packed.old.stderr b/tests/ui/field_projections/not-field-if-packed.old.stderr new file mode 100644 index 0000000000000..f46ba9875b136 --- /dev/null +++ b/tests/ui/field_projections/not-field-if-packed.old.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): Field` is not satisfied + --> $DIR/not-field-if-packed.rs:14:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-packed.rs:11:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_projections/not-field-if-packed.rs b/tests/ui/field_projections/not-field-if-packed.rs new file mode 100644 index 0000000000000..2e523eb525d7f --- /dev/null +++ b/tests/ui/field_projections/not-field-if-packed.rs @@ -0,0 +1,15 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[repr(packed)] +pub struct MyStruct(usize); + +fn assert_field() {} + +fn main() { + assert_field::(); //~ ERROR: the trait bound `field_of!(MyStruct, 0): Field` is not satisfied [E0277] +} diff --git a/tests/ui/field_projections/send.next.stderr b/tests/ui/field_projections/send.next.stderr new file mode 100644 index 0000000000000..d72f2e1907af7 --- /dev/null +++ b/tests/ui/field_projections/send.next.stderr @@ -0,0 +1,26 @@ +error: impls of auto traits for field representing types not supported + --> $DIR/send.rs:13:1 + | +LL | unsafe impl Send for field_of!(Bar, bar_field) {} + | ^^^^^^^^^^^^----^^^^^-------------------------^^^ + | | | + | | field representing type + | auto trait + +error[E0277]: `field_of!(Foo, field)` cannot be sent between threads safely + --> $DIR/send.rs:20:15 + | +LL | is_send::(); + | ^^^^^^^^^^^^^^^^^^^^^ `field_of!(Foo, field)` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `field_of!(Foo, field)` + = help: the trait `Send` is implemented for `field_of!(Bar, bar_field)` +note: required by a bound in `is_send` + --> $DIR/send.rs:16:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_projections/send.old.stderr b/tests/ui/field_projections/send.old.stderr new file mode 100644 index 0000000000000..d72f2e1907af7 --- /dev/null +++ b/tests/ui/field_projections/send.old.stderr @@ -0,0 +1,26 @@ +error: impls of auto traits for field representing types not supported + --> $DIR/send.rs:13:1 + | +LL | unsafe impl Send for field_of!(Bar, bar_field) {} + | ^^^^^^^^^^^^----^^^^^-------------------------^^^ + | | | + | | field representing type + | auto trait + +error[E0277]: `field_of!(Foo, field)` cannot be sent between threads safely + --> $DIR/send.rs:20:15 + | +LL | is_send::(); + | ^^^^^^^^^^^^^^^^^^^^^ `field_of!(Foo, field)` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `field_of!(Foo, field)` + = help: the trait `Send` is implemented for `field_of!(Bar, bar_field)` +note: required by a bound in `is_send` + --> $DIR/send.rs:16:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_projections/send.rs b/tests/ui/field_projections/send.rs new file mode 100644 index 0000000000000..6c1ff04234187 --- /dev/null +++ b/tests/ui/field_projections/send.rs @@ -0,0 +1,22 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![feature(field_projections)] +#![allow(incomplete_features)] +use std::field::field_of; + +struct Foo { + field: u32, +} +struct Bar { + bar_field: u32, +} +unsafe impl Send for field_of!(Bar, bar_field) {} +//~^ ERROR: impls of auto traits for field representing types not supported + +fn is_send() {} + +fn main() { + is_send::(); + is_send::(); + //~^ ERROR: `field_of!(Foo, field)` cannot be sent between threads safely [E0277] +} diff --git a/tests/ui/field_projections/union.rs b/tests/ui/field_projections/union.rs new file mode 100644 index 0000000000000..3c9c9269160a6 --- /dev/null +++ b/tests/ui/field_projections/union.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![allow(incomplete_features, dead_code)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub union Foo { + a: isize, + b: usize, +} + +type X = field_of!(Foo, a); + +fn main() { + assert_eq!(X::OFFSET, ::OFFSET); +} diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index a028f4331725e..6f1777c84c191 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h1dddcfd03744167fE) +error: symbol-name(_ZN5basic4main17h9dcd691dfd66f09dE) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h1dddcfd03744167f) +error: demangling(basic::main::h9dcd691dfd66f09d) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index 14cbd877d9f8a..397ae379a3f48 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h4b3099ec5dc5d306E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h0c5a913a7866cf0eE) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h4b3099ec5dc5d306) +error: demangling(issue_60925::foo::Foo::foo::h0c5a913a7866cf0e) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name]