diff --git a/Cargo.lock b/Cargo.lock index d83bdf767e5c1..2aeaf69b22780 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3427,6 +3427,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "serde", "tracing", ] @@ -4293,6 +4294,7 @@ dependencies = [ "rustc_target", "rustc_thread_pool", "rustc_type_ir", + "serde", "smallvec", "thin-vec", "tracing", diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 83d96d8d04daf..4776454204f32 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -15,6 +15,7 @@ rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } +serde = { version = "1.0.125", features = ["derive"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 41ad14f550ab0..2aec3b52cc58d 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -33,7 +33,7 @@ rustc_index::newtype_index! { /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, serde::Serialize)] #[encodable] #[orderable] pub struct FieldIdx {} @@ -57,7 +57,7 @@ rustc_index::newtype_index! { /// /// `struct`s, `tuples`, and `unions`s are considered to have a single variant /// with variant index zero, aka [`FIRST_VARIANT`]. - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, serde::Serialize)] #[encodable] #[orderable] pub struct VariantIdx { diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b2b1f997ac545..69ed8fe0292be 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2557,6 +2557,11 @@ pub enum TyKind { /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. Pat(Box, Box), + /// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`). + /// + /// Usually not written directly in user code but indirectly via the macro + /// `core::field::field_of!(...)`. + FieldOf(Box, Vec), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 2e494b968b6bd..43ef6897b79cf 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::ImplicitSelf | ast::TyKind::CVarArgs | ast::TyKind::Pat(..) + | ast::TyKind::FieldOf(..) | ast::TyKind::Dummy | ast::TyKind::Err(..) => break None, } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 51d1fd20cec6e..df74329f8635a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1499,6 +1499,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Pat(ty, pat) => { hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span)) } + TyKind::FieldOf(ty, fields) => match self.lower_ty_field_path(fields, ty.span) { + Ok(field_path) => hir::TyKind::FieldOf(self.lower_ty_alloc(ty, itctx), field_path), + Err(err) => hir::TyKind::Err(err), + }, TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index e066bce95158d..d5375ccb55910 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -7,7 +7,7 @@ use rustc_hir::definitions::DefPathData; use rustc_hir::{self as hir, LangItem, Target}; use rustc_middle::span_bug; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{DesugaringKind, Ident, Span}; +use rustc_span::{DesugaringKind, ErrorGuaranteed, Ident, Span}; use super::errors::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, @@ -478,6 +478,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) } } + pub(crate) fn lower_ty_field_path( + &mut self, + fields: &[Ident], + ty_span: Span, + ) -> Result<&'hir hir::TyFieldPath, ErrorGuaranteed> { + Ok(self.arena.alloc(self.lower_ty_field_path_mut(fields, ty_span)?)) + } + + fn lower_ty_field_path_mut( + &mut self, + fields: &[Ident], + ty_span: Span, + ) -> Result { + match fields.len() { + 0 => span_bug!(ty_span, "expected at least one field ident parsed in `field_of!`"), + 1 => Ok(hir::TyFieldPath { variant: None, field: self.lower_ident(fields[0]) }), + 2 => Ok(hir::TyFieldPath { + variant: Some(self.lower_ident(fields[0])), + field: self.lower_ident(fields[1]), + }), + _ => Err(self.dcx().span_err( + fields.iter().map(|f| f.span).collect::>(), + "`field_of!` only supports a single field or a variant with a field", + )), + } + } + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index e50e31c226fdb..da99ea433bcd6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1377,6 +1377,25 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + ast::TyKind::FieldOf(ty, fields) => { + self.word("builtin # field_of"); + self.popen(); + let ib = self.ibox(0); + self.print_type(ty); + 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(); + } } self.end(ib); } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 91defbad0a0e0..085a33705b5e7 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1875,6 +1875,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) @@ -1919,6 +1920,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 02476a3322527..1a64db170e20e 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -216,6 +216,8 @@ fn push_debuginfo_type_name<'tcx>( write!(output, "{:?}", t).unwrap(); } } + // FIXME(FRTs): implement debuginfo for field representing types + ty::FRT(..) => todo!(), ty::Slice(inner_type) => { if cpp_like_debuginfo { output.push_str("slice2$<"); diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 5d37db06d76ac..71d891c756622 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -104,6 +104,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::Adt(_, _) + | ty::FRT(_, _) | ty::Foreign(_) | ty::Pat(_, _) | ty::Slice(_) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index b771addb8df55..efd07f7186f77 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -90,7 +90,7 @@ fn const_to_valtree_inner<'tcx>( } match ty.kind() { - ty::FnDef(..) => { + ty::FnDef(..) | ty::FRT(..) => { *num_nodes += 1; Ok(ty::ValTree::zst(tcx)) } @@ -273,7 +273,7 @@ pub fn valtree_to_const_value<'tcx>( // create inner `MPlace`s which are filled recursively. // FIXME: Does this need an example? match *cv.ty.kind() { - ty::FnDef(..) => { + ty::FnDef(..) | ty::FRT(..) => { assert!(cv.valtree.is_zst()); mir::ConstValue::ZeroSized } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fe1dd1b6eb352..e570ef85e6bed 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; 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::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{FieldIdData, FloatTy, PolyExistentialPredicate, Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; @@ -223,6 +223,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; } + sym::field_offset => { + let frt_ty = instance.args.type_at(0); + + let (ty, field) = match frt_ty.kind() { + &ty::FRT(ty, field) => (ty, field), + 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, got {frt_ty}") + } + }; + let FieldIdData::Resolved { variant, field } = &*field.0 else { + span_bug!( + self.cur_span(), + "expected resolved field representing type, got `field_of!({ty}, {field:?})`" + ) + }; + let layout = self.layout_of(ty)?; + let cx = ty::layout::LayoutCx::new(*self.tcx, self.typing_env); + + let layout = layout.for_variant(&cx, *variant); + let offset = layout.fields.offset(field.index()).bytes(); + + self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; + } sym::vtable_for => { let tp_ty = instance.args.type_at(0); let result_ty = instance.args.type_at(1); @@ -301,6 +328,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::UnsafeBinder(_) | ty::Never | ty::Tuple(_) + | ty::FRT(..) | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), }; let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 1c1c59da9d886..ba9c2c7c212ac 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::FRT(..) | 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 5d8ae42f5eccd..72f926eb69e08 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -785,6 +785,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { | ty::Dynamic(..) | ty::Closure(..) | ty::Pat(..) + | ty::FRT(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => interp_ok(false), // Some types only occur during typechecking, they have no layout. diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index db651811551f3..5564277ac5053 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -34,6 +34,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::Float(_) | ty::Str | ty::Pat(_, _) + | ty::FRT(..) | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_, _) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8959bc586af7b..1566b96202994 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -488,6 +488,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 f645c7fe7dea5..f495ed592af1b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1729,6 +1729,12 @@ impl<'hir> Block<'hir> { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct TyFieldPath { + pub variant: Option, + pub field: Ident, +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct TyPat<'hir> { #[stable_hasher(ignore)] @@ -3773,6 +3779,10 @@ pub enum TyKind<'hir, Unambig = ()> { Err(rustc_span::ErrorGuaranteed), /// Pattern types (`pattern_type!(u32 is 1..)`) Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>), + /// Field representing type (`field_of!(Struct, field)`). + /// + /// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`. + FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath), /// `TyKind::Infer` means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. /// diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d1b75325af268..82c4af2120594 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1047,6 +1047,13 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) - try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); } + TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + try_visit!(visitor.visit_ty_unambig(ty)); + if let Some(variant) = *variant { + try_visit!(visitor.visit_ident(variant)); + } + try_visit!(visitor.visit_ident(*field)); + } } V::Result::output() } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..9ccdf77f50269 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -448,6 +448,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); + + // Field projection related lang-items + Field, sym::field, field, Target::Trait, GenericRequirement::Exact(0); + FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0); + FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0); + FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d3d167f6e2544..cea4f31c73006 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -117,6 +117,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 @@ -295,6 +296,7 @@ pub(crate) fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), + sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 8b50eceb26e49..862655506af91 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -261,6 +261,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 fe47f3258846d..69e3a93f4cffa 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -15,7 +15,7 @@ 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_span::ErrorGuaranteed; +use rustc_span::{ErrorGuaranteed, Span}; use crate::errors; @@ -160,12 +160,16 @@ impl<'tcx> InherentCollect<'tcx> { let id = id.owner_id.def_id; let item_span = self.tcx.def_span(id); let self_ty = self.tcx.type_of(id).instantiate_identity(); - let mut self_ty = self.tcx.peel_off_free_alias_tys(self_ty); - // We allow impls on pattern types exactly when we allow impls on the base type. - // FIXME(pattern_types): Figure out the exact coherence rules we want here. - while let ty::Pat(base, _) = *self_ty.kind() { - self_ty = base; - } + let self_ty = self.tcx.peel_off_free_alias_tys(self_ty); + self.check_impl_self_ty(self_ty, id, item_span) + } + + fn check_impl_self_ty( + &mut self, + self_ty: Ty<'tcx>, + id: LocalDefId, + item_span: Span, + ) -> Result<(), ErrorGuaranteed> { match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), @@ -175,7 +179,10 @@ impl<'tcx> InherentCollect<'tcx> { ty::Dynamic(..) => { Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span })) } - ty::Pat(_, _) => unreachable!(), + // We allow impls on pattern types exactly when we allow impls on the base type. + // FIXME(pattern_types): Figure out the exact coherence rules we want here. + ty::Pat(base, _) => self.check_impl_self_ty(base, id, item_span), + ty::FRT(ty, _) => self.check_impl_self_ty(ty, id, item_span), ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f1e138dbcb97a..de1c9814c9ae9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -82,7 +82,10 @@ pub(crate) fn orphan_check_impl( ); if tcx.trait_is_auto(trait_def_id) { - let self_ty = trait_ref.self_ty(); + let mut self_ty = trait_ref.self_ty(); + if let ty::FRT(ty, _) = self_ty.kind() { + self_ty = *ty; + } // If the impl is in the same crate as the auto-trait, almost anything // goes. @@ -233,6 +236,7 @@ pub(crate) fn orphan_check_impl( let sp = tcx.def_span(impl_def_id); span_bug!(sp, "weird self type for autotrait impl") } + ty::FRT(..) => unreachable!("handled above"), ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow), }; 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 6e1e6c157a91e..7fd7fc959dc22 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3004,6 +3004,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.record_ty(pat.hir_id, ty, pat.span); pat_ty } + hir::TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => ty::lower_field_of( + self.tcx(), + self.lower_ty(ty), + self.item_def_id(), + ty.span, + hir_ty.span, + hir_ty.hir_id, + *variant, + *field, + ), hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index ce4668736b570..011a3f1dc2166 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -255,6 +255,10 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_ty(current, typ, variance); } + ty::FRT(ty, _) => { + self.add_constraints_from_ty(current, ty, variance); + } + ty::Slice(typ) => { self.add_constraints_from_ty(current, typ, variance); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e0e9fe7758535..4d0a14eda06c4 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -19,7 +19,7 @@ use rustc_hir::attrs::{AttributeKind, PrintAttribute}; use rustc_hir::{ BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, - PreciseCapturingArg, RangeEnd, Term, TyPatKind, + PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind, }; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym}; @@ -464,6 +464,17 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + hir::TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + self.word("field_of!("); + self.print_type(ty); + self.word(", "); + if let Some(variant) = *variant { + self.print_ident(variant); + self.word("."); + } + self.print_ident(*field); + self.word(")"); + } } self.end(ib) } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3f13a102684e0..9074e378c7cd3 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -137,6 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 23f6fee406a55..779fab28ef0af 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -420,6 +420,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { | ty::Alias(..) | ty::Foreign(..) | ty::Pat(..) + | ty::FRT(..) | ty::Param(..) => { if t.flags().intersects(self.needs_canonical_flags) { t.super_fold_with(self) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index f5b882494863a..8b27cbf51fd98 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -439,6 +439,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_types_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 38094c67c34a0..c4e3f21a1f24d 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -580,6 +580,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // but only the base type is relevant for being representable in FFI. ty::Pat(base, ..) => self.visit_type(state, base), + ty::FRT(..) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_field_representing_types_reason, + help: None, + }, + // Primitive types with a stable representation. ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index fbcce16cedca8..f161a7021da6d 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -33,6 +33,7 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_thread_pool = { path = "../rustc_thread_pool" } rustc_type_ir = { path = "../rustc_type_ir" } +serde = { version = "1.0.125", features = ["derive"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 510c546f82a4e..2806edb3bfd2c 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str; -use rustc_abi::{FIRST_VARIANT, ReprOptions, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, ReprOptions, VariantIdx}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; @@ -11,12 +11,13 @@ use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHa use rustc_errors::ErrorGuaranteed; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, find_attr}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; +use rustc_span::{Ident, Span}; use rustc_type_ir::solve::AdtDestructorKind; use tracing::{debug, info, trace}; @@ -681,3 +682,100 @@ pub enum Representability { Representable, Infinite(ErrorGuaranteed), } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable)] +#[rustc_pass_by_value] +pub struct FieldId<'tcx>(pub Interned<'tcx, FieldIdData>); + +#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone, TyEncodable, TyDecodable, HashStable)] +pub enum FieldIdData { + Resolved { + variant: VariantIdx, + field: FieldIdx, + }, + Unresolved { + item_def_id: LocalDefId, + full_span: Span, + hir_id: HirId, + ty_span: Span, + variant: Option, + field: Ident, + }, +} + +impl serde::Serialize for FieldId<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match &*self.0 { + FieldIdData::Resolved { variant, field } => { + serde::Serialize::serialize(&(*variant, *field), serializer) + } + unresolved @ FieldIdData::Unresolved { .. } => bug!("cannot serialize {unresolved:?}"), + } + } +} + +impl<'tcx> FieldId<'tcx> { + pub fn resolve(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + match *self.0 { + FieldIdData::Resolved { .. } => Ty::new_field_representing_type(tcx, ty, self), + FieldIdData::Unresolved { hir_id, item_def_id, full_span, ty_span, variant, field } => { + ty::lower_field_of(tcx, ty, item_def_id, ty_span, full_span, hir_id, variant, field) + } + } + } + + pub fn ty(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + match *self.0 { + FieldIdData::Resolved { variant, field } => match ty.kind() { + ty::Adt(def, args) => def.variants()[variant].fields[field].ty(tcx, args), + ty::Tuple(tys) => { + debug_assert_eq!(FIRST_VARIANT, variant); + tys[field.index()] + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::FRT(_, _) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::UnsafeBinder(_) + | ty::Dynamic(_, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Array(..) + | ty::Pat(..) + | ty::Slice(..) + | ty::Error(_) + | ty::Alias(..) => bug!( + "don't expect to see this TyKind here, should be handled by lowering hir to mir" + ), + }, + FieldIdData::Unresolved { ty_span, .. } => Ty::new_error( + tcx, + tcx.dcx().span_err(ty_span, format!("type `{ty}` doesn't have fields")), + ), + } + } +} + +impl<'tcx> rustc_type_ir::inherent::FieldId> for FieldId<'tcx> { + fn ty(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + FieldId::ty(self, tcx, ty) + } +} diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 75b1317e022bd..70b1bc922de5d 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -23,7 +23,7 @@ use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance}; use crate::mir::mono::MonoItem; use crate::mir::{self}; use crate::traits; -use crate::ty::{self, AdtDef, GenericArgsRef, Ty, TyCtxt}; +use crate::ty::{self, AdtDef, FieldId, GenericArgsRef, Ty, TyCtxt}; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -195,6 +195,12 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for AdtDef<'tcx> { } } +impl<'tcx, E: TyEncoder<'tcx>> Encodable for FieldId<'tcx> { + fn encode(&self, e: &mut E) { + self.0.0.encode(e) + } +} + impl<'tcx, E: TyEncoder<'tcx>> Encodable for AllocId { fn encode(&self, e: &mut E) { e.encode_alloc_id(self) @@ -394,6 +400,12 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for ConstAllocation<'tcx> { } } +impl<'tcx, D: TyDecoder<'tcx>> Decodable for FieldId<'tcx> { + fn decode(decoder: &mut D) -> Self { + decoder.interner().mk_field_id_from_data(Decodable::decode(decoder)) + } +} + impl<'tcx, D: TyDecoder<'tcx>> Decodable for AdtDef<'tcx> { fn decode(decoder: &mut D) -> Self { decoder.interner().mk_adt_def_from_data(Decodable::decode(decoder)) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e9fa3f14358e8..bea1848a378a7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -15,7 +15,9 @@ use std::ops::{Bound, Deref}; use std::sync::{Arc, OnceLock}; use std::{fmt, iter, mem}; -use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; +use rustc_abi::{ + ExternAbi, FIRST_VARIANT, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx, +}; use rustc_ast as ast; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; @@ -30,7 +32,8 @@ use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, + Applicability, Diag, DiagCtxtHandle, E0001, E0609, E0616, ErrorGuaranteed, LintDiagnostic, + MultiSpan, struct_span_code_err, }; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -78,11 +81,11 @@ 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, - PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, - ValTree, ValTreeKind, Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, FieldId, FieldIdData, + 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, }; #[allow(rustc::usage_of_ty_tykind)] @@ -252,6 +255,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } + type FieldId = FieldId<'tcx>; + fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { match self.def_kind(alias.def_id) { DefKind::AssocTy => { @@ -578,6 +583,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -814,6 +820,8 @@ bidirectional_lang_item_map! { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -845,6 +853,7 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, @@ -946,6 +955,7 @@ pub struct CtxtInterners<'tcx> { bound_variable_kinds: InternedSet<'tcx, List>, layout: InternedSet<'tcx, LayoutData>, adt_def: InternedSet<'tcx, AdtDefData>, + field_id: InternedSet<'tcx, FieldIdData>, external_constraints: InternedSet<'tcx, ExternalConstraintsData>>, predefined_opaques_in_body: InternedSet<'tcx, List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>>, fields: InternedSet<'tcx, List>, @@ -983,6 +993,7 @@ impl<'tcx> CtxtInterners<'tcx> { bound_variable_kinds: InternedSet::with_capacity(N * 2), layout: InternedSet::with_capacity(N), adt_def: InternedSet::with_capacity(N), + field_id: InternedSet::with_capacity(N), external_constraints: InternedSet::with_capacity(N), predefined_opaques_in_body: InternedSet::with_capacity(N), fields: InternedSet::with_capacity(N * 4), @@ -2642,6 +2653,7 @@ impl<'tcx> TyCtxt<'tcx> { Infer, Alias, Pat, + FRT, Foreign )?; @@ -2791,6 +2803,7 @@ direct_interners! { const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>, layout: pub mk_layout(LayoutData): Layout -> Layout<'tcx>, adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, + field_id: pub mk_field_id_from_data(FieldIdData): FieldId -> FieldId<'tcx>, external_constraints: pub mk_external_constraints(ExternalConstraintsData>): ExternalConstraints -> ExternalConstraints<'tcx>, } @@ -3580,3 +3593,194 @@ pub fn provide(providers: &mut Providers) { pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool { attrs.iter().any(|x| x.has_name(name)) } +pub fn lower_field_of<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + item_def_id: LocalDefId, + ty_span: Span, + full_span: Span, + hir_id: HirId, + variant: Option, + field: Ident, +) -> Ty<'tcx> { + let dcx = tcx.dcx(); + match ty.kind() { + ty::Adt(def, _) => { + let base_did = def.did(); + let kind_name = tcx.def_descr(base_did); + let (variant_idx, variant) = if def.is_enum() { + let Some(variant) = variant else { + let adt_path = tcx.def_path_str(base_did); + let err = struct_span_code_err!( + dcx, + field.span, + // FIXME(FRTs): use correct error number + E0001, + "field access on {kind_name} `{adt_path}` requires a variant" + ) + .with_span_help(field.span.shrink_to_lo(), "add the variant here `Variant.`") + .emit(); + return Ty::new_error(tcx, err); + }; + + if let Some(res) = def + .variants() + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == variant) + { + res + } else { + let adt_path = tcx.def_path_str(base_did); + let err = struct_span_code_err!( + dcx, + variant.span, + // FIXME(FRTs): use correct error number + E0001, + "no variant `{variant}` on {kind_name} `{adt_path}`" + ) + .with_span_label(variant.span, "unknown variant") + .emit(); + return Ty::new_error(tcx, err); + } + } else { + if let Some(variant) = variant { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + dcx, + variant.span, + // FIXME(FRTs): use correct error number + E0001, + "{kind_name} `{adt_path}` does not have any variants", + ) + .with_span_label(variant.span, "variant unknown") + .emit(); + } + (FIRST_VARIANT, def.non_enum_variant()) + }; + let block = tcx.local_def_id_to_hir_id(item_def_id); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), block); + if let Some((field_idx, field)) = variant + .fields + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == ident) + { + if field.vis.is_accessible_from(def_scope, tcx) { + tcx.check_stability(field.did, Some(hir_id), ident.span, None); + } else { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + dcx, + ident.span, + E0616, + "field `{ident}` of {kind_name} `{adt_path}` is private", + ) + .with_span_label(ident.span, "private field") + .emit(); + } + Ty::new_field_representing_type( + tcx, + ty, + tcx.mk_field_id_from_data(FieldIdData::Resolved { + variant: variant_idx, + field: field_idx, + }), + ) + } else { + let adt_path = tcx.def_path_str(base_did); + let err = struct_span_code_err!( + dcx, + ident.span, + E0609, + "no field `{ident}` on {kind_name} `{adt_path}`" + ) + .with_span_label(ident.span, "unknown field") + .emit(); + Ty::new_error(tcx, err) + } + } + ty::Tuple(tys) => { + let index = match field.as_str().parse::() { + Ok(idx) => idx, + Err(_) => { + let err = struct_span_code_err!( + dcx, + field.span, + E0609, + "no field `{field}` on type `{ty}`" + ) + .with_span_label(field.span, "unknown field") + .emit(); + return Ty::new_error(tcx, err); + } + }; + if field.name != sym::integer(index) { + bug!("we parsed above, but now not equal?"); + } + if tys.get(index).is_some() { + Ty::new_field_representing_type( + tcx, + ty, + tcx.mk_field_id_from_data(FieldIdData::Resolved { + variant: FIRST_VARIANT, + field: index.into(), + }), + ) + } else { + let err = struct_span_code_err!( + dcx, + field.span, + E0609, + "no field `{field}` on type `{ty}`" + ) + .with_span_label(field.span, "unknown field") + .emit(); + Ty::new_error(tcx, err) + } + } + ty::Alias(..) | ty::Error(..) => Ty::new_field_representing_type( + tcx, + ty, + tcx.mk_field_id_from_data(FieldIdData::Unresolved { + hir_id, + full_span, + ty_span, + item_def_id, + variant, + field, + }), + ), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::FRT(_, _) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::UnsafeBinder(_) + | ty::Dynamic(_, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Slice(..) => { + Ty::new_error(tcx, dcx.span_err(ty_span, format!("type `{ty}` doesn't have fields"))) + } + ty::Infer(_) => { + Ty::new_error(tcx, dcx.span_err(ty_span, format!("cannot use `{ty}` in this position"))) + } + // FIXME(FRTs): support these types? + ty::Array(..) | ty::Pat(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("type `{ty}` is not yet supported in `field_of!`")), + ), + } +} diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 66542525d2841..03784b2420f67 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -186,6 +186,7 @@ impl<'tcx> Ty<'tcx> { ty::Foreign(_) => "extern type".into(), ty::Array(..) => "array".into(), ty::Pat(..) => "pattern type".into(), + ty::FRT(..) => "field representing type".into(), ty::Slice(_) => "slice".into(), ty::RawPtr(_, _) => "raw pointer".into(), ty::Ref(.., mutbl) => match mutbl { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 81b8142d03f37..58bdcbee6dbb0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -839,6 +839,7 @@ where | ty::Uint(_) | ty::Float(_) | ty::FnPtr(..) + | ty::FRT(..) | ty::Never | ty::FnDef(..) | ty::CoroutineWitness(..) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8eee114ead02e..cecb8b30ba8a5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -80,7 +80,8 @@ pub use self::consts::{ ExprKind, ScalarInt, SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value, }; pub use self::context::{ - CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, + CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, + lower_field_of, tls, }; pub use self::fold::*; pub use self::instance::{Instance, InstanceKind, ReifyReason, UnusedGenericParams}; diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 04a7cd2c75f28..e3f70fe131c90 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -85,6 +85,7 @@ impl MappingFlags { | ty::Float(_) | ty::Adt(_, _) | ty::Tuple(_) + | ty::FRT(..) | ty::Array(_, _) | ty::Alias(_, _) | ty::Param(_) => MappingFlags::TO, diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 0fd68e74e0441..456447a342d11 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -296,6 +296,8 @@ fn characteristic_def_id_of_type_cached<'a>( characteristic_def_id_of_type_cached(subty, visited) } + ty::FRT(ty, _) => characteristic_def_id_of_type_cached(ty, visited), + ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited), ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2a65517de4033..e012b76a067cd 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write as _}; use std::iter; use std::ops::{Deref, DerefMut}; -use rustc_abi::{ExternAbi, Size}; +use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; @@ -723,6 +723,44 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty.print(self)?; write!(self, ") is {pat:?}")?; } + ty::FRT(ty, field) => { + write!(self, "field_of!(")?; + ty.print(self)?; + write!(self, ", ")?; + match &*field.0 { + ty::FieldIdData::Resolved { variant, field } => match ty.kind() { + ty::Adt(def, _) => { + let variant = if def.is_enum() { + let variant = &def.variants()[*variant]; + write!(self, "{}.", variant.name)?; + variant + } else { + def.non_enum_variant() + }; + write!(self, "{}", variant.fields[*field].name)?; + } + ty::Tuple(_) => { + debug_assert_eq!(*variant, FIRST_VARIANT); + write!(self, "{}", field.index())?; + } + _ => bug!("unexpected ty in resolved FRT: {ty}"), + }, + ty::FieldIdData::Unresolved { + variant, + field, + hir_id: _, + ty_span: _, + full_span: _, + item_def_id: _, + } => { + if let Some(variant) = variant { + write!(self, "{variant}.")?; + } + write!(self, "{field}")?; + } + } + write!(self, ")")?; + } ty::RawPtr(ty, mutbl) => { write!(self, "*{} ", mutbl.ptr_str())?; ty.print(self)?; diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index f1aa7076d98ac..b7cdb26c7d117 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -136,6 +136,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Placeholder(_) | ty::Infer(_) | ty::Slice(_) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 314d2ba396327..8180a54740ace 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -400,6 +400,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { } ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?), ty::Pat(ty, pat) => ty::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?), + ty::FRT(ty, field) => *field.resolve(folder.cx(), ty.try_fold_with(folder)?).kind(), ty::Bool | ty::Char @@ -439,6 +440,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::CoroutineClosure(did, args) => ty::CoroutineClosure(did, args.fold_with(folder)), ty::Alias(kind, data) => ty::Alias(kind, data.fold_with(folder)), ty::Pat(ty, pat) => ty::Pat(ty.fold_with(folder), pat.fold_with(folder)), + ty::FRT(ty, field) => *field.resolve(folder.cx(), ty.fold_with(folder)).kind(), ty::Bool | ty::Char @@ -491,6 +493,8 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { try_visit!(ty.visit_with(visitor)); pat.visit_with(visitor) } + // FIXME(FRTs): add FieldId to the visitor infrastructure if we need to visit it. + ty::FRT(ty, _field) => ty.visit_with(visitor), ty::Error(guar) => guar.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c282f2211f650..bfc90a5328728 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, FieldId, GenericArg, GenericArgs, GenericArgsRef, List, + ParamEnv, Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -533,6 +534,15 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, Pat(base, pat)) } + #[inline] + pub fn new_field_representing_type( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + field_id: FieldId<'tcx>, + ) -> Ty<'tcx> { + Ty::new(tcx, FRT(ty, field_id)) + } + #[inline] #[instrument(level = "debug", skip(tcx))] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { @@ -1106,6 +1116,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_pat(interner, ty, pat) } + fn new_field_representing_type(tcx: TyCtxt<'tcx>, ty: Self, field_id: FieldId<'tcx>) -> Self { + Self::new_field_representing_type(tcx, ty, field_id) + } + fn new_unsafe_binder(interner: TyCtxt<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>) -> Self { Ty::new_unsafe_binder(interner, ty) } @@ -1125,6 +1139,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { fn has_unsafe_fields(self) -> bool { Ty::has_unsafe_fields(self) } + + fn is_packed(self) -> bool { + Ty::is_packed(self) + } } /// Type utilities @@ -1179,6 +1197,11 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), Adt(..)) } + #[inline] + pub fn is_packed(self) -> bool { + matches!(self.kind(), Adt(def, _) if def.repr().packed()) + } + #[inline] pub fn is_ref(self) -> bool { matches!(self.kind(), Ref(..)) @@ -1680,6 +1703,7 @@ impl<'tcx> Ty<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(..) + | ty::FRT(..) | ty::Foreign(_) | ty::Str | ty::Array(..) @@ -1731,6 +1755,7 @@ impl<'tcx> Ty<'tcx> { | ty::Array(..) | ty::Closure(..) | ty::CoroutineClosure(..) + | ty::FRT(..) | ty::Never | ty::Error(_) // Extern types have metadata = (). @@ -1921,6 +1946,7 @@ impl<'tcx> Ty<'tcx> { | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -1974,6 +2000,9 @@ impl<'tcx> Ty<'tcx> { // ZST which can't be named are fine. ty::FnDef(..) => true, + // ZST. + ty::FRT(..) => true, + ty::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(), // A 100-tuple isn't "trivial", so doing this only for reasonable sizes. @@ -2044,6 +2073,7 @@ impl<'tcx> Ty<'tcx> { | ty::Array(..) | ty::Foreign(_) | ty::Pat(_, _) + | ty::FRT(..) | ty::FnDef(..) | ty::UnsafeBinder(..) | ty::Dynamic(..) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2797f2fcdb72e..2a3cdb76f93fa 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1171,6 +1171,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(_, _) | ty::FnDef(..) @@ -1212,6 +1213,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(_, _) | ty::FnDef(..) @@ -1263,6 +1265,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) @@ -1434,6 +1437,9 @@ impl<'tcx> Ty<'tcx> { // Raw pointers use bitwise comparison. ty::RawPtr(_, _) | ty::FnPtr(..) => true, + // FRTs are ZSTs. + ty::FRT(..) => true, + // Floating point numbers are not `Eq`. ty::Float(_) => false, @@ -1513,6 +1519,7 @@ pub fn needs_drop_components_with_async<'tcx>( | ty::Uint(_) | ty::Float(_) | ty::Never + | ty::FRT(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Char diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ced9bd735ba2b..0790d26e9ed91 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -154,6 +154,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) @@ -196,6 +197,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 5254f60a15036..8795d3adfce5b 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -867,7 +867,7 @@ fn try_write_constant<'tcx>( match ty.kind() { // ZSTs. Nothing to do. - ty::FnDef(..) => {} + ty::FnDef(..) | ty::FRT(..) => {} // Those are scalars, must be handled above. ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 61a2dcd226b7b..b8728ba2414a8 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -380,6 +380,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index c370fd24a1bb3..4e99d8de6ea65 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -408,6 +408,7 @@ where // For fundamental types, we just look inside of them. ty::Ref(_, ty, _) => ty.visit_with(self), + ty::FRT(ty, _) => ty.visit_with(self), ty::Adt(def, args) => { if self.def_id_is_local(def.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 d27d80a086ad1..f7af17751d7c6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -348,6 +348,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 @@ -617,6 +622,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), } }; @@ -690,6 +696,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -808,6 +815,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) 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 05ea217c1de08..e21da4a1d0902 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 @@ -35,6 +35,7 @@ where | ty::Float(_) | ty::FnDef(..) | ty::FnPtr(..) + | ty::FRT(..) | ty::Error(_) | ty::Never | ty::Char => Ok(ty::Binder::dummy(vec![])), @@ -117,7 +118,7 @@ where match ty.kind() { // 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 FRT, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -132,6 +133,7 @@ where | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -194,7 +196,7 @@ where { match ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) @@ -389,6 +391,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( | ty::Never | ty::Tuple(_) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) @@ -784,6 +789,7 @@ pub(in crate::solve) fn const_conditions_for_destruct( | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::FRT(..) | ty::Error(_) => Ok(vec![]), // Coroutines and closures could implement `[const] Drop`, 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 2837b8565f603..4da76b88b5de0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -430,6 +430,13 @@ where ) -> Vec> { unreachable!("Unsize is not const") } + + fn consider_builtin_field_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<::Interner, Self>, + ) -> Result::Interner>, 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 70c28421c57ea..1d0c33ca6b5ca 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 @@ -631,6 +631,7 @@ where | ty::Float(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -884,6 +885,7 @@ where | ty::Float(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -950,6 +952,29 @@ where ) -> Result, NoSolution> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + let self_ty = goal.predicate.self_ty(); + let ty::FRT(container, field) = self_ty.kind() else { + return Err(NoSolution); + }; + + let ty = if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldBase) { + container + } else if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldType) { + field.ty(ecx.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 651f073efb828..12777d2e18d5b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -735,6 +735,7 @@ where | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -841,6 +842,23 @@ where } }) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + if let ty::FRT(ty, _) = goal.predicate.self_ty().kind() + && !ty.is_packed() + { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } else { + Err(NoSolution) + } + } } /// Small helper function to change the `def_id` of a trait predicate - this is not normally @@ -1241,6 +1259,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8835fba1adcdc..c6e8e0060a1ee 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1140,7 +1140,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(crate) 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 0185c51c5c560..d3a85cf79a04f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -329,6 +329,8 @@ impl<'a> Parser<'a> { self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty(lo)? + } else if self.is_builtin() { + self.parse_builtin_ty()? } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer @@ -811,6 +813,42 @@ impl<'a> Parser<'a> { Ok(TyKind::Err(guar)) } + 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..1295a7a814461 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -301,6 +301,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { | ty::Str | ty::Tuple(_) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 35101624bd68e..c4025c85ebc6e 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -410,6 +410,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TraitObject, Infer, Pat, + FieldOf, Err ] ); @@ -679,6 +680,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Tup, Path, Pat, + FieldOf, TraitObject, ImplTrait, Paren, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 5e75192ff3092..645f24d1d04be 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -407,6 +407,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Adt(..) | ty::Tuple(..) => { ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) } } + ty::FRT(..) => ConstructorSet::Struct { empty: false }, ty::Ref(..) => ConstructorSet::Ref, ty::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 8656ec6e39aed..488390b36be36 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -291,6 +291,7 @@ where | ty::RawPtr(..) | ty::Ref(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnPtr(..) | ty::UnsafeBinder(_) | ty::Param(..) diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 14656a2e594ad..5557663a90d74 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; +use rustc_abi::FieldIdx; use serde::Serialize; use super::abi::ReprOptions; @@ -554,6 +555,7 @@ pub enum RigidTy { Str, Array(Ty, TyConst), Pat(Ty, Pattern), + FRT(Ty, rustc_abi::VariantIdx, FieldIdx), Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index d9f314a8e29cc..4beb79a2153c0 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -5,7 +5,7 @@ // Prefer importing rustc_public over internal rustc constructs to make this file more readable. -use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy}; +use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, FieldIdData, Ty as InternalTy}; use rustc_public_bridge::Tables; use crate::abi::Layout; @@ -153,6 +153,13 @@ impl RustcInternal for RigidTy { RigidTy::Pat(ty, pat) => { rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx)) } + RigidTy::FRT(ty, variant, field) => rustc_ty::TyKind::FRT( + ty.internal(tables, tcx), + tcx.tcx().mk_field_id_from_data(FieldIdData::Resolved { + variant: *variant, + field: *field, + }), + ), RigidTy::Adt(def, args) => { rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx)) } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index ca8234280be85..3e4fd83a1fb18 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1,6 +1,6 @@ //! Conversion of internal Rust compiler `ty` items to stable ones. -use rustc_middle::ty::Ty; +use rustc_middle::ty::{FieldIdData, Ty}; use rustc_middle::{bug, mir, ty}; use rustc_public_bridge::Tables; use rustc_public_bridge::context::CompilerCtxt; @@ -413,6 +413,14 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Pat(ty, pat) => { TyKind::RigidTy(RigidTy::Pat(ty.stable(tables, cx), pat.stable(tables, cx))) } + ty::FRT(ty, field) => match &*field.0 { + FieldIdData::Resolved { variant, field } => { + TyKind::RigidTy(RigidTy::FRT(ty.stable(tables, cx), *variant, *field)) + } + unresolved @ FieldIdData::Unresolved { .. } => { + bug!("encountered unresolved FieldId: {unresolved:?}") + } + }, ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables, cx))), ty::RawPtr(ty, mutbl) => { TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables, cx), mutbl.stable(tables, cx))) diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index acc3334769613..737f104041253 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -158,6 +158,7 @@ impl Visitable for RigidTy { c.visit(visitor) } RigidTy::Pat(t, _p) => t.visit(visitor), + RigidTy::FRT(t, _v, _f) => t.visit(visitor), RigidTy::Slice(inner) => inner.visit(visitor), RigidTy::RawPtr(ty, _) => ty.visit(visitor), RigidTy::Ref(reg, ty, _) => { 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 5505fe82cea65..3f9e781801be4 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 @@ -434,6 +434,9 @@ pub(crate) fn encode_ty<'tcx>( typeid.push_str(&s); } + // FIXME(FRTs): add mangling support + ty::FRT(..) => todo!(), + ty::Slice(ty0) => { // u5sliceIE as vendor extended type let mut s = String::from("u5sliceI"); 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 9cea681fcb579..b3dc15da7ba8f 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 @@ -60,6 +60,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { | ty::Foreign(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Slice(..) | ty::Str | ty::Tuple(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 51920db8cd79e..e8cc68d73260c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1078,7 +1078,12 @@ symbols! { ffi_pure, ffi_returns_twice, field, + field_base, field_init_shorthand, + field_of, + field_offset, + field_projections, + field_type, file, file_options, flags, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 3896e06a627b7..908a270f6701b 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -106,6 +106,7 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 755b4923e9cd4..04cfdb37bd130 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -510,6 +510,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { self.print_pat(pat)?; } + // FIXME(FRTs): add mangling support + ty::FRT(..) => todo!("mangle {ty}"), + ty::Array(ty, len) => { self.push("A"); ty.print(self)?; 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 00d06779e652e..f3b133365145d 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 @@ -1828,6 +1828,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::CoroutineClosure(..) => Some(21), ty::Pat(..) => Some(22), ty::UnsafeBinder(..) => Some(23), + ty::FRT(..) => 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 66c949a38cea7..3a57600ba3644 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -328,7 +328,7 @@ fn evaluate_host_effect_for_copy_clone_goal<'tcx>( let self_ty = obligation.predicate.self_ty(); let constituent_tys = match *self_ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) @@ -470,6 +470,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::FRT(..) | ty::Error(_) => thin_vec![], // Coroutines and closures could implement `[const] Drop`, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e5c2adaa261d3..f653e71ee0b56 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. @@ -1027,6 +1028,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1082,6 +1084,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1551,6 +1554,17 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } }); (metadata_ty.into(), obligations) + } else if tcx.is_lang_item(trait_def_id, LangItem::Field) { + let &ty::FRT(container, field) = 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.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 2e60805cd10a5..2aada90e0d985 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -43,6 +43,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::Str | ty::Foreign(..) + | ty::FRT(..) | ty::Error(_) => true, // `T is PAT` and `[T]` have same properties as T. @@ -275,6 +276,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( | ty::Float(_) | ty::Str | ty::Never + | ty::FRT(..) | ty::Foreign(..) | ty::RawPtr(..) | ty::Ref(..) 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 d0833f0308350..1ca81189e6b6a 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_candidates_for_field_trait(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 @@ -691,6 +694,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -865,6 +869,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::Adt(..) | ty::RawPtr(_, _) @@ -1132,7 +1137,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *self_ty.kind() { // These impls are built-in because we cannot express sufficiently // generic impls in libcore. - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Tuple(..) | ty::Pat(..) => { + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Tuple(..) + | ty::Pat(..) + | ty::FRT(..) => { candidates.vec.push(BuiltinCandidate); } @@ -1247,6 +1257,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => { candidates.vec.push(SizedCandidate); } @@ -1329,6 +1340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) | ty::Dynamic(_, _) @@ -1365,6 +1377,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(..) @@ -1416,6 +1429,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -1437,4 +1451,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_field_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if let ty::FRT(ty, _) = obligation.predicate.self_ty().skip_binder().kind() + && !ty.is_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 4f65b30775ed0..8d90aebe30bd3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -265,6 +265,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | LangItem::FnPtrTrait | LangItem::PointeeTrait | LangItem::Tuple + | LangItem::Field | LangItem::Unpin, ) => ty::Binder::dummy(vec![]), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), @@ -1299,6 +1300,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d6c9adfb28177..bb6780581ee04 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2160,6 +2160,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { @@ -2197,7 +2198,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn copy_clone_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec>> { match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => ty::Binder::dummy(vec![]), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => ty::Binder::dummy(vec![]), ty::Uint(_) | ty::Int(_) @@ -2303,6 +2304,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::FnPtr(..) | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::FRT(..) | ty::Never | 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 d383cb9d91dd4..a93050163c9ba 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -794,7 +794,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } } - ty::RawPtr(_, _) => { + ty::RawPtr(_, _) | ty::FRT(..) => { // Simple cases that are WF if their type args are WF. } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cbe15eb54787f..cad69e6198990 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -380,6 +380,22 @@ fn resolve_associated_item<'tcx>( assert_eq!(name, sym::transmute); let args = tcx.erase_and_anonymize_regions(rcvr_args); Some(ty::Instance::new_raw(trait_item_id, args)) + } else 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::FRT(..) => {} + _ => 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 { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 62f3667ad7f4f..f0cbcac8f5ec5 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -379,6 +379,8 @@ fn layout_of_uncached<'tcx>( tcx.mk_layout(layout) } + ty::FRT(..) => cx.layout_of(tcx.types.unit)?.layout, + // Basic scalars. ty::Bool => tcx.mk_layout(LayoutData::scalar( cx, diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 0ef435b1a0e21..2881f0ec62330 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -289,6 +289,7 @@ where | ty::RawPtr(..) | ty::FnDef(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnPtr(..) | ty::Tuple(_) | ty::Bound(..) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index bb25a14ef7443..7097db2bd0031 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -34,6 +34,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::FnDef(..) | ty::FnPtr(..) | ty::Array(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) @@ -377,6 +378,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index ed6416a7f55f2..33b76a8a623da 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -46,6 +46,9 @@ pub enum SimplifiedType { Function(usize), UnsafeBinder, Placeholder, + /// Field representing type. + // FIXME(FRTs): should we have a `Option` here that gets filled with the Adt's DefId? + FRT, Error, } @@ -127,6 +130,7 @@ pub fn simplify_type( ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), ty::Pat(ty, ..) => simplify_type(cx, ty, treat_params), + ty::FRT(..) => Some(SimplifiedType::FRT), ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !cx.trait_is_auto(principal_def_id) => { @@ -298,6 +302,7 @@ impl matches!(rhs.kind(), ty::FRT(rhs_ty, rhs_field) if + lhs_field == rhs_field && self.types_may_unify_inner(lhs_ty, rhs_ty, depth) + ), + ty::UnsafeBinder(lhs_ty) => match rhs.kind() { ty::UnsafeBinder(rhs_ty) => { self.types_may_unify(lhs_ty.skip_binder(), rhs_ty.skip_binder()) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 8b057e5866cd9..a232411e87599 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -322,6 +322,10 @@ impl FlagComputation { self.add_ty_pat(pat); } + ty::FRT(ty, _field) => { + self.add_ty(ty); + } + ty::Slice(tt) => self.add_ty(tt), ty::RawPtr(ty, _) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 4323b1ca64690..16a5463060982 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -119,6 +119,8 @@ pub trait Ty>: fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; + fn new_field_representing_type(interner: I, ty: Self, field: I::FieldId) -> Self; + fn new_unsafe_binder(interner: I, ty: ty::Binder) -> Self; fn tuple_fields(self) -> I::Tys; @@ -152,6 +154,9 @@ pub trait Ty>: /// Checks whether this type is an ADT that has unsafe fields. fn has_unsafe_fields(self) -> bool; + /// Checks whether this type is an ADT that is `repr(packed)`. + fn is_packed(self) -> bool; + fn fn_sig(self, interner: I) -> ty::Binder> { self.kind().fn_sig(interner) } @@ -174,6 +179,7 @@ pub trait Ty>: | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) @@ -637,6 +643,10 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn destructor(self, interner: I) -> Option; } +pub trait FieldId: Copy + Debug + Hash + Eq { + fn ty(self, interner: I, ty: I::Ty) -> I::Ty; +} + pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { fn caller_bounds(self) -> impl SliceLike; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 03cf738c05987..4a66deea813cf 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -211,6 +211,8 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; + type FieldId: FieldId; + fn alias_ty_kind(self, alias: ty::AliasTy) -> ty::AliasTyKind; fn alias_term_kind(self, alias: ty::AliasTerm) -> ty::AliasTermKind; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 39b575ebab63b..f1c45a4d98b5e 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..aa39f5344ba7f 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -198,6 +198,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 3610605462ba9..c0f2621c07780 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -511,6 +511,15 @@ pub fn structurally_relate_tys>( Ok(Ty::new_pat(cx, ty, pat)) } + (ty::FRT(a_ty, a_field), ty::FRT(b_ty, b_field)) => { + let ty = relation.relate(a_ty, b_ty)?; + if a_field == b_field { + Ok(Ty::new_field_representing_type(cx, ty, a_field)) + } else { + Err(TypeError::Mismatch) + } + } + (ty::UnsafeBinder(a_binder), ty::UnsafeBinder(b_binder)) => { Ok(Ty::new_unsafe_binder(cx, relation.binders(*a_binder, *b_binder)?)) } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 498797bef653a..addbd2886dc63 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -106,6 +106,9 @@ pub enum TyKind { /// Only supports integer range patterns for now. Pat(I::Ty, I::Pat), + /// Field representing type (`field_of!(Struct, field)`). + FRT(I::Ty, I::FieldId), + /// The pointee of an array slice. Written as `[T]`. Slice(I::Ty), @@ -298,6 +301,7 @@ impl TyKind { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -350,6 +354,7 @@ impl fmt::Debug for TyKind { Str => write!(f, "str"), Array(t, c) => write!(f, "[{t:?}; {c:?}]"), Pat(t, p) => write!(f, "pattern_type!({t:?} is {p:?})"), + FRT(t, i) => write!(f, "field_of!({t:?}, {i:?})"), Slice(t) => write!(f, "[{:?}]", &t), RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index e48d598a5328d..a07fde2d00975 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -92,6 +92,9 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg push_ty_pat::(stack, pat); stack.push(ty.into()); } + ty::FRT(ty, _) => { + stack.push(ty.into()); + } ty::Array(ty, len) => { stack.push(len.into()); stack.push(ty.into()); diff --git a/library/core/src/field.rs b/library/core/src/field.rs new file mode 100644 index 0000000000000..66ae7f3a85c9b --- /dev/null +++ b/library/core/src/field.rs @@ -0,0 +1,35 @@ +//! Field Reflection + +/// Type representing a field of a `struct`, `union`, `enum` variant 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 = "field_base"] + type Base; + + /// The type of the field. + #[lang = "field_type"] + type Type; + + /// The offset of the field in bytes. + #[lang = "field_offset"] + const OFFSET: usize = crate::intrinsics::field_offset::(); +} + +/// Expands to the field representing type of the given field. +/// +/// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the +/// variant must also be specified. Only a single field is supported. +#[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 8d112b4c5d187..c0906c16af11c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2806,6 +2806,20 @@ pub const fn align_of() -> usize; #[lang = "offset_of"] pub const fn offset_of(variant: u32, field: u32) -> usize; +/// The offset of a field queried by its field representing type. +/// +/// Returns the offset of the field represented by `F`. This function essentially does the same as +/// the [`offset_of`] intrinsic, but expects the field to be represented by a generic rather than +/// the variant and field indices. This also is a safe intrinsic and can only be evaluated at +/// compile-time, so it should only appear in constants or inline const blocks. +/// +/// There should be no need to call this intrinsic manually, as its value is used to define +/// [`Field::OFFSET`](crate::field::Field::OFFSET), which is publicly accessible. +#[rustc_intrinsic] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_const_unstable(feature = "field_projections", issue = "145383")] +pub const fn field_offset() -> usize; + /// Returns the number of variants of the type `T` cast to a `usize`; /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index dfc9a7b5dbc32..e3d3d07037efe 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)] @@ -312,6 +313,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 f5b9f69a5f9f9..26c4094f3c615 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -493,6 +493,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/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0ad9d2fdbe804..b5572df678f97 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -46,7 +46,9 @@ use rustc_hir::{LangItem, PredicateOrigin, find_attr}; use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{ + self, AdtKind, FieldIdData, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode, +}; use rustc_middle::{bug, span_bug}; use rustc_span::ExpnKind; use rustc_span::hygiene::{AstPass, MacroKind}; @@ -1808,6 +1810,14 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), + TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => { + let field_str = if let Some(variant) = variant { + format!("{variant}.{field}") + } else { + format!("{field}") + }; + Type::FRT(Box::new(clean_ty(ty, cx)), field_str.into()) + } TyKind::Array(ty, const_arg) => { // NOTE(min_const_generics): We can't use `const_eval_poly` for constants // as we currently do not supply the parent generics to anonymous constants @@ -2049,6 +2059,35 @@ pub(crate) fn clean_middle_ty<'tcx>( Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), format!("{pat:?}").into_boxed_str(), ), + ty::FRT(ty, field) => { + let field_str = match *field.0 { + FieldIdData::Resolved { variant, field } => match ty.kind() { + ty::Adt(def, _) => { + if def.is_enum() { + let variant = &def.variants()[variant]; + format!("{}.{}", variant.name, variant.fields[field].name) + } else { + format!("{}", def.non_enum_variant().fields[field].name) + } + } + ty::Tuple(_) => { + format!("{}", field.index()) + } + _ => bug!("unexpected ty in resolved FRT: {ty}"), + }, + FieldIdData::Unresolved { variant, field, .. } => { + if let Some(variant) = variant { + format!("{variant}.{field}") + } else { + format!("{field}") + } + } + }; + Type::FRT( + Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), + field_str.into_boxed_str(), + ) + } ty::Array(ty, n) => { let n = cx.tcx.normalize_erasing_regions(cx.typing_env(), n); let n = print_const(cx, n); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c2e63f475bec4..cce2a9b19cb54 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1314,6 +1314,7 @@ pub(crate) enum Type { /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), Pat(Box, Box), + FRT(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` @@ -1527,6 +1528,7 @@ impl Type { Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, + Type::FRT(..) => PrimitiveType::FRT, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, @@ -1574,6 +1576,7 @@ pub(crate) enum PrimitiveType { Slice, Array, Pat, + FRT, Tuple, Unit, RawPointer, @@ -1729,6 +1732,7 @@ impl PrimitiveType { Char => sym::char, Array => sym::array, Pat => sym::pat, + FRT => sym::field_of, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index eee13ff2b0dc0..8d1bca67bd766 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -958,6 +958,11 @@ fn fmt_type( fmt::Display::fmt(&print_type(t, cx), f)?; write!(f, " is {pat}") } + clean::Type::FRT(t, field) => { + write!(f, "field_of!(")?; + fmt::Display::fmt(&print_type(t, cx), f)?; + write!(f, ", {field})") + } clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( f, PrimitiveType::Array, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 12b207dda5693..e738b4b605083 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2041,6 +2041,7 @@ fn get_index_type_id( } // Not supported yet clean::Type::Pat(..) + | clean::Type::FRT(..) | clean::Generic(_) | clean::SelfTy | clean::ImplTrait(_) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 892cc483dbd6f..11c94ebfebe3b 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -579,6 +579,8 @@ impl FromClean for Type { type_: Box::new(t.into_json(renderer)), __pat_unstable_do_not_use: p.to_string(), }, + // FIXME(FRTs): implement + clean::Type::FRT(..) => todo!(), ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index debea4fc0cec6..a63a8ec518dab 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -526,6 +526,7 @@ fn ty_to_res<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), ty::Tuple(_) => Res::Primitive(Tuple), ty::Pat(..) => Res::Primitive(Pat), + ty::FRT(..) => Res::Primitive(FRT), ty::Array(..) => Res::Primitive(Array), ty::Slice(_) => Res::Primitive(Slice), ty::RawPtr(_, _) => Res::Primitive(RawPointer), diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 32fd4afb122e6..2bdbdfd905257 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -855,6 +855,7 @@ impl TyCoercionStability { | TyKind::Ptr(_) | TyKind::FnPtr(_) | TyKind::Pat(..) + | TyKind::FieldOf(..) | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) => Self::Deref, @@ -907,6 +908,7 @@ impl TyCoercionStability { | ty::Uint(_) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Float(_) | ty::RawPtr(..) | ty::FnPtr(..) 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 7fb8616072a59..7d50466c86605 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -532,6 +532,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { // experimental | TyKind::Pat(..) + | TyKind::FieldOf(..) // unused | TyKind::CVarArgs diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index c7bb3a064a093..9370aa0a5e883 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -14,7 +14,7 @@ use rustc_hir::{ GenericParam, GenericParamKind, GenericParamSource, Generics, HirId, HirIdMap, InlineAsmOperand, ItemId, ItemKind, LetExpr, Lifetime, LifetimeKind, LifetimeParamKind, Node, ParamName, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PreciseCapturingArgKind, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, - TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind, + TyFieldPath, TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::LateContext; @@ -689,9 +689,12 @@ impl HirEqInterExpr<'_, '_, '_> { (ConstArgKind::Literal(kind_l), ConstArgKind::Literal(kind_r)) => kind_l == kind_r, (ConstArgKind::Array(l_arr), ConstArgKind::Array(r_arr)) => { l_arr.elems.len() == r_arr.elems.len() - && l_arr.elems.iter().zip(r_arr.elems.iter()) - .all(|(l_elem, r_elem)| self.eq_const_arg(l_elem, r_elem)) - } + && l_arr + .elems + .iter() + .zip(r_arr.elems.iter()) + .all(|(l_elem, r_elem)| self.eq_const_arg(l_elem, r_elem)) + }, // Use explicit match for now since ConstArg is undergoing flux. ( ConstArgKind::Path(..) @@ -1508,6 +1511,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_ty_pat(pat); }, + TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + self.hash_ty(ty); + if let Some(variant) = variant { + self.hash_name(variant.name); + } + self.hash_name(field.name); + }, TyKind::Ptr(mut_ty) => { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 2d7bc59c62788..998b9f3e08bec 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1035,6 +1035,12 @@ impl Rewrite for ast::Ty { let pat = pat.rewrite_result(context, shape)?; Ok(format!("{ty} is {pat}")) } + ast::TyKind::FieldOf(ref ty, ref fields) => { + let ty = ty.rewrite_result(context, shape)?; + let fields = fields.iter().map(|f| format!("{f}")).collect::>(); + let fields = fields.join("."); + Ok(format!("field_of!({ty}, {fields})",)) + } ast::TyKind::UnsafeBinder(ref binder) => { let mut result = String::new(); if binder.generic_params.is_empty() { diff --git a/tests/debuginfo/function-names.rs b/tests/debuginfo/function-names.rs index e2f106e565439..abba8814c6663 100644 --- a/tests/debuginfo/function-names.rs +++ b/tests/debuginfo/function-names.rs @@ -39,7 +39,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>(); @@ -85,6 +85,7 @@ use std::ops::Coroutine; use std::pin::Pin; + use Mod1::TestTrait2; fn main() { 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_representing_types/auxiliary/extern-crate.rs b/tests/ui/field_representing_types/auxiliary/extern-crate.rs new file mode 100644 index 0000000000000..8e7e6d2679b44 --- /dev/null +++ b/tests/ui/field_representing_types/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_representing_types/bad-next.rs b/tests/ui/field_representing_types/bad-next.rs new file mode 100644 index 0000000000000..fa7e98346ffb0 --- /dev/null +++ b/tests/ui/field_representing_types/bad-next.rs @@ -0,0 +1,43 @@ +//@ compile-flags: -Znext-solver +#![feature(field_projections, freeze)] +#![expect(incomplete_features, dead_code)] +use std::field::field_of; + +struct Struct { + field: i32, +} + +trait WithAssoc { + type Assoc: Special; +} + +trait Special { + type Assoc; +} + +trait Constraint: WithAssoc {} + +impl Special for Struct { + type Assoc = Self; +} + +fn works(_: field_of!(<::Assoc as Special>::Assoc, field)) {} + +// FIXME(FRTs): correct the error printing for infer vars +fn fails1(_x: field_of!(<::Assoc as Special>::Assoc, field)) {} +//~^ ERROR: cannot use `_` in this position + +fn fails2() { + let _: field_of!(<::Assoc as Special>::Assoc, field); + // last time I checked this, this line above produced an ICE due to a `delay_bug` +} + +fn invalid_field() { + // FIXME(FRTs): don't report the error multiple times? + let _: field_of!(<::Assoc as Special>::Assoc, other); + //~^ ERROR: no field `other` on struct `Struct` + //~^^ ERROR: no field `other` on struct `Struct` + //~^^^ ERROR: no field `other` on struct `Struct` +} + +fn main() {} diff --git a/tests/ui/field_representing_types/bad-next.stderr b/tests/ui/field_representing_types/bad-next.stderr new file mode 100644 index 0000000000000..b0b6e711d1775 --- /dev/null +++ b/tests/ui/field_representing_types/bad-next.stderr @@ -0,0 +1,31 @@ +error: cannot use `_` in this position + --> $DIR/bad-next.rs:27:40 + | +LL | fn fails1(_x: field_of!(<::Assoc as Special>::Assoc, field)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0609]: no field `other` on struct `Struct` + --> $DIR/bad-next.rs:37:67 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^ unknown field + +error[E0609]: no field `other` on struct `Struct` + --> $DIR/bad-next.rs:37:67 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^ unknown field + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0609]: no field `other` on struct `Struct` + --> $DIR/bad-next.rs:37:67 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^ unknown field + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/field_representing_types/bad-old.rs b/tests/ui/field_representing_types/bad-old.rs new file mode 100644 index 0000000000000..4f795c5faf052 --- /dev/null +++ b/tests/ui/field_representing_types/bad-old.rs @@ -0,0 +1,38 @@ +#![feature(field_projections, freeze)] +#![expect(incomplete_features, dead_code)] +use std::field::field_of; + +struct Struct { + field: i32, +} + +trait WithAssoc { + type Assoc: Special; +} + +trait Special { + type Assoc; +} + +trait Constraint: WithAssoc {} + +impl Special for Struct { + type Assoc = Self; +} + +fn works(_: field_of!(<::Assoc as Special>::Assoc, field)) {} + +fn fails1(_x: field_of!(<::Assoc as Special>::Assoc, field)) {} + +fn fails2() { + let _: field_of!(<::Assoc as Special>::Assoc, field); +} + +fn invalid_field() { + // FIXME(FRTs): don't report the error multiple times? + let _: field_of!(<::Assoc as Special>::Assoc, other); + //~^ ERROR: no field `other` on struct `Struct` + //~^^ ERROR: no field `other` on struct `Struct` +} + +fn main() {} diff --git a/tests/ui/field_representing_types/bad-old.stderr b/tests/ui/field_representing_types/bad-old.stderr new file mode 100644 index 0000000000000..de8b855bada38 --- /dev/null +++ b/tests/ui/field_representing_types/bad-old.stderr @@ -0,0 +1,17 @@ +error[E0609]: no field `other` on struct `Struct` + --> $DIR/bad-old.rs:33:67 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^ unknown field + +error[E0609]: no field `other` on struct `Struct` + --> $DIR/bad-old.rs:33:67 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^ unknown field + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/field_representing_types/incoherent-impl.next.stderr b/tests/ui/field_representing_types/incoherent-impl.next.stderr new file mode 100644 index 0000000000000..8e5450eb47bcb --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.next.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:22:1 + | +LL | impl 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:25: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 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_representing_types/incoherent-impl.old.stderr b/tests/ui/field_representing_types/incoherent-impl.old.stderr new file mode 100644 index 0000000000000..8e5450eb47bcb --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.old.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:22:1 + | +LL | impl 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:25: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 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_representing_types/incoherent-impl.rs b/tests/ui/field_representing_types/incoherent-impl.rs new file mode 100644 index 0000000000000..38969222f66d1 --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.rs @@ -0,0 +1,28 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ aux-build:extern-crate.rs +#![expect(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 MyTrait for field_of!(Player, pos) {} +impl MyTrait for field_of!((usize, usize), 0) {} + +pub struct Player { + pos: Point, +} + +impl ForeignTrait for field_of!(Point, x) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] +impl ForeignTrait for field_of!(Player, pos) {} +impl ForeignTrait for field_of!((usize, usize), 0) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] + +fn main() {} diff --git a/tests/ui/field_representing_types/invalid.next.stderr b/tests/ui/field_representing_types/invalid.next.stderr new file mode 100644 index 0000000000000..dccedb4fc4a91 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.next.stderr @@ -0,0 +1,31 @@ +error: unexpected end of macro invocation + --> $DIR/invalid.rs:23:28 + | +LL | let _: field_of!(Struct); + | ^ missing tokens in macro arguments + | +note: while trying to match `,` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: no rules expected `extra` + --> $DIR/invalid.rs:24:37 + | +LL | let _: field_of!(Struct, field, extra); + | ^^^^^ no rules expected this token in macro call + | + = note: while trying to match sequence end + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:26:28 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^ + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:27:30 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/field_representing_types/invalid.old.stderr b/tests/ui/field_representing_types/invalid.old.stderr new file mode 100644 index 0000000000000..dccedb4fc4a91 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.old.stderr @@ -0,0 +1,31 @@ +error: unexpected end of macro invocation + --> $DIR/invalid.rs:23:28 + | +LL | let _: field_of!(Struct); + | ^ missing tokens in macro arguments + | +note: while trying to match `,` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: no rules expected `extra` + --> $DIR/invalid.rs:24:37 + | +LL | let _: field_of!(Struct, field, extra); + | ^^^^^ no rules expected this token in macro call + | + = note: while trying to match sequence end + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:26:28 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^ + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:27:30 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/field_representing_types/invalid.rs b/tests/ui/field_representing_types/invalid.rs new file mode 100644 index 0000000000000..2a79c4c52a0f5 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.rs @@ -0,0 +1,28 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +pub struct Struct { + field: i32, +} + +pub union Union { + field: i32, +} + +pub enum Enum { + Variant { field: i32 }, +} + +pub trait Trait {} + +fn main() { + let _: field_of!(Struct); //~ ERROR: unexpected end of macro invocation + let _: field_of!(Struct, field, extra); //~ ERROR: no rules expected `extra` + // FIXME(FRTs): adjust error message to mention `field_of!` + let _: field_of!(Enum, Variant..field); //~ ERROR: offset_of expects dot-separated field and variant names + let _: field_of!(Struct, [42]); //~ ERROR: offset_of expects dot-separated field and variant names +} diff --git a/tests/ui/field_representing_types/nonexistent.next.stderr b/tests/ui/field_representing_types/nonexistent.next.stderr new file mode 100644 index 0000000000000..73ac42db9c211 --- /dev/null +++ b/tests/ui/field_representing_types/nonexistent.next.stderr @@ -0,0 +1,124 @@ +error[E0573]: expected type, found function `main` + --> $DIR/nonexistent.rs:43:22 + | +LL | let _: field_of!(main, field); + | ^^^^ not a type + +error[E0609]: no field `other` on struct `Struct` + --> $DIR/nonexistent.rs:23:30 + | +LL | let _: field_of!(Struct, other); + | ^^^^^ unknown field + +error[E0609]: no field `0` on struct `Struct` + --> $DIR/nonexistent.rs:24:30 + | +LL | let _: field_of!(Struct, 0); + | ^ unknown field + +error[E0609]: no field `other` on union `Union` + --> $DIR/nonexistent.rs:25:29 + | +LL | let _: field_of!(Union, other); + | ^^^^^ unknown field + +error[E0609]: no field `0` on union `Union` + --> $DIR/nonexistent.rs:26:29 + | +LL | let _: field_of!(Union, 0); + | ^ unknown field + +error[E0609]: no field `other` on enum `Enum` + --> $DIR/nonexistent.rs:28:36 + | +LL | let _: field_of!(Enum, Variant.other); + | ^^^^^ unknown field + +error[E0609]: no field `0` on enum `Enum` + --> $DIR/nonexistent.rs:29:36 + | +LL | let _: field_of!(Enum, Variant.0); + | ^ unknown field + +error[E0001]: no variant `OtherVariant` on enum `Enum` + --> $DIR/nonexistent.rs:31:28 + | +LL | let _: field_of!(Enum, OtherVariant.field); + | ^^^^^^^^^^^^ unknown variant + +error[E0001]: no variant `OtherVariant` on enum `Enum` + --> $DIR/nonexistent.rs:32:28 + | +LL | let _: field_of!(Enum, OtherVariant.0); + | ^^^^^^^^^^^^ unknown variant + +error[E0609]: no field `2` on type `((), ())` + --> $DIR/nonexistent.rs:33:32 + | +LL | let _: field_of!(((), ()), 2); + | ^ unknown field + +error[E0609]: no field `field` on type `((), ())` + --> $DIR/nonexistent.rs:34:32 + | +LL | let _: field_of!(((), ()), field); + | ^^^^^ unknown field + +error: type `i32` doesn't have fields + --> $DIR/nonexistent.rs:36:22 + | +LL | let _: field_of!(i32, field); + | ^^^ + +error: type `[Struct]` doesn't have fields + --> $DIR/nonexistent.rs:37:22 + | +LL | let _: field_of!([Struct], field); + | ^^^^^^^^ + +error: type `[Struct; 42]` is not yet supported in `field_of!` + --> $DIR/nonexistent.rs:38:22 + | +LL | let _: field_of!([Struct; 42], field); + | ^^^^^^^^^^^^ + +error: type `&'static Struct` doesn't have fields + --> $DIR/nonexistent.rs:39:22 + | +LL | let _: field_of!(&'static Struct, field); + | ^^^^^^^^^^^^^^^ + +error: type `*const Struct` doesn't have fields + --> $DIR/nonexistent.rs:40:22 + | +LL | let _: field_of!(*const Struct, field); + | ^^^^^^^^^^^^^ + +error: type `fn() -> Struct` doesn't have fields + --> $DIR/nonexistent.rs:41:22 + | +LL | let _: field_of!(fn() -> Struct, field); + | ^^^^^^^^^^^^^^ + +error: type `dyn Trait` doesn't have fields + --> $DIR/nonexistent.rs:42:22 + | +LL | let _: field_of!(dyn Trait, field); + | ^^^^^^^^^ + +error: cannot use `_` in this position + --> $DIR/nonexistent.rs:44:22 + | +LL | let _: field_of!(_, field); + | ^ + +error: type `T` doesn't have fields + --> $DIR/nonexistent.rs:48:22 + | +LL | let _: field_of!(T, field); + | ^ + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0001, E0573, E0609. +For more information about an error, try `rustc --explain E0001`. diff --git a/tests/ui/field_representing_types/nonexistent.old.stderr b/tests/ui/field_representing_types/nonexistent.old.stderr new file mode 100644 index 0000000000000..73ac42db9c211 --- /dev/null +++ b/tests/ui/field_representing_types/nonexistent.old.stderr @@ -0,0 +1,124 @@ +error[E0573]: expected type, found function `main` + --> $DIR/nonexistent.rs:43:22 + | +LL | let _: field_of!(main, field); + | ^^^^ not a type + +error[E0609]: no field `other` on struct `Struct` + --> $DIR/nonexistent.rs:23:30 + | +LL | let _: field_of!(Struct, other); + | ^^^^^ unknown field + +error[E0609]: no field `0` on struct `Struct` + --> $DIR/nonexistent.rs:24:30 + | +LL | let _: field_of!(Struct, 0); + | ^ unknown field + +error[E0609]: no field `other` on union `Union` + --> $DIR/nonexistent.rs:25:29 + | +LL | let _: field_of!(Union, other); + | ^^^^^ unknown field + +error[E0609]: no field `0` on union `Union` + --> $DIR/nonexistent.rs:26:29 + | +LL | let _: field_of!(Union, 0); + | ^ unknown field + +error[E0609]: no field `other` on enum `Enum` + --> $DIR/nonexistent.rs:28:36 + | +LL | let _: field_of!(Enum, Variant.other); + | ^^^^^ unknown field + +error[E0609]: no field `0` on enum `Enum` + --> $DIR/nonexistent.rs:29:36 + | +LL | let _: field_of!(Enum, Variant.0); + | ^ unknown field + +error[E0001]: no variant `OtherVariant` on enum `Enum` + --> $DIR/nonexistent.rs:31:28 + | +LL | let _: field_of!(Enum, OtherVariant.field); + | ^^^^^^^^^^^^ unknown variant + +error[E0001]: no variant `OtherVariant` on enum `Enum` + --> $DIR/nonexistent.rs:32:28 + | +LL | let _: field_of!(Enum, OtherVariant.0); + | ^^^^^^^^^^^^ unknown variant + +error[E0609]: no field `2` on type `((), ())` + --> $DIR/nonexistent.rs:33:32 + | +LL | let _: field_of!(((), ()), 2); + | ^ unknown field + +error[E0609]: no field `field` on type `((), ())` + --> $DIR/nonexistent.rs:34:32 + | +LL | let _: field_of!(((), ()), field); + | ^^^^^ unknown field + +error: type `i32` doesn't have fields + --> $DIR/nonexistent.rs:36:22 + | +LL | let _: field_of!(i32, field); + | ^^^ + +error: type `[Struct]` doesn't have fields + --> $DIR/nonexistent.rs:37:22 + | +LL | let _: field_of!([Struct], field); + | ^^^^^^^^ + +error: type `[Struct; 42]` is not yet supported in `field_of!` + --> $DIR/nonexistent.rs:38:22 + | +LL | let _: field_of!([Struct; 42], field); + | ^^^^^^^^^^^^ + +error: type `&'static Struct` doesn't have fields + --> $DIR/nonexistent.rs:39:22 + | +LL | let _: field_of!(&'static Struct, field); + | ^^^^^^^^^^^^^^^ + +error: type `*const Struct` doesn't have fields + --> $DIR/nonexistent.rs:40:22 + | +LL | let _: field_of!(*const Struct, field); + | ^^^^^^^^^^^^^ + +error: type `fn() -> Struct` doesn't have fields + --> $DIR/nonexistent.rs:41:22 + | +LL | let _: field_of!(fn() -> Struct, field); + | ^^^^^^^^^^^^^^ + +error: type `dyn Trait` doesn't have fields + --> $DIR/nonexistent.rs:42:22 + | +LL | let _: field_of!(dyn Trait, field); + | ^^^^^^^^^ + +error: cannot use `_` in this position + --> $DIR/nonexistent.rs:44:22 + | +LL | let _: field_of!(_, field); + | ^ + +error: type `T` doesn't have fields + --> $DIR/nonexistent.rs:48:22 + | +LL | let _: field_of!(T, field); + | ^ + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0001, E0573, E0609. +For more information about an error, try `rustc --explain E0001`. diff --git a/tests/ui/field_representing_types/nonexistent.rs b/tests/ui/field_representing_types/nonexistent.rs new file mode 100644 index 0000000000000..bae92a8e515bf --- /dev/null +++ b/tests/ui/field_representing_types/nonexistent.rs @@ -0,0 +1,49 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +pub struct Struct { + field: i32, +} + +pub union Union { + field: i32, +} + +pub enum Enum { + Variant { field: i32 }, +} + +pub trait Trait {} + +fn main() { + let _: field_of!(Struct, other); //~ ERROR: no field `other` on struct `Struct` [E0609] + let _: field_of!(Struct, 0); //~ ERROR: no field `0` on struct `Struct` [E0609] + let _: field_of!(Union, other); //~ ERROR: no field `other` on union `Union` [E0609] + let _: field_of!(Union, 0); //~ ERROR: no field `0` on union `Union` [E0609] + // FIXME(FRTs): make the error mention the variant too. + let _: field_of!(Enum, Variant.other); //~ ERROR: no field `other` on enum `Enum` [E0609] + let _: field_of!(Enum, Variant.0); //~ ERROR: no field `0` on enum `Enum` [E0609] + // FIXME(FRTs): select correct error code + let _: field_of!(Enum, OtherVariant.field); //~ ERROR: no variant `OtherVariant` on enum `Enum` + let _: field_of!(Enum, OtherVariant.0); //~ ERROR: no variant `OtherVariant` on enum `Enum` + let _: field_of!(((), ()), 2); //~ ERROR: no field `2` on type `((), ())` [E0609] + let _: field_of!(((), ()), field); //~ ERROR: no field `field` on type `((), ())` [E0609] + + let _: field_of!(i32, field); //~ ERROR: type `i32` doesn't have fields + let _: field_of!([Struct], field); //~ ERROR: type `[Struct]` doesn't have fields + let _: field_of!([Struct; 42], field); //~ ERROR: type `[Struct; 42]` is not yet supported in `field_of!` + let _: field_of!(&'static Struct, field); //~ ERROR: type `&'static Struct` doesn't have fields + let _: field_of!(*const Struct, field); //~ ERROR: type `*const Struct` doesn't have fields + let _: field_of!(fn() -> Struct, field); //~ ERROR: type `fn() -> Struct` doesn't have fields + let _: field_of!(dyn Trait, field); //~ ERROR: type `dyn Trait` doesn't have fields + let _: field_of!(main, field); //~ ERROR: expected type, found function `main` + let _: field_of!(_, field); //~ ERROR: cannot use `_` in this position +} + +fn generic() { + let _: field_of!(T, field); //~ ERROR: type `T` doesn't have fields +} diff --git a/tests/ui/field_representing_types/not-field-if-packed.next.stderr b/tests/ui/field_representing_types/not-field-if-packed.next.stderr new file mode 100644 index 0000000000000..a4ff5875a87a1 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-packed.rs:15:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::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_representing_types/not-field-if-packed.old.stderr b/tests/ui/field_representing_types/not-field-if-packed.old.stderr new file mode 100644 index 0000000000000..a4ff5875a87a1 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.old.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-packed.rs:15:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::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_representing_types/not-field-if-packed.rs b/tests/ui/field_representing_types/not-field-if-packed.rs new file mode 100644 index 0000000000000..7391ef312ba2c --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.rs @@ -0,0 +1,17 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[repr(packed)] +pub struct MyStruct(usize); + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): improve this error message, point to the `repr(packed)` span. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied [E0277] +} diff --git a/tests/ui/field_representing_types/offset.rs b/tests/ui/field_representing_types/offset.rs new file mode 100644 index 0000000000000..b1c9d15005ed7 --- /dev/null +++ b/tests/ui/field_representing_types/offset.rs @@ -0,0 +1,50 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; +use std::ptr; + +#[repr(C)] +pub struct Struct { + a: i32, + b: i64, +} + +// FIXME(FRTs): need to mark these fields as used by the `field_of!` macro. +#[expect(dead_code)] +pub union Union { + a: i32, + b: i64, +} + +#[repr(C, i8)] +pub enum Enum { + A { a: i32, b: i64 }, + B { x: i64, y: i32 }, +} + +fn project_ref<'a, T, F: Field>(r: &'a T) -> &'a F::Type { + unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } +} + +fn main() { + assert_eq!(::OFFSET, 0); + assert_eq!(::OFFSET, 8); + + assert_eq!(::OFFSET, 0); + assert_eq!(::OFFSET, 0); + + assert_eq!(::OFFSET, 8); + assert_eq!(::OFFSET, 16); + assert_eq!(::OFFSET, 8); + assert_eq!(::OFFSET, 16); + let s = Struct { a: 42, b: 24 }; + let r = &s; + let a = project_ref::(r); + let b = project_ref::(r); + assert_eq!(*a, 42); + assert_eq!(*b, 24); +} diff --git a/tests/ui/field_representing_types/privacy.next.stderr b/tests/ui/field_representing_types/privacy.next.stderr new file mode 100644 index 0000000000000..9df19529d3fab --- /dev/null +++ b/tests/ui/field_representing_types/privacy.next.stderr @@ -0,0 +1,15 @@ +error[E0616]: field `a` of struct `Struct` is private + --> $DIR/privacy.rs:28:30 + | +LL | let _: field_of!(Struct, a); + | ^ private field + +error[E0616]: field `a` of union `foo::Union` is private + --> $DIR/privacy.rs:30:29 + | +LL | let _: field_of!(Union, a); + | ^ private field + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_representing_types/privacy.old.stderr b/tests/ui/field_representing_types/privacy.old.stderr new file mode 100644 index 0000000000000..9df19529d3fab --- /dev/null +++ b/tests/ui/field_representing_types/privacy.old.stderr @@ -0,0 +1,15 @@ +error[E0616]: field `a` of struct `Struct` is private + --> $DIR/privacy.rs:28:30 + | +LL | let _: field_of!(Struct, a); + | ^ private field + +error[E0616]: field `a` of union `foo::Union` is private + --> $DIR/privacy.rs:30:29 + | +LL | let _: field_of!(Union, a); + | ^ private field + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_representing_types/privacy.rs b/tests/ui/field_representing_types/privacy.rs new file mode 100644 index 0000000000000..a25be8e780bad --- /dev/null +++ b/tests/ui/field_representing_types/privacy.rs @@ -0,0 +1,34 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +mod foo { + pub struct Struct { + a: i32, + pub b: i32, + } + + pub union Union { + a: i32, + pub b: u32, + } + + pub enum Enum { + A { field: i32 }, + B(i32), + } +} + +use foo::{Enum, Struct, Union}; + +fn main() { + let _: field_of!(Struct, a); //~ ERROR: field `a` of struct `Struct` is private [E0616] + let _: field_of!(Struct, b); + let _: field_of!(Union, a); //~ ERROR: field `a` of union `foo::Union` is private [E0616] + let _: field_of!(Union, b); + let _: field_of!(Enum, A.field); + let _: field_of!(Enum, B.0); +} diff --git a/tests/ui/field_representing_types/traits.rs b/tests/ui/field_representing_types/traits.rs new file mode 100644 index 0000000000000..6b5bb15f9ee9a --- /dev/null +++ b/tests/ui/field_representing_types/traits.rs @@ -0,0 +1,29 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![feature(field_projections, freeze)] +#![expect(incomplete_features, dead_code)] +use std::field::field_of; +use std::marker::{Freeze, Unpin}; + +struct Struct { + field: u32, +} + +union Union { + field: u32, +} + +enum Enum { + Variant1 { field: u32 }, + Variant2(u32), +} + +fn assert_traits() {} + +fn main() { + assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); +} diff --git a/tests/ui/field_representing_types/weird-impls.next.stderr b/tests/ui/field_representing_types/weird-impls.next.stderr new file mode 100644 index 0000000000000..99a76545fa2a3 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.next.stderr @@ -0,0 +1,33 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/weird-impls.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: impls of auto traits for field representing types not supported + --> $DIR/weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^----^^^^^----------------------^^^ + | | | + | | field representing type + | auto trait + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:21:1 + | +LL | unsafe impl Field for field_of!(MyStruct2, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:30:1 + | +LL | unsafe impl Field for MyField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0120, E0322. +For more information about an error, try `rustc --explain E0120`. diff --git a/tests/ui/field_representing_types/weird-impls.old.stderr b/tests/ui/field_representing_types/weird-impls.old.stderr new file mode 100644 index 0000000000000..99a76545fa2a3 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.old.stderr @@ -0,0 +1,33 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/weird-impls.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: impls of auto traits for field representing types not supported + --> $DIR/weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^----^^^^^----------------------^^^ + | | | + | | field representing type + | auto trait + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:21:1 + | +LL | unsafe impl Field for field_of!(MyStruct2, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:30:1 + | +LL | unsafe impl Field for MyField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0120, E0322. +For more information about an error, try `rustc --explain E0120`. diff --git a/tests/ui/field_representing_types/weird-impls.rs b/tests/ui/field_representing_types/weird-impls.rs new file mode 100644 index 0000000000000..828daa4638182 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.rs @@ -0,0 +1,37 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(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) {} +} + +unsafe impl Send for field_of!(MyStruct, 0) {} +//~^ ERROR: impls of auto traits for field representing types not supported + +#[repr(packed)] +pub struct MyStruct2(usize); + +unsafe impl Field for field_of!(MyStruct2, 0) { + //~^ ERROR: explicit impls for the `Field` trait are not permitted [E0322] + type Base = MyStruct2; + type Type = usize; + const OFFSET: usize = 0; +} + +pub struct MyField; + +unsafe impl Field for MyField { + //~^ 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/reflection/dump.bit64.run.stdout b/tests/ui/reflection/dump.bit64.run.stdout index 681e81b71d56b..963462777e8b6 100644 --- a/tests/ui/reflection/dump.bit64.run.stdout +++ b/tests/ui/reflection/dump.bit64.run.stdout @@ -11,7 +11,7 @@ Type { offset: 1, }, Field { - ty: TypeId(0x41223169ff28813ba79b7268a2a968d9), + ty: TypeId(0x76973a66f24df6db7173e51528fa5bec), offset: 2, }, ], 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]