diff --git a/Cargo.lock b/Cargo.lock index d7d19be42c580..c8b9599a09534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4749,6 +4749,7 @@ dependencies = [ "ena", "indexmap", "rustc-hash 2.1.1", + "rustc_abi", "rustc_ast_ir", "rustc_data_structures", "rustc_error_messages", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 738435891f16f..4ec33cd71591a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2553,6 +2553,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, Option, Ident), /// 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 6265a06410d59..c871401ca6491 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1497,6 +1497,13 @@ 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, variant, field) => hir::TyKind::FieldOf( + self.lower_ty_alloc(ty, itctx), + self.arena.alloc(hir::TyFieldPath { + variant: variant.map(|variant| self.lower_ident(variant)), + field: self.lower_ident(*field), + }), + ), TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c8874ed99dca9..f4168301bc5df 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1377,6 +1377,23 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + ast::TyKind::FieldOf(ty, variant, field) => { + self.word("builtin # field_of"); + self.popen(); + let ib = self.ibox(0); + self.print_type(ty); + self.word(","); + self.space(); + + if let Some(variant) = variant { + self.print_ident(*variant); + self.word("."); + } + self.print_ident(*field); + + self.end(ib); + self.pclose(); + } } self.end(ib); } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e0c75da87a4b6..7dd7e00b213c6 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -13,7 +13,9 @@ 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, TypeVisitableExt}; +use rustc_middle::ty::{ + FieldInfo, FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt, +}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; @@ -227,6 +229,32 @@ 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, variant, field) = match frt_ty.kind() { + ty::Adt(def, args) + if let Some(FieldInfo { base, variant_idx, field_idx, .. }) = + def.field_representing_type_info(tcx, args) => + { + (base, variant_idx, field_idx) + } + 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 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); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index b810cd0db8cd1..5b07392e8fcca 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -221,6 +221,8 @@ declare_features! ( (internal, custom_mir, "1.65.0", None), /// Implementation details of externally implementable items (internal, eii_internals, "1.94.0", None), + /// Implementation details of field representing types. + (internal, field_representing_type_raw, "CURRENT_RUSTC_VERSION", None), /// Outputs useful `assert!` messages (unstable, generic_assert, "1.63.0", None), /// Allows using the #[rustc_intrinsic] attribute. @@ -492,6 +494,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)), /// Allows marking trait functions as `final` to prevent overriding impls (unstable, final_associated_functions, "CURRENT_RUSTC_VERSION", Some(1)), /// Controlling the behavior of fmt::Debug diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f1f7350c1e790..c177ec5aa47a4 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1737,6 +1737,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)] @@ -3800,6 +3806,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 21bcf53b5619f..e29fdd284b385 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 f57b1a9c8dea3..9ec681c374574 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -435,6 +435,13 @@ 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 representing types. + FieldRepresentingType, sym::field_representing_type, field_representing_type, Target::Struct, GenericRequirement::Exact(3); + 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 5669b6793add7..d77dfd24e1fa5 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 @@ -297,6 +298,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/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 40bdcc4352873..d21821add6dd6 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1446,6 +1446,15 @@ pub struct NoVariantNamed<'tcx> { pub ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag("no field `{$field}` on type `{$ty}`", code = E0609)] +pub struct NoFieldOnType<'tcx> { + #[primary_span] + pub span: Span, + pub ty: Ty<'tcx>, + pub field: Ident, +} + // FIXME(fmease): Deduplicate: #[derive(Diagnostic)] 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 3f82375308927..e3a1da25e8c3b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -21,6 +21,7 @@ pub mod generics; use std::slice; +use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -50,11 +51,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::check_c_variadic_abi; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoFieldOnType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; +use crate::{NoVariantNamed, check_c_variadic_abi}; /// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness /// trait or a default trait) @@ -3046,6 +3047,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.record_ty(pat.hir_id, ty, pat.span); pat_ty } + hir::TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => self.lower_field_of( + self.lower_ty(ty), + self.item_def_id(), + ty.span, + hir_ty.hir_id, + *variant, + *field, + ), hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; @@ -3087,6 +3096,159 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + fn lower_field_of( + &self, + ty: Ty<'tcx>, + item_def_id: LocalDefId, + ty_span: Span, + hir_id: HirId, + variant: Option, + field: Ident, + ) -> Ty<'tcx> { + let dcx = self.dcx(); + let tcx = self.tcx(); + 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 err = dcx + .create_err(NoVariantNamed { span: field.span, ident: field, ty }) + .with_span_help( + field.span.shrink_to_lo(), + "you might be missing a 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 err = dcx + .create_err(NoVariantNamed { span: variant.span, ident: variant, ty }) + .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, + E0609, + "{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, variant_idx, field_idx) + } else { + let err = + dcx.create_err(NoFieldOnType { span: ident.span, field: ident, ty }).emit(); + Ty::new_error(tcx, err) + } + } + ty::Tuple(tys) => { + let index = match field.as_str().parse::() { + Ok(idx) => idx, + Err(_) => { + let err = + dcx.create_err(NoFieldOnType { span: field.span, field, ty }).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, FIRST_VARIANT, index.into()) + } else { + let err = dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit(); + Ty::new_error(tcx, err) + } + } + // FIXME(FRTs): support type aliases + /* + ty::Alias(AliasTyKind::Free, ty) => { + return self.lower_field_of( + ty, + item_def_id, + ty_span, + hir_id, + variant, + field, + ); + }*/ + ty::Alias(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("could not resolve fields of `{ty}`")), + ), + ty::Error(err) => Ty::new_error(tcx, *err), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | 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!`")), + ), + } + } + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: Option) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index fc6cc4e672517..ec0688b64f3dd 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/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index e0467f4dc6ed2..788ece1cf02a2 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -490,15 +490,6 @@ impl HelpUseLatestEdition { } } -#[derive(Diagnostic)] -#[diag("no field `{$field}` on type `{$ty}`", code = E0609)] -pub(crate) struct NoFieldOnType<'tcx> { - #[primary_span] - pub(crate) span: Span, - pub(crate) ty: Ty<'tcx>, - pub(crate) field: Ident, -} - #[derive(Diagnostic)] #[diag("no field named `{$field}` on enum variant `{$container}::{$ident}`", code = E0609)] pub(crate) struct NoFieldOnVariant<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5b40531f94627..d35b16cac6f94 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -21,6 +21,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; use rustc_hir_analysis::NoVariantNamed; +use rustc_hir_analysis::errors::NoFieldOnType; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; @@ -44,7 +45,7 @@ use crate::coercion::CoerceMany; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, + FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 242d3742abad0..e893649a63380 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; @@ -16,6 +16,8 @@ use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_session::DataTypeKind; +use rustc_span::sym; +use rustc_type_ir::FieldInfo; use rustc_type_ir::solve::AdtDestructorKind; use tracing::{debug, info, trace}; @@ -24,8 +26,8 @@ use super::{ }; use crate::ich::StableHashingContext; use crate::mir::interpret::ErrorHandled; -use crate::ty; use crate::ty::util::{Discr, IntTypeExt}; +use crate::ty::{self, ConstKind}; #[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] pub struct AdtFlags(u16); @@ -59,6 +61,8 @@ bitflags::bitflags! { const IS_PIN = 1 << 11; /// Indicates whether the type is `#[pin_project]`. const IS_PIN_PROJECT = 1 << 12; + /// Indicates whether the type is `FieldRepresentingType`. + const IS_FIELD_REPRESENTING_TYPE = 1 << 13; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -201,6 +205,51 @@ impl<'tcx> AdtDef<'tcx> { pub fn repr(self) -> ReprOptions { self.0.0.repr } + + pub fn field_representing_type_info( + self, + tcx: TyCtxt<'tcx>, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Option>> { + if !self.is_field_representing_type() { + return None; + } + let base = args.type_at(0); + let variant_idx = match args.const_at(1).kind() { + ConstKind::Value(v) => VariantIdx::from_u32(v.to_leaf().to_u32()), + _ => return None, + }; + let field_idx = match args.const_at(2).kind() { + ConstKind::Value(v) => FieldIdx::from_u32(v.to_leaf().to_u32()), + _ => return None, + }; + let (ty, variant, name) = match base.kind() { + ty::Adt(base_def, base_args) => { + let variant = base_def.variant(variant_idx); + let field = &variant.fields[field_idx]; + (field.ty(tcx, base_args), base_def.is_enum().then_some(variant.name), field.name) + } + ty::Tuple(tys) => { + if variant_idx != FIRST_VARIANT { + bug!("expected variant of tuple to be FIRST_VARIANT, but found {variant_idx:?}") + } + ( + if let Some(ty) = tys.get(field_idx.index()) { + *ty + } else { + bug!( + "expected valid tuple index, but got {field_idx:?}, tuple length: {}", + tys.len() + ) + }, + None, + sym::integer(field_idx.index()), + ) + } + _ => panic!(), + }; + Some(FieldInfo { base, ty, variant, variant_idx, name, field_idx }) + } } impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { @@ -212,6 +261,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_struct() } + fn is_packed(self) -> bool { + self.repr().packed() + } + fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option>> { Some(interner.type_of(self.non_enum_variant().tail_opt()?.did)) } @@ -224,6 +277,14 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_manually_drop() } + fn field_representing_type_info( + self, + tcx: TyCtxt<'tcx>, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Option>> { + self.field_representing_type_info(tcx, args) + } + fn all_field_tys( self, tcx: TyCtxt<'tcx>, @@ -324,6 +385,9 @@ impl AdtDefData { if tcx.is_lang_item(did, LangItem::Pin) { flags |= AdtFlags::IS_PIN; } + if tcx.is_lang_item(did, LangItem::FieldRepresentingType) { + flags |= AdtFlags::IS_FIELD_REPRESENTING_TYPE; + } AdtDefData { did, variants, flags, repr } } @@ -452,6 +516,10 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_PIN_PROJECT) } + pub fn is_field_representing_type(self) -> bool { + self.flags().contains(AdtFlags::IS_FIELD_REPRESENTING_TYPE) + } + /// Returns `true` if this type has a destructor. pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool { self.destructor(tcx).is_some() diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 7580cc65d530a..d9ce2ed22a3f9 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -755,6 +755,8 @@ bidirectional_lang_item_map! { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -786,6 +788,7 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 02b804c1ab29c..a3fd19e3d8f54 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -17,7 +17,7 @@ use rustc_hir::limit::Limit; use rustc_macros::{Lift, extension}; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_span::{Ident, RemapPathScopeComponents, Symbol, kw, sym}; -use rustc_type_ir::{Upcast as _, elaborate}; +use rustc_type_ir::{FieldInfo, Upcast as _, elaborate}; use smallvec::SmallVec; // `pretty` is a separate module only for organization. @@ -793,6 +793,16 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { false => write!(self, "{}", self.tcx().item_name(def_id))?, }, }, + ty::Adt(def, args) + if let Some(FieldInfo { base, variant, name, .. }) = + def.field_representing_type_info(self.tcx(), args) => + { + if let Some(variant) = variant { + write!(self, "field_of!({base}, {variant}.{name})")?; + } else { + write!(self, "field_of!({base}, {name})")?; + } + } ty::Adt(def, args) => self.print_def_path(def.did(), args)?, ty::Dynamic(data, r) => { let print_r = self.should_print_optional_region(r); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4be30d8b6c918..4de05ac18ddaf 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, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty, - TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, Const, ConstKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, + Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, ValTree, + Value, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -487,6 +488,41 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, Pat(base, pat)) } + #[inline] + pub fn new_field_representing_type( + tcx: TyCtxt<'tcx>, + base: Ty<'tcx>, + variant: VariantIdx, + field: FieldIdx, + ) -> Ty<'tcx> { + let Some(did) = tcx.lang_items().field_representing_type() else { + bug!("could not locate the `FieldRepresentingType` lang item") + }; + let def = tcx.adt_def(did); + let args = tcx.mk_args(&[ + base.into(), + Const::new( + tcx, + ConstKind::Value(Value { + ty: tcx.types.u32, + valtree: ValTree::from_scalar_int(tcx, variant.as_u32().into()), + }), + ) + .into(), + Const::new( + tcx, + ConstKind::Value(Value { + ty: tcx.types.u32, + valtree: ValTree::from_scalar_int(tcx, field.as_u32().into()), + }), + ) + .into(), + ]); + args.const_at(1).to_leaf().to_u32(); + args.const_at(2).to_leaf().to_u32(); + Ty::new_adt(tcx, def, args) + } + #[inline] #[instrument(level = "debug", skip(tcx))] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { 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 63f246db8a5fb..038168ca638b4 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), } }; 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..f337715b9b8fc 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 @@ -7,7 +7,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; -use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; +use rustc_type_ir::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -950,6 +950,33 @@ 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::Adt(def, args) = self_ty.kind() else { + return Err(NoSolution); + }; + let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(ecx.cx(), args) + else { + return Err(NoSolution); + }; + + let ty = if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldBase) { + base + } else if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldType) { + ty + } 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 dd012a5146e61..cf320c39b3eb8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -8,7 +8,7 @@ use rustc_type_ir::solve::{ AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind, }; use rustc_type_ir::{ - self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, + self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace}; @@ -844,6 +844,28 @@ 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::Adt(def, args) = goal.predicate.self_ty().kind() + && let Some(FieldInfo { base, .. }) = def.field_representing_type_info(ecx.cx(), args) + && match base.kind() { + ty::Adt(def, _) => def.is_struct() && !def.is_packed(), + ty::Tuple(..) => true, + _ => false, + } + { + 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 diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5c44bfb0cf3f3..2d19652f2ddfd 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 6ff165eb22b71..eae129b22cfb0 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 @@ -803,6 +805,53 @@ 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(); + } + } + + match fields.len() { + 0 => Err(self.dcx().struct_span_err( + self.token.span, + "`field_of!` expects dot-separated field and variant names", + )), + 1 => Ok(TyKind::FieldOf(container, None, fields[0])), + 2 => Ok(TyKind::FieldOf(container, Some(fields[0]), fields[1])), + _ => Err(self.dcx().struct_span_err( + fields.iter().map(|f| f.span).collect::>(), + "`field_of!` only supports a single field or a variant with a field", + )), + } + } + /// Parses a function pointer type (`TyKind::FnPtr`). /// ```ignore (illustrative) /// [unsafe] [extern "ABI"] fn (S) -> T diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 23fbb9ab3a2b7..e424cc09fb607 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 ] ); @@ -688,6 +689,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { MacCall, CVarArgs, Dummy, + FieldOf, Err ] ); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5623b984b2420..0b44f166dad54 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1092,7 +1092,14 @@ symbols! { ffi_pure, ffi_returns_twice, field, + field_base, field_init_shorthand, + field_of, + field_offset, + field_projections, + field_representing_type, + field_representing_type_raw, + field_type, fields, file, file_options, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 5bbbad0b5bbfa..3df5c9e33438a 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -13,7 +13,7 @@ use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, + self, FieldInfo, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::sym; @@ -987,6 +987,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | LangItem::Future | LangItem::Iterator | LangItem::AsyncIterator + | LangItem::Field | LangItem::Fn | LangItem::FnMut | LangItem::FnOnce @@ -1547,6 +1548,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } }); (metadata_ty.into(), obligations) + } else if tcx.is_lang_item(trait_def_id, LangItem::Field) { + let ty::Adt(def, args) = self_ty.kind() else { + bug!("only field representing types can implement `Field`") + }; + let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(tcx, args) else { + bug!("only field representing types can implement `Field`") + }; + if tcx.is_lang_item(item_def_id, LangItem::FieldBase) { + (base.into(), PredicateObligations::new()) + } else if tcx.is_lang_item(item_def_id, LangItem::FieldType) { + (ty.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/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 935834b832f24..5849ce187bc2e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -14,7 +14,9 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate}; +use rustc_middle::ty::{ + self, FieldInfo, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate, +}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument, trace}; @@ -128,6 +130,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 @@ -1439,4 +1444,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_field_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if let ty::Adt(def, args) = obligation.predicate.self_ty().skip_binder().kind() + && let Some(FieldInfo { base, .. }) = def.field_representing_type_info(self.tcx(), args) + && match base.kind() { + ty::Adt(def, _) => def.is_struct() && !def.repr().packed(), + ty::Tuple(..) => true, + _ => false, + } + { + 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 b0e045274d0d8..9e7637df26490 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -262,6 +262,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some( LangItem::Destruct | LangItem::DiscriminantKind + | LangItem::Field | LangItem::FnPtrTrait | LangItem::PointeeTrait | LangItem::Tuple diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index a014098e7ed55..ac4137e9a6680 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -386,6 +386,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::Adt(def, _) if def.is_field_representing_type() => {} + _ => 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_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index fbe3827128930..28804c811f361 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -11,6 +11,7 @@ derive-where = "1.2.7" ena = "0.14.4" indexmap = "2.0.0" rustc-hash = "2.0.0" +rustc_abi = { path = "../rustc_abi", default-features = false } rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_error_messages = { path = "../rustc_error_messages", optional = true } @@ -33,6 +34,7 @@ nightly = [ "dep:rustc_macros", "dep:rustc_serialize", "dep:rustc_span", + "rustc_abi/nightly", "rustc_ast_ir/nightly", "rustc_index/nightly", "rustc_type_ir_macros/nightly", diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index c9580d589d217..8256ea7ed312e 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -13,7 +13,9 @@ use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::solve::{AdtDestructorKind, SizedTraitKind}; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{self as ty, ClauseKind, CollectAndApply, Interner, PredicateKind, UpcastFrom}; +use crate::{ + self as ty, ClauseKind, CollectAndApply, FieldInfo, Interner, PredicateKind, UpcastFrom, +}; pub trait Ty>: Copy @@ -577,6 +579,8 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_struct(self) -> bool; + fn is_packed(self) -> bool; + /// Returns the type of the struct tail. /// /// Expects the `AdtDef` to be a struct. If it is not, then this will panic. @@ -586,6 +590,12 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_manually_drop(self) -> bool; + fn field_representing_type_info( + self, + interner: I, + args: I::GenericArgs, + ) -> Option>; + // FIXME: perhaps use `all_fields` and expose `FieldDef`. fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; 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/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 3d9fc7f5c998d..6c1e0ae047a4e 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -13,6 +13,8 @@ extern crate self as rustc_type_ir; use std::fmt; use std::hash::Hash; +#[cfg(feature = "nightly")] +use rustc_abi::{FieldIdx, VariantIdx}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; @@ -440,3 +442,14 @@ impl fmt::Display for ClosureKind { self.as_str().fmt(f) } } + +pub struct FieldInfo { + pub base: I::Ty, + pub ty: I::Ty, + pub variant: Option, + #[cfg(feature = "nightly")] + pub variant_idx: VariantIdx, + pub name: I::Symbol, + #[cfg(feature = "nightly")] + pub field_idx: FieldIdx, +} diff --git a/library/core/src/field.rs b/library/core/src/field.rs new file mode 100644 index 0000000000000..73f08a443705c --- /dev/null +++ b/library/core/src/field.rs @@ -0,0 +1,59 @@ +//! Field Reflection + +use crate::marker::PhantomData; + +/// Field Representing Type +#[unstable(feature = "field_representing_type_raw", issue = "none")] +#[lang = "field_representing_type"] +#[expect(missing_debug_implementations)] +#[fundamental] +pub struct FieldRepresentingType { + _phantom: PhantomData, +} + +impl Copy + for FieldRepresentingType +{ +} + +impl Clone + for FieldRepresentingType +{ + fn clone(&self) -> Self { + *self + } +} + +/// 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(field_representing_type_raw, builtin_syntax)] +pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { + builtin # field_of($Container, $($fields)+) +} + +/// 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_dyn_incompatible_trait] +pub unsafe trait Field: Send + Sync + Copy + 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::(); +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 9d5f49c88295a..7cf9dabbd6b57 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2824,6 +2824,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 d650239a44c60..464b9b16cf65b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -138,6 +138,7 @@ #![feature(extern_types)] #![feature(f16)] #![feature(f128)] +#![feature(field_projections)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] @@ -277,6 +278,8 @@ pub mod cmp; pub mod convert; pub mod default; pub mod error; +#[unstable(feature = "field_projections", issue = "145383")] +pub mod field; pub mod index; pub mod marker; pub mod ops; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 03e3da013cd26..e5c834b8e0f7b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -497,6 +497,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 18f991ad43aa6..3314fdeed3d83 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1810,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::FieldOf(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 diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index bd6399adc3b88..75f2ded9bba2f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1348,6 +1348,7 @@ pub(crate) enum Type { /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), Pat(Box, Box), + FieldOf(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` @@ -1561,6 +1562,7 @@ impl Type { Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, + Type::FieldOf(..) => PrimitiveType::FieldOf, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, @@ -1608,6 +1610,7 @@ pub(crate) enum PrimitiveType { Slice, Array, Pat, + FieldOf, Tuple, Unit, RawPointer, @@ -1763,6 +1766,7 @@ impl PrimitiveType { Char => sym::char, Array => sym::array, Pat => sym::pat, + FieldOf => 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 7646375cbf7a3..8ac9b57cb9e88 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::FieldOf(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 cc27770c8aae8..6197ed9449746 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2050,6 +2050,7 @@ fn get_index_type_id( } // Not supported yet clean::Type::Pat(..) + | clean::Type::FieldOf(..) | clean::Generic(_) | clean::SelfTy | clean::ImplTrait(_) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 599d7b10005d9..1848c79322556 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::FieldOf(..) => todo!(), ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 32fd4afb122e6..e0520afe982a2 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, 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 def5d968b063f..14389b0d29bb4 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -531,6 +531,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 abc6885bef2d1..903b3ab9e180c 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; @@ -1529,6 +1529,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_ty_pat(pat); }, + TyKind::FieldOf(base, TyFieldPath { variant, field }) => { + self.hash_ty(base); + 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..d040a5ca5a3d6 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1035,6 +1035,14 @@ impl Rewrite for ast::Ty { let pat = pat.rewrite_result(context, shape)?; Ok(format!("{ty} is {pat}")) } + ast::TyKind::FieldOf(ref ty, ref variant, ref field) => { + let ty = ty.rewrite_result(context, shape)?; + if let Some(variant) = variant { + Ok(format!("field_of!({ty}, {variant}.{field})")) + } else { + Ok(format!("field_of!({ty}, {field})")) + } + } 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..3cc324be13ccf 100644 --- a/tests/debuginfo/function-names.rs +++ b/tests/debuginfo/function-names.rs @@ -85,6 +85,7 @@ use std::ops::Coroutine; use std::pin::Pin; + use Mod1::TestTrait2; fn main() { diff --git a/tests/ui/README.md b/tests/ui/README.md index 16cdde08431c0..7b43f080be456 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -601,6 +601,12 @@ See: - [`ffi_const` | The Unstable book](https://doc.rust-lang.org/unstable-book/language-features/ffi-const.html) - [`ffi_pure` | The Unstable book](https://doc.rust-lang.org/beta/unstable-book/language-features/ffi-pure.html) +## `tests/ui/field_representing_types`: `#![feature(field_projections)]` + +Tests for field representing types `field_of!(Struct, field)`. + +See: [Tracking Issue for Field Projections #145383](https://github.com/rust-lang/rust/issues/145383) + ## `tests/ui/float` See: [Tracking Issue for `f16` and `f128` float types #116909](https://github.com/rust-lang/rust/issues/116909) 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/feature-gates/feature-gate-field-representing-type-raw.rs b/tests/ui/feature-gates/feature-gate-field-representing-type-raw.rs new file mode 100644 index 0000000000000..a315c01c11b83 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-representing-type-raw.rs @@ -0,0 +1,4 @@ +use std::field::FieldRepresentingType; +//~^ ERROR: use of unstable library feature `field_representing_type_raw` [E0658] + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-field-representing-type-raw.stderr b/tests/ui/feature-gates/feature-gate-field-representing-type-raw.stderr new file mode 100644 index 0000000000000..fdb5e34d3497a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-representing-type-raw.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature `field_representing_type_raw` + --> $DIR/feature-gate-field-representing-type-raw.rs:1:5 + | +LL | use std::field::FieldRepresentingType; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(field_representing_type_raw)]` 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 1 previous error + +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/incoherent-impl.next.stderr b/tests/ui/field_representing_types/incoherent-impl.next.stderr new file mode 100644 index 0000000000000..47f91e8dd2e9d --- /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 types defined outside of the crate + --> $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 types defined outside of the crate + --> $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..47f91e8dd2e9d --- /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 types defined outside of the crate + --> $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 types defined outside of the crate + --> $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..22db652359f69 --- /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 types defined outside of the crate [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 types defined outside of the crate [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..2a5e884a5e3f9 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.next.stderr @@ -0,0 +1,71 @@ +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: unexpected end of macro invocation + --> $DIR/invalid.rs:24:29 + | +LL | let _: field_of!(Struct,); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:expr` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: no rules expected `extra` + --> $DIR/invalid.rs:25: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:27:28 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:27:12 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:29:30 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:29:12 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `field_of!` only supports a single field or a variant with a field + --> $DIR/invalid.rs:31:30 + | +LL | let _: field_of!(Struct, field1.field2.field3); + | ------------------^^^^^^-^^^^^^-^^^^^^- + | | + | in this macro invocation + | this macro call doesn't expand to a type + +error: aborting due to 8 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..2a5e884a5e3f9 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.old.stderr @@ -0,0 +1,71 @@ +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: unexpected end of macro invocation + --> $DIR/invalid.rs:24:29 + | +LL | let _: field_of!(Struct,); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:expr` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: no rules expected `extra` + --> $DIR/invalid.rs:25: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:27:28 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:27:12 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:29:30 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:29:12 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `field_of!` only supports a single field or a variant with a field + --> $DIR/invalid.rs:31:30 + | +LL | let _: field_of!(Struct, field1.field2.field3); + | ------------------^^^^^^-^^^^^^-^^^^^^- + | | + | in this macro invocation + | this macro call doesn't expand to a type + +error: aborting due to 8 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..d1fc217db7ddd --- /dev/null +++ b/tests/ui/field_representing_types/invalid.rs @@ -0,0 +1,32 @@ +//@ 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,); //~ 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!` & prevent double errors + let _: field_of!(Enum, Variant..field); //~ ERROR: offset_of expects dot-separated field and variant names + //~^ ERROR: `field_of!` expects dot-separated field and variant names + let _: field_of!(Struct, [42]); //~ ERROR: offset_of expects dot-separated field and variant names + //~^ ERROR: `field_of!` expects dot-separated field and variant names + let _: field_of!(Struct, field1.field2.field3); //~ ERROR: `field_of!` only supports a single field or a variant with a field +} diff --git a/tests/ui/field_representing_types/non-struct.next.stderr b/tests/ui/field_representing_types/non-struct.next.stderr new file mode 100644 index 0000000000000..7a6c33c3b6f54 --- /dev/null +++ b/tests/ui/field_representing_types/non-struct.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyUnion, field): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:22:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyUnion, field)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyEnum, A.a): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:25:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyEnum, A.a)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/non-struct.old.stderr b/tests/ui/field_representing_types/non-struct.old.stderr new file mode 100644 index 0000000000000..7a6c33c3b6f54 --- /dev/null +++ b/tests/ui/field_representing_types/non-struct.old.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyUnion, field): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:22:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyUnion, field)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyEnum, A.a): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:25:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyEnum, A.a)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/non-struct.rs b/tests/ui/field_representing_types/non-struct.rs new file mode 100644 index 0000000000000..50fd4b0e6b0e7 --- /dev/null +++ b/tests/ui/field_representing_types/non-struct.rs @@ -0,0 +1,27 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub union MyUnion { + field: u32, + other: i32, +} + +pub enum MyEnum { + A { a: i32, b: i64 }, + B { x: i64, y: i32 }, +} + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): improve this error message, point to the `union`. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyUnion, field): std::field::Field` is not satisfied [E0277] + // FIXME(FRTs): improve this error message, point to the `enum`. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyEnum, A.a): std::field::Field` is not satisfied [E0277] +} 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..76026ab4a496f --- /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 type `Struct` + --> $DIR/nonexistent.rs:23:30 + | +LL | let _: field_of!(Struct, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Struct` + --> $DIR/nonexistent.rs:24:30 + | +LL | let _: field_of!(Struct, 0); + | ^ + +error[E0609]: no field `other` on type `Union` + --> $DIR/nonexistent.rs:25:29 + | +LL | let _: field_of!(Union, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Union` + --> $DIR/nonexistent.rs:26:29 + | +LL | let _: field_of!(Union, 0); + | ^ + +error[E0609]: no field `other` on type `Enum` + --> $DIR/nonexistent.rs:28:36 + | +LL | let _: field_of!(Enum, Variant.other); + | ^^^^^ + +error[E0609]: no field `0` on type `Enum` + --> $DIR/nonexistent.rs:29:36 + | +LL | let _: field_of!(Enum, Variant.0); + | ^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:31:28 + | +LL | let _: field_of!(Enum, OtherVariant.field); + | ^^^^^^^^^^^^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:32:28 + | +LL | let _: field_of!(Enum, OtherVariant.0); + | ^^^^^^^^^^^^ + +error[E0609]: no field `2` on type `((), ())` + --> $DIR/nonexistent.rs:33:32 + | +LL | let _: field_of!(((), ()), 2); + | ^ + +error[E0609]: no field `field` on type `((), ())` + --> $DIR/nonexistent.rs:34:32 + | +LL | let _: field_of!(((), ()), 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: E0573, E0599, E0609. +For more information about an error, try `rustc --explain E0573`. 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..76026ab4a496f --- /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 type `Struct` + --> $DIR/nonexistent.rs:23:30 + | +LL | let _: field_of!(Struct, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Struct` + --> $DIR/nonexistent.rs:24:30 + | +LL | let _: field_of!(Struct, 0); + | ^ + +error[E0609]: no field `other` on type `Union` + --> $DIR/nonexistent.rs:25:29 + | +LL | let _: field_of!(Union, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Union` + --> $DIR/nonexistent.rs:26:29 + | +LL | let _: field_of!(Union, 0); + | ^ + +error[E0609]: no field `other` on type `Enum` + --> $DIR/nonexistent.rs:28:36 + | +LL | let _: field_of!(Enum, Variant.other); + | ^^^^^ + +error[E0609]: no field `0` on type `Enum` + --> $DIR/nonexistent.rs:29:36 + | +LL | let _: field_of!(Enum, Variant.0); + | ^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:31:28 + | +LL | let _: field_of!(Enum, OtherVariant.field); + | ^^^^^^^^^^^^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:32:28 + | +LL | let _: field_of!(Enum, OtherVariant.0); + | ^^^^^^^^^^^^ + +error[E0609]: no field `2` on type `((), ())` + --> $DIR/nonexistent.rs:33:32 + | +LL | let _: field_of!(((), ()), 2); + | ^ + +error[E0609]: no field `field` on type `((), ())` + --> $DIR/nonexistent.rs:34:32 + | +LL | let _: field_of!(((), ()), 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: E0573, E0599, E0609. +For more information about an error, try `rustc --explain E0573`. diff --git a/tests/ui/field_representing_types/nonexistent.rs b/tests/ui/field_representing_types/nonexistent.rs new file mode 100644 index 0000000000000..74083666af76d --- /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 type `Struct` [E0609] + let _: field_of!(Struct, 0); //~ ERROR: no field `0` on type `Struct` [E0609] + let _: field_of!(Union, other); //~ ERROR: no field `other` on type `Union` [E0609] + let _: field_of!(Union, 0); //~ ERROR: no field `0` on type `Union` [E0609] + // FIXME(FRTs): make the error mention the variant too. + let _: field_of!(Enum, Variant.other); //~ ERROR: no field `other` on type `Enum` [E0609] + let _: field_of!(Enum, Variant.0); //~ ERROR: no field `0` on type `Enum` [E0609] + // FIXME(FRTs): select correct error code + let _: field_of!(Enum, OtherVariant.field); //~ ERROR: no variant named `OtherVariant` found for enum `Enum` + let _: field_of!(Enum, OtherVariant.0); //~ ERROR: no variant named `OtherVariant` found for 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..8c769de884d29 --- /dev/null +++ b/tests/ui/field_representing_types/offset.rs @@ -0,0 +1,52 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; +use std::mem::offset_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, offset_of!(Struct, a)); + assert_eq!(::OFFSET, offset_of!(Struct, b)); + + let _: field_of!(Union, a); + let _: field_of!(Union, b); + + let _: field_of!(Enum, A.a); + let _: field_of!(Enum, A.b); + let _: field_of!(Enum, B.x); + let _: field_of!(Enum, B.y); + + 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..237a2cbc55ff7 --- /dev/null +++ b/tests/ui/field_representing_types/privacy.next.stderr @@ -0,0 +1,15 @@ +error[E0616]: field `a` of struct `foo::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..237a2cbc55ff7 --- /dev/null +++ b/tests/ui/field_representing_types/privacy.old.stderr @@ -0,0 +1,15 @@ +error[E0616]: field `a` of struct `foo::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..7805ff6721a6e --- /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 `foo::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/projections.next.stderr b/tests/ui/field_representing_types/projections.next.stderr new file mode 100644 index 0000000000000..bf3b41cc23a54 --- /dev/null +++ b/tests/ui/field_representing_types/projections.next.stderr @@ -0,0 +1,32 @@ +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:39:18 + | +LL | _: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:45:19 + | +LL | _x: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `::Assoc` + --> $DIR/projections.rs:24:22 + | +LL | let _: field_of!(::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:51:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:56:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/field_representing_types/projections.old.stderr b/tests/ui/field_representing_types/projections.old.stderr new file mode 100644 index 0000000000000..bf3b41cc23a54 --- /dev/null +++ b/tests/ui/field_representing_types/projections.old.stderr @@ -0,0 +1,32 @@ +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:39:18 + | +LL | _: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:45:19 + | +LL | _x: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `::Assoc` + --> $DIR/projections.rs:24:22 + | +LL | let _: field_of!(::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:51:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:56:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/field_representing_types/projections.rs b/tests/ui/field_representing_types/projections.rs new file mode 100644 index 0000000000000..625f22e25e991 --- /dev/null +++ b/tests/ui/field_representing_types/projections.rs @@ -0,0 +1,58 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![feature(field_projections, freeze)] +#![expect(incomplete_features, dead_code)] + +use std::field::field_of; + +struct Struct { + field: u32, +} + +type Alias = Struct; + +trait Trait { + type Assoc; +} + +impl Trait for Struct { + type Assoc = Struct; +} + +fn main() { + let _: field_of!(Alias, field); + let _: field_of!(::Assoc, field); + //~^ ERROR: could not resolve fields of `::Assoc` +} + +trait Special { + type Assoc; +} + +trait Constraint: Trait {} + +impl Special for Struct { + type Assoc = Self; +} + +fn with_constraint1( + _: field_of!(<::Assoc as Special>::Assoc, field), + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +) { +} + +fn with_constraint2( + _x: field_of!(<::Assoc as Special>::Assoc, field), + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +) { +} + +fn with_constraint3() { + let _: field_of!(<::Assoc as Special>::Assoc, field); + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +} + +fn with_constraint_invalid_field() { + let _: field_of!(<::Assoc as Special>::Assoc, other); + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +} 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..41cc0bc697689 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.next.stderr @@ -0,0 +1,30 @@ +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[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type defined in the current crate + --> $DIR/weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait for type in another crate + +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, E0321, 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..41cc0bc697689 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.old.stderr @@ -0,0 +1,30 @@ +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[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type defined in the current crate + --> $DIR/weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait for type in another crate + +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, E0321, 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..d61c0c7a6a056 --- /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: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type defined in the current crate [E0321] + +#[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() {}