diff --git a/Cargo.lock b/Cargo.lock index 88f37696210ac..e02c951785ab5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3470,6 +3470,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "serde", "tracing", ] @@ -4334,6 +4335,7 @@ dependencies = [ "rustc_target", "rustc_thread_pool", "rustc_type_ir", + "serde", "smallvec", "thin-vec", "tracing", diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 83d96d8d04daf..4776454204f32 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -15,6 +15,7 @@ rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } +serde = { version = "1.0.125", features = ["derive"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 41ad14f550ab0..2aec3b52cc58d 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -33,7 +33,7 @@ rustc_index::newtype_index! { /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, serde::Serialize)] #[encodable] #[orderable] pub struct FieldIdx {} @@ -57,7 +57,7 @@ rustc_index::newtype_index! { /// /// `struct`s, `tuples`, and `unions`s are considered to have a single variant /// with variant index zero, aka [`FIRST_VARIANT`]. - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, serde::Serialize)] #[encodable] #[orderable] pub struct VariantIdx { diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7c922417ee29f..f800ef76f977c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2555,6 +2555,11 @@ pub enum TyKind { /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. Pat(Box, Box), + /// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`). + /// + /// Usually not written directly in user code but indirectly via the macro + /// `core::field::field_of!(...)`. + FieldOf(Box, Vec), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 2e494b968b6bd..43ef6897b79cf 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::ImplicitSelf | ast::TyKind::CVarArgs | ast::TyKind::Pat(..) + | ast::TyKind::FieldOf(..) | ast::TyKind::Dummy | ast::TyKind::Err(..) => break None, } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 416fef8e3af38..08996c6901bfa 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1495,6 +1495,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Pat(ty, pat) => { hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span)) } + TyKind::FieldOf(ty, fields) => match fields.len() { + 0 => span_bug!(ty.span, "expected at least one field ident parsed in `field_of!`"), + 1 => hir::TyKind::FieldOf( + self.lower_ty(ty, itctx), + None, + self.lower_ident(fields[0]), + ), + 2 => hir::TyKind::FieldOf( + self.lower_ty(ty, itctx), + Some(self.lower_ident(fields[0])), + self.lower_ident(fields[1]), + ), + _ => hir::TyKind::Err(self.dcx().span_err( + fields.iter().map(|f| f.span).collect::>(), + "`field_of!` only supports a single field or a variant with a field", + )), + }, 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 1aa08dfd3d5ee..0400584a4f499 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1377,6 +1377,25 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + ast::TyKind::FieldOf(ty, fields) => { + self.word("builtin # field_of"); + self.popen(); + let ib = self.ibox(0); + self.print_type(ty); + self.word(","); + self.space(); + + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); + + for &field in rest { + self.word("."); + self.print_ident(field); + } + } + self.end(ib); + self.pclose(); + } } self.end(ib); } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 91defbad0a0e0..085a33705b5e7 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1875,6 +1875,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) @@ -1919,6 +1920,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 18279a4d05fe0..1880ee96044d6 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -216,6 +216,8 @@ fn push_debuginfo_type_name<'tcx>( write!(output, "{:?}", t).unwrap(); } } + // TODO(FRTs): implement debuginfo for field representing types + ty::FRT(..) => todo!(), ty::Slice(inner_type) => { if cpp_like_debuginfo { output.push_str("slice2$<"); diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index b771addb8df55..efd07f7186f77 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -90,7 +90,7 @@ fn const_to_valtree_inner<'tcx>( } match ty.kind() { - ty::FnDef(..) => { + ty::FnDef(..) | ty::FRT(..) => { *num_nodes += 1; Ok(ty::ValTree::zst(tcx)) } @@ -273,7 +273,7 @@ pub fn valtree_to_const_value<'tcx>( // create inner `MPlace`s which are filled recursively. // FIXME: Does this need an example? match *cv.ty.kind() { - ty::FnDef(..) => { + ty::FnDef(..) | ty::FRT(..) => { assert!(cv.valtree.is_zst()); mir::ConstValue::ZeroSized } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d70d157d88085..eeec9e7fa7a10 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{FieldId, FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; @@ -222,6 +222,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; } + sym::field_offset => { + let frt_ty = instance.args.type_at(0); + + let (ty, field) = match frt_ty.kind() { + &ty::FRT(ty, field) => (ty, field), + ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) | ty::Infer(..) => { + // This can happen in code which is generic over the field type. + throw_inval!(TooGeneric) + } + _ => { + span_bug!(self.cur_span(), "expected field representing type, got {frt_ty}") + } + }; + let FieldId::Resolved { variant, field } = field else { + span_bug!( + self.cur_span(), + "expected resolved field representing type, got `field_of!({ty}, {field:?})`" + ) + }; + let layout = self.layout_of(ty)?; + let cx = ty::layout::LayoutCx::new(*self.tcx, self.typing_env); + + let layout = layout.for_variant(&cx, variant); + let offset = layout.fields.offset(field.index()).bytes(); + + self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; + } sym::vtable_for => { let tp_ty = instance.args.type_at(0); let result_ty = instance.args.type_at(1); @@ -305,6 +332,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::UnsafeBinder(_) | ty::Never | ty::Tuple(_) + | ty::FRT(..) | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), }; let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 1c1c59da9d886..ba9c2c7c212ac 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -507,6 +507,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => true, ty::Str | ty::Slice(_) | ty::Dynamic(_, _) | ty::Foreign(..) => false, diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5d8ae42f5eccd..72f926eb69e08 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -785,6 +785,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { | ty::Dynamic(..) | ty::Closure(..) | ty::Pat(..) + | ty::FRT(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => interp_ok(false), // Some types only occur during typechecking, they have no layout. diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index db651811551f3..5564277ac5053 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -34,6 +34,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::Float(_) | ty::Str | ty::Pat(_, _) + | ty::FRT(..) | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_, _) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index ca2ce2f18812b..bda291f1daeca 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -492,6 +492,8 @@ declare_features! ( (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. (unstable, ffi_pure, "1.45.0", Some(58329)), + /// Experimental field projections. + (incomplete, field_projections, "CURRENT_RUSTC_VERSION", Some(145383)), /// Controlling the behavior of fmt::Debug (unstable, fmt_debug, "1.82.0", Some(129709)), /// Allows using `#[align(...)]` on function items diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index da3e79efd6dff..02c730798e265 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3755,6 +3755,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>, Option, Ident), /// `TyKind::Infer` means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. /// @@ -5051,8 +5055,8 @@ mod size_asserts { static_assert_size!(TraitImplHeader<'_>, 48); static_assert_size!(TraitItem<'_>, 88); static_assert_size!(TraitItemKind<'_>, 48); - static_assert_size!(Ty<'_>, 48); - static_assert_size!(TyKind<'_>, 32); + static_assert_size!(Ty<'_>, 56); + static_assert_size!(TyKind<'_>, 40); // tidy-alphabetical-end } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index e636b3fff654d..f45b1872cef90 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, 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 4ac3e4e83e80a..17ecbaa121b27 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,6 +447,12 @@ language_item_table! { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); + + // Field projection related lang-items + Field, sym::field, field, Target::Trait, GenericRequirement::Exact(0); + FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0); + FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0); + FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 4e8333f678b66..f12b1d0e0a82c 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -116,6 +116,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fabsf128 | sym::fadd_algebraic | sym::fdiv_algebraic + | sym::field_offset | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -292,6 +293,7 @@ pub(crate) fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), + sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 725294dfd3771..8a5a51d33f1fd 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -261,6 +261,17 @@ pub(super) fn check_item<'tcx>( .with_span_label(sp, "auto trait") .emit()); } + if is_auto && let rustc_hir::TyKind::FieldOf(..) = impl_.self_ty.kind { + res = res.and(Err(tcx + .dcx() + .struct_span_err( + item.span, + "impls of auto traits for field representing types not supported", + ) + .with_span_label(impl_.self_ty.span, "field representing type") + .with_span_label(of_trait.trait_ref.path.span, "auto trait") + .emit())); + } match header.polarity { ty::ImplPolarity::Positive => { res = res.and(check_impl(tcx, item, impl_)); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 38ae7852ca99f..ea7fc08ecc2a7 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -164,6 +164,10 @@ impl<'tcx> InherentCollect<'tcx> { while let ty::Pat(base, _) = *self_ty.kind() { self_ty = base; } + // TODO(FRTs): make properly recursive with pattern types as well + while let ty::FRT(ty, _) = *self_ty.kind() { + self_ty = ty; + } match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), @@ -173,7 +177,7 @@ impl<'tcx> InherentCollect<'tcx> { ty::Dynamic(..) => { Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span })) } - ty::Pat(_, _) => unreachable!(), + ty::Pat(_, _) | ty::FRT(..) => unreachable!(), ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f1e138dbcb97a..de1c9814c9ae9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -82,7 +82,10 @@ pub(crate) fn orphan_check_impl( ); if tcx.trait_is_auto(trait_def_id) { - let self_ty = trait_ref.self_ty(); + let mut self_ty = trait_ref.self_ty(); + if let ty::FRT(ty, _) = self_ty.kind() { + self_ty = *ty; + } // If the impl is in the same crate as the auto-trait, almost anything // goes. @@ -233,6 +236,7 @@ pub(crate) fn orphan_check_impl( let sp = tcx.def_span(impl_def_id); span_bug!(sp, "weird self type for autotrait impl") } + ty::FRT(..) => unreachable!("handled above"), ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow), }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 23110d2c5c87b..5d27446f8b791 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -22,6 +22,7 @@ pub mod generics; use std::assert_matches::assert_matches; use std::slice; +use rustc_abi::FIRST_VARIANT; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2710,6 +2711,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.record_ty(pat.hir_id, ty, pat.span); pat_ty } + hir::TyKind::FieldOf(ty, variant, field) => { + self.lower_field_of(self.lower_ty(ty), hir_ty.hir_id, *variant, *field) + } hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; @@ -2751,6 +2755,144 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + fn lower_field_of( + &self, + ty: Ty<'tcx>, + hir_id: HirId, + variant: Option, + field: Ident, + ) -> Ty<'tcx> { + 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 adt_path = tcx.def_path_str(base_did); + let err = struct_span_code_err!( + self.dcx(), + field.span, + E0001, + "field access on {kind_name} `{adt_path}` requires a variant" + ) + .with_span_help( + field.span.shrink_to_lo(), + "add the variant here `Variant.`", + ) + .emit(); + return Ty::new_error(tcx, err); + }; + + if let Some(res) = def + .variants() + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == variant) + { + res + } else { + let adt_path = tcx.def_path_str(base_did); + let err = struct_span_code_err!( + self.dcx(), + variant.span, + E0001, + "no variant `{variant}` on {kind_name} `{adt_path}`" + ) + .with_span_label(variant.span, "unknown variant") + .emit(); + return Ty::new_error(tcx, err); + } + } else { + if let Some(variant) = variant { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + self.dcx(), + variant.span, + E0001, + "{kind_name} `{adt_path}` does not have any variants", + ) + .with_span_label(variant.span, "variant unknown") + .emit(); + } + (FIRST_VARIANT, def.non_enum_variant()) + }; + let block = tcx.local_def_id_to_hir_id(self.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!( + self.dcx(), + ident.span, + E0616, + "field `{ident}` of {kind_name} `{adt_path}` is private", + ) + .with_span_label(ident.span, "private field") + .emit(); + } + Ty::new_resolved_field_representing_type(tcx, ty, variant_idx, field_idx) + } else { + let adt_path = tcx.def_path_str(base_did); + let err = struct_span_code_err!( + self.dcx(), + ident.span, + E0609, + "no field `{ident}` on {kind_name} `{adt_path}`" + ) + .with_span_label(ident.span, "unknown field") + .emit(); + Ty::new_error(tcx, err) + } + } + ty::Tuple(tys) => { + let index = match field.as_str().parse::() { + Ok(idx) => idx, + Err(_) => { + let err = struct_span_code_err!( + self.dcx(), + field.span, + E0609, + "no field `{field}` on type `{ty}`" + ) + .with_span_label(field.span, "unknown field") + .emit(); + return Ty::new_error(tcx, err); + } + }; + // TODO(FRTs): why even have this check? + if field.name != sym::integer(index) { + bug!("we parsed above, but now not equal?"); + } + if tys.get(index).is_some() { + Ty::new_resolved_field_representing_type(tcx, ty, FIRST_VARIANT, index.into()) + } else { + let err = struct_span_code_err!( + self.dcx(), + field.span, + E0609, + "no field `{field}` on type `{ty}`" + ) + .with_span_label(field.span, "unknown field") + .emit(); + Ty::new_error(tcx, err) + } + } + // TODO(FRTs): throw error in obvious cases + _ => Ty::new_unresolved_field_representing_type( + tcx, + ty, + variant.map(|v| v.name), + field.name, + ), + } + } + /// 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_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index ce4668736b570..011a3f1dc2166 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -255,6 +255,10 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_ty(current, typ, variance); } + ty::FRT(ty, _) => { + self.add_constraints_from_ty(current, ty, variance); + } + ty::Slice(typ) => { self.add_constraints_from_ty(current, typ, variance); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5c03135ee1bf6..e15f571c74509 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -460,6 +460,17 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + hir::TyKind::FieldOf(ty, variant, field) => { + self.word("field_of!("); + self.print_type(ty); + self.word(", "); + if let Some(variant) = variant { + self.print_ident(variant); + self.word("."); + } + self.print_ident(field); + self.word(")"); + } } self.end(ib) } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3f13a102684e0..9074e378c7cd3 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -137,6 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index c07b41b56caaa..bd9b95ae9f956 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -420,6 +420,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { | ty::Alias(..) | ty::Foreign(..) | ty::Pat(..) + | ty::FRT(..) | ty::Param(..) => { if t.flags().intersects(self.needs_canonical_flags) { t.super_fold_with(self) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 1160bd9ffa749..ef59aae331425 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -434,6 +434,7 @@ lint_improper_ctypes_enum_repr_help = consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum lint_improper_ctypes_enum_repr_reason = enum has no representation hint +lint_improper_ctypes_field_representing_types_reason = field representing types have no C equivalent lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 38094c67c34a0..c4e3f21a1f24d 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -580,6 +580,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // but only the base type is relevant for being representable in FFI. ty::Pat(base, ..) => self.visit_type(state, base), + ty::FRT(..) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_field_representing_types_reason, + help: None, + }, + // Primitive types with a stable representation. ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index fbcce16cedca8..f161a7021da6d 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -33,6 +33,7 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_thread_pool = { path = "../rustc_thread_pool" } rustc_type_ir = { path = "../rustc_type_ir" } +serde = { version = "1.0.125", features = ["derive"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 510c546f82a4e..6a8037fd8a862 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; @@ -14,9 +14,10 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_index::{IndexSlice, IndexVec}; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; +use rustc_span::Symbol; use rustc_type_ir::solve::AdtDestructorKind; use tracing::{debug, info, trace}; @@ -681,3 +682,47 @@ pub enum Representability { Representable, Infinite(ErrorGuaranteed), } + +#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug, Encodable, Decodable, HashStable)] +pub enum FieldId { + Resolved { variant: VariantIdx, field: FieldIdx }, + Unresolved { variant: Option, field: Symbol }, +} + +impl serde::Serialize for FieldId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Self::Resolved { variant, field } => { + serde::Serialize::serialize(&(*variant, *field), serializer) + } + unresolved @ Self::Unresolved { .. } => bug!("cannot serialize {unresolved:?}"), + } + } +} + +impl FieldId { + pub fn ty<'tcx>(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + match self { + FieldId::Resolved { variant, field } => match ty.kind() { + ty::Adt(def, args) => def.variants()[variant].fields[field].ty(tcx, args), + ty::Tuple(tys) => { + debug_assert_eq!(FIRST_VARIANT, variant); + tys[field.index()] + } + // TODO(FRTs): error? + _ => todo!(), + }, + // TODO(FRTs): error? + FieldId::Unresolved { .. } => todo!(), + } + } +} + +impl<'tcx> rustc_type_ir::inherent::FieldId> for FieldId { + fn ty(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + FieldId::ty(self, tcx, ty) + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 25cc739f5ff95..480c471f61391 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -78,9 +78,9 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, - GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, ParamTy, - Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, FieldId, GenericArg, + GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, + ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, ValTree, ValTreeKind, Visibility, }; @@ -252,6 +252,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } + type FieldId = FieldId; + fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { match self.def_kind(alias.def_id) { DefKind::AssocTy => { @@ -578,6 +580,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -814,6 +817,8 @@ bidirectional_lang_item_map! { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -845,6 +850,7 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, @@ -2640,6 +2646,7 @@ impl<'tcx> TyCtxt<'tcx> { Infer, Alias, Pat, + FRT, Foreign )?; diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 66542525d2841..03784b2420f67 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -186,6 +186,7 @@ impl<'tcx> Ty<'tcx> { ty::Foreign(_) => "extern type".into(), ty::Array(..) => "array".into(), ty::Pat(..) => "pattern type".into(), + ty::FRT(..) => "field representing type".into(), ty::Slice(_) => "slice".into(), ty::RawPtr(_, _) => "raw pointer".into(), ty::Ref(.., mutbl) => match mutbl { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 81b8142d03f37..58bdcbee6dbb0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -839,6 +839,7 @@ where | ty::Uint(_) | ty::Float(_) | ty::FnPtr(..) + | ty::FRT(..) | ty::Never | ty::FnDef(..) | ty::CoroutineWitness(..) diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 04a7cd2c75f28..e3f70fe131c90 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -85,6 +85,7 @@ impl MappingFlags { | ty::Float(_) | ty::Adt(_, _) | ty::Tuple(_) + | ty::FRT(..) | ty::Array(_, _) | ty::Alias(_, _) | ty::Param(_) => MappingFlags::TO, diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 0fd68e74e0441..456447a342d11 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -296,6 +296,8 @@ fn characteristic_def_id_of_type_cached<'a>( characteristic_def_id_of_type_cached(subty, visited) } + ty::FRT(ty, _) => characteristic_def_id_of_type_cached(ty, visited), + ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited), ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2a65517de4033..fcaf59fb4011b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write as _}; use std::iter; use std::ops::{Deref, DerefMut}; -use rustc_abi::{ExternAbi, Size}; +use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; @@ -723,6 +723,37 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty.print(self)?; write!(self, ") is {pat:?}")?; } + ty::FRT(ty, field) => { + write!(self, "field_of!(")?; + ty.print(self)?; + write!(self, ", ")?; + match field { + ty::FieldId::Resolved { variant, field } => match ty.kind() { + ty::Adt(def, _) => { + let variant = if def.is_enum() { + let variant = &def.variants()[variant]; + write!(self, "{}.", variant.name)?; + variant + } else { + def.non_enum_variant() + }; + write!(self, "{}", variant.fields[field].name)?; + } + ty::Tuple(_) => { + debug_assert_eq!(variant, FIRST_VARIANT); + write!(self, "{}", field.index())?; + } + _ => bug!("unexpected ty in resolved FRT: {ty}"), + }, + ty::FieldId::Unresolved { variant, field } => { + if let Some(variant) = variant { + write!(self, "{variant}.")?; + } + write!(self, "{field}")?; + } + } + write!(self, ")")?; + } ty::RawPtr(ty, mutbl) => { write!(self, "*{} ", mutbl.ptr_str())?; ty.print(self)?; diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index f1aa7076d98ac..b7cdb26c7d117 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -136,6 +136,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Placeholder(_) | ty::Infer(_) | ty::Slice(_) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 1a5a3f3965fab..59add4a5be7d9 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -400,6 +400,9 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { } ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?), ty::Pat(ty, pat) => ty::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?), + // TODO(FRTs): add FieldId to the folder infrastructure if we need to change it from + // folders. + ty::FRT(ty, field) => ty::FRT(ty.try_fold_with(folder)?, field), ty::Bool | ty::Char @@ -439,6 +442,9 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::CoroutineClosure(did, args) => ty::CoroutineClosure(did, args.fold_with(folder)), ty::Alias(kind, data) => ty::Alias(kind, data.fold_with(folder)), ty::Pat(ty, pat) => ty::Pat(ty.fold_with(folder), pat.fold_with(folder)), + // TODO(FRTs): add FieldId to the folder infrastructure if we need to change it from + // folders. + ty::FRT(ty, field) => ty::FRT(ty.fold_with(folder), field), ty::Bool | ty::Char @@ -491,6 +497,8 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { try_visit!(ty.visit_with(visitor)); pat.visit_with(visitor) } + // TODO(FRTs): add FieldId to the visitor infrastructure if we need to visit it. + ty::FRT(ty, _field) => ty.visit_with(visitor), ty::Error(guar) => guar.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c3834607e9236..a9d149955cab3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -26,8 +26,9 @@ use crate::infer::canonical::Canonical; use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, BoundRegionKind, Discr, FieldId, GenericArg, GenericArgs, GenericArgsRef, List, + ParamEnv, Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -533,6 +534,26 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, Pat(base, pat)) } + #[inline] + pub fn new_resolved_field_representing_type( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + variant: VariantIdx, + field: FieldIdx, + ) -> Ty<'tcx> { + Ty::new(tcx, FRT(ty, FieldId::Resolved { variant, field })) + } + + #[inline] + pub fn new_unresolved_field_representing_type( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + variant: Option, + field: Symbol, + ) -> Ty<'tcx> { + Ty::new(tcx, FRT(ty, FieldId::Unresolved { variant, field })) + } + #[inline] #[instrument(level = "debug", skip(tcx))] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { @@ -1106,6 +1127,17 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_pat(interner, ty, pat) } + fn new_field_representing_type(tcx: TyCtxt<'tcx>, ty: Self, field: FieldId) -> Self { + match field { + FieldId::Resolved { variant, field } => { + Ty::new_resolved_field_representing_type(tcx, ty, variant, field) + } + FieldId::Unresolved { variant, field } => { + Ty::new_unresolved_field_representing_type(tcx, ty, variant, field) + } + } + } + fn new_unsafe_binder(interner: TyCtxt<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>) -> Self { Ty::new_unsafe_binder(interner, ty) } @@ -1125,6 +1157,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { fn has_unsafe_fields(self) -> bool { Ty::has_unsafe_fields(self) } + + fn is_packed(self) -> bool { + Ty::is_packed(self) + } } /// Type utilities @@ -1179,6 +1215,11 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), Adt(..)) } + #[inline] + pub fn is_packed(self) -> bool { + matches!(self.kind(), Adt(def, _) if def.repr().packed()) + } + #[inline] pub fn is_ref(self) -> bool { matches!(self.kind(), Ref(..)) @@ -1674,6 +1715,7 @@ impl<'tcx> Ty<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(..) + | ty::FRT(..) | ty::Foreign(_) | ty::Str | ty::Array(..) @@ -1725,6 +1767,7 @@ impl<'tcx> Ty<'tcx> { | ty::Array(..) | ty::Closure(..) | ty::CoroutineClosure(..) + | ty::FRT(..) | ty::Never | ty::Error(_) // Extern types have metadata = (). @@ -1915,6 +1958,7 @@ impl<'tcx> Ty<'tcx> { | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -1968,6 +2012,9 @@ impl<'tcx> Ty<'tcx> { // ZST which can't be named are fine. ty::FnDef(..) => true, + // ZST. + ty::FRT(..) => true, + ty::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(), // A 100-tuple isn't "trivial", so doing this only for reasonable sizes. @@ -2038,6 +2085,7 @@ impl<'tcx> Ty<'tcx> { | ty::Array(..) | ty::Foreign(_) | ty::Pat(_, _) + | ty::FRT(..) | ty::FnDef(..) | ty::UnsafeBinder(..) | ty::Dynamic(..) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2797f2fcdb72e..2a3cdb76f93fa 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1171,6 +1171,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(_, _) | ty::FnDef(..) @@ -1212,6 +1213,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(_, _) | ty::FnDef(..) @@ -1263,6 +1265,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) @@ -1434,6 +1437,9 @@ impl<'tcx> Ty<'tcx> { // Raw pointers use bitwise comparison. ty::RawPtr(_, _) | ty::FnPtr(..) => true, + // FRTs are ZSTs. + ty::FRT(..) => true, + // Floating point numbers are not `Eq`. ty::Float(_) => false, @@ -1513,6 +1519,7 @@ pub fn needs_drop_components_with_async<'tcx>( | ty::Uint(_) | ty::Float(_) | ty::Never + | ty::FRT(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Char diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ced9bd735ba2b..0790d26e9ed91 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -154,6 +154,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) @@ -196,6 +197,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 5254f60a15036..8795d3adfce5b 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -867,7 +867,7 @@ fn try_write_constant<'tcx>( match ty.kind() { // ZSTs. Nothing to do. - ty::FnDef(..) => {} + ty::FnDef(..) | ty::FRT(..) => {} // Those are scalars, must be handled above. ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 9162284422d0d..2ea8bebc9da6c 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -378,6 +378,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index c370fd24a1bb3..4e99d8de6ea65 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -408,6 +408,7 @@ where // For fundamental types, we just look inside of them. ty::Ref(_, ty, _) => ty.visit_with(self), + ty::FRT(ty, _) => ty.visit_with(self), ty::Adt(def, args) => { if self.def_id_is_local(def.def_id()) { ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index d27d80a086ad1..f7af17751d7c6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -348,6 +348,11 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Vec>; + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; } /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit @@ -617,6 +622,7 @@ where Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } + Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal), _ => Err(NoSolution), } }; @@ -690,6 +696,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -808,6 +815,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 2f6ff12e1e8df..d47f1471f253f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -35,6 +35,7 @@ where | ty::Float(_) | ty::FnDef(..) | ty::FnPtr(..) + | ty::FRT(..) | ty::Error(_) | ty::Never | ty::Char => Ok(ty::Binder::dummy(vec![])), @@ -117,7 +118,7 @@ where match ty.kind() { // impl {Meta,}Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char // impl {Meta,}Sized for &mut? T, [T; N], dyn* Trait, !, Coroutine, CoroutineWitness - // impl {Meta,}Sized for Closure, CoroutineClosure + // impl {Meta,}Sized for FRT, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -132,6 +133,7 @@ where | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -194,7 +196,7 @@ where { match ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) @@ -389,6 +391,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( | ty::Never | ty::Tuple(_) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) @@ -784,6 +789,7 @@ pub(in crate::solve) fn const_conditions_for_destruct( | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::FRT(..) | ty::Error(_) => Ok(vec![]), // Coroutines and closures could implement `[const] Drop`, diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 2837b8565f603..4da76b88b5de0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -430,6 +430,13 @@ where ) -> Vec> { unreachable!("Unsize is not const") } + + fn consider_builtin_field_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<::Interner, Self>, + ) -> Result::Interner>, NoSolution> { + unreachable!("Field is not const") + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 70c28421c57ea..1d0c33ca6b5ca 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -631,6 +631,7 @@ where | ty::Float(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -884,6 +885,7 @@ where | ty::Float(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -950,6 +952,29 @@ where ) -> Result, NoSolution> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + let self_ty = goal.predicate.self_ty(); + let ty::FRT(container, field) = self_ty.kind() else { + return Err(NoSolution); + }; + + let ty = if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldBase) { + container + } else if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldType) { + field.ty(ecx.cx(), container) + } else { + panic!("unexpected associated type {:?} in `Field`", goal.predicate) + }; + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 651f073efb828..12777d2e18d5b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -735,6 +735,7 @@ where | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -841,6 +842,23 @@ where } }) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + if let ty::FRT(ty, _) = goal.predicate.self_ty().kind() + && !ty.is_packed() + { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } else { + Err(NoSolution) + } + } } /// Small helper function to change the `def_id` of a trait predicate - this is not normally @@ -1241,6 +1259,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8bbf534294f4e..2e89ac3654470 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 3ccaae868a659..b1bfaac4014f9 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 @@ -813,6 +815,42 @@ impl<'a> Parser<'a> { Ok(TyKind::Err(guar)) } + fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { + self.parse_builtin(|this, lo, ident| { + Ok(match ident.name { + sym::field_of => Some(this.parse_ty_field_of(lo)?), + _ => None, + }) + }) + } + + /// Built-in macro for `field_of!` expressions. + pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { + let container = self.parse_ty()?; + self.expect(exp!(Comma))?; + + let fields = self.parse_floating_field_access()?; + let trailing_comma = self.eat_noexpect(&TokenKind::Comma); + + if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { + if trailing_comma { + e.note("unexpected third argument to field_of"); + } else { + e.note("field_of expects dot-separated field and variant names"); + } + e.emit(); + } + + // Eat tokens until the macro call ends. + if self.may_recover() { + while !self.token.kind.is_close_delim_or_eof() { + self.bump(); + } + } + + Ok(TyKind::FieldOf(container, fields)) + } + /// Parses a function pointer type (`TyKind::FnPtr`). /// ```ignore (illustrative) /// [unsafe] [extern "ABI"] fn (S) -> T diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index fee920221e1d1..1295a7a814461 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -301,6 +301,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { | ty::Str | ty::Tuple(_) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 35101624bd68e..c4025c85ebc6e 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -410,6 +410,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TraitObject, Infer, Pat, + FieldOf, Err ] ); @@ -679,6 +680,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Tup, Path, Pat, + FieldOf, TraitObject, ImplTrait, Paren, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d66c303b17260..63d4d6f6e6500 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -407,6 +407,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Adt(..) | ty::Tuple(..) => { ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) } } + ty::FRT(..) => ConstructorSet::Struct { empty: false }, ty::Ref(..) => ConstructorSet::Ref, ty::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 8656ec6e39aed..488390b36be36 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -291,6 +291,7 @@ where | ty::RawPtr(..) | ty::Ref(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnPtr(..) | ty::UnsafeBinder(_) | ty::Param(..) diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 14656a2e594ad..13e68fd6f6686 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; +use rustc_middle::ty::FieldId; use serde::Serialize; use super::abi::ReprOptions; @@ -554,6 +555,7 @@ pub enum RigidTy { Str, Array(Ty, TyConst), Pat(Ty, Pattern), + FRT(Ty, FieldId), Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index d9f314a8e29cc..972fd21f7f1b2 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -153,6 +153,7 @@ impl RustcInternal for RigidTy { RigidTy::Pat(ty, pat) => { rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx)) } + RigidTy::FRT(ty, field) => rustc_ty::TyKind::FRT(ty.internal(tables, tcx), *field), RigidTy::Adt(def, args) => { rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx)) } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index ca8234280be85..02ac75bf2a699 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -413,6 +413,7 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Pat(ty, pat) => { TyKind::RigidTy(RigidTy::Pat(ty.stable(tables, cx), pat.stable(tables, cx))) } + ty::FRT(ty, field) => TyKind::RigidTy(RigidTy::FRT(ty.stable(tables, cx), *field)), ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables, cx))), ty::RawPtr(ty, mutbl) => { TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables, cx), mutbl.stable(tables, cx))) diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index acc3334769613..33d9cdb55bac8 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -158,6 +158,7 @@ impl Visitable for RigidTy { c.visit(visitor) } RigidTy::Pat(t, _p) => t.visit(visitor), + RigidTy::FRT(t, _i) => t.visit(visitor), RigidTy::Slice(inner) => inner.visit(visitor), RigidTy::RawPtr(ty, _) => ty.visit(visitor), RigidTy::Ref(reg, ty, _) => { diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 5505fe82cea65..c5ee28a3f8562 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -434,6 +434,9 @@ pub(crate) fn encode_ty<'tcx>( typeid.push_str(&s); } + // TODO(FRTs): add mangling support + ty::FRT(..) => todo!(), + ty::Slice(ty0) => { // u5sliceIE as vendor extended type let mut s = String::from("u5sliceI"); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 9cea681fcb579..b3dc15da7ba8f 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -60,6 +60,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { | ty::Foreign(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Slice(..) | ty::Str | ty::Tuple(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72709753b1dff..2571b288c8a02 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1067,7 +1067,12 @@ symbols! { ffi_pure, ffi_returns_twice, field, + field_base, field_init_shorthand, + field_of, + field_offset, + field_projections, + field_type, file, file_options, flags, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 3896e06a627b7..908a270f6701b 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -106,6 +106,7 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 755b4923e9cd4..42ee89e7b1961 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -510,6 +510,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { self.print_pat(pat)?; } + // TODO(FRTs): add mangling support + ty::FRT(..) => todo!("mangle {ty}"), + ty::Array(ty, len) => { self.push("A"); ty.print(self)?; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d96a1b0a8c0eb..d368d65941b9d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1831,6 +1831,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::CoroutineClosure(..) => Some(21), ty::Pat(..) => Some(22), ty::UnsafeBinder(..) => Some(23), + ty::FRT(..) => Some(24), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 66c949a38cea7..3a57600ba3644 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -328,7 +328,7 @@ fn evaluate_host_effect_for_copy_clone_goal<'tcx>( let self_ty = obligation.predicate.self_ty(); let constituent_tys = match *self_ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) @@ -470,6 +470,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::FRT(..) | ty::Error(_) => thin_vec![], // Coroutines and closures could implement `[const] Drop`, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e5c2adaa261d3..f653e71ee0b56 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -996,7 +996,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | LangItem::FnOnce | LangItem::AsyncFn | LangItem::AsyncFnMut - | LangItem::AsyncFnOnce, + | LangItem::AsyncFnOnce + | LangItem::Field, ) => true, Some(LangItem::AsyncFnKindHelper) => { // FIXME(async_closures): Validity constraints here could be cleaned up. @@ -1027,6 +1028,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1082,6 +1084,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1551,6 +1554,17 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } }); (metadata_ty.into(), obligations) + } else if tcx.is_lang_item(trait_def_id, LangItem::Field) { + let &ty::FRT(container, field) = self_ty.kind() else { + bug!("only `field_of!()` can implement `Field`") + }; + if tcx.is_lang_item(item_def_id, LangItem::FieldBase) { + (container.into(), PredicateObligations::new()) + } else if tcx.is_lang_item(item_def_id, LangItem::FieldType) { + (field.ty(tcx, container).into(), PredicateObligations::new()) + } else { + bug!("unexpected associated type {:?} in `Field`", obligation.predicate); + } } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 2e60805cd10a5..2aada90e0d985 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -43,6 +43,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::Str | ty::Foreign(..) + | ty::FRT(..) | ty::Error(_) => true, // `T is PAT` and `[T]` have same properties as T. @@ -275,6 +276,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( | ty::Float(_) | ty::Str | ty::Never + | ty::FRT(..) | ty::Foreign(..) | ty::RawPtr(..) | ty::Ref(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index d0833f0308350..1ca81189e6b6a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -128,6 +128,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut candidates, ); } + Some(LangItem::Field) => { + self.assemble_candidates_for_field_trait(obligation, &mut candidates); + } _ => { // We re-match here for traits that can have both builtin impls and user written impls. // After the builtin impls we need to also add user written impls, which we do not want to @@ -691,6 +694,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -865,6 +869,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::Adt(..) | ty::RawPtr(_, _) @@ -1132,7 +1137,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *self_ty.kind() { // These impls are built-in because we cannot express sufficiently // generic impls in libcore. - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Tuple(..) | ty::Pat(..) => { + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Tuple(..) + | ty::Pat(..) + | ty::FRT(..) => { candidates.vec.push(BuiltinCandidate); } @@ -1247,6 +1257,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => { candidates.vec.push(SizedCandidate); } @@ -1329,6 +1340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) | ty::Dynamic(_, _) @@ -1365,6 +1377,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(..) @@ -1416,6 +1429,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -1437,4 +1451,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_field_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if let ty::FRT(ty, _) = obligation.predicate.self_ty().skip_binder().kind() + && !ty.is_packed() + { + candidates.vec.push(BuiltinCandidate); + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 20a8842f2e8e5..924f69e38d069 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -265,6 +265,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | LangItem::FnPtrTrait | LangItem::PointeeTrait | LangItem::Tuple + | LangItem::Field | LangItem::Unpin, ) => ty::Binder::dummy(vec![]), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), @@ -1300,6 +1301,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d6c9adfb28177..bb6780581ee04 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2160,6 +2160,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { @@ -2197,7 +2198,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn copy_clone_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec>> { match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => ty::Binder::dummy(vec![]), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => ty::Binder::dummy(vec![]), ty::Uint(_) | ty::Int(_) @@ -2303,6 +2304,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::FnPtr(..) | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::FRT(..) | ty::Never | ty::Char => { ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] }) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 394095508393c..d18e9aa76630f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -794,7 +794,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } } - ty::RawPtr(_, _) => { + ty::RawPtr(_, _) | ty::FRT(..) => { // Simple cases that are WF if their type args are WF. } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 23bbd9ca6d639..b00d9fc29a0d1 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -382,6 +382,22 @@ fn resolve_associated_item<'tcx>( assert_eq!(name, sym::transmute); let args = tcx.erase_and_anonymize_regions(rcvr_args); Some(ty::Instance::new_raw(trait_item_id, args)) + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::Field) { + if tcx.is_lang_item(trait_item_id, LangItem::FieldOffset) { + let self_ty = trait_ref.self_ty(); + match self_ty.kind() { + ty::FRT(..) => {} + _ => bug!("expected field representing type, found {self_ty}"), + } + Some(Instance { + def: ty::InstanceKind::Item( + tcx.lang_items().get(LangItem::FieldOffset).unwrap(), + ), + args: rcvr_args, + }) + } else { + bug!("unexpected associated associated item") + } } else { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 62f3667ad7f4f..f0cbcac8f5ec5 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -379,6 +379,8 @@ fn layout_of_uncached<'tcx>( tcx.mk_layout(layout) } + ty::FRT(..) => cx.layout_of(tcx.types.unit)?.layout, + // Basic scalars. ty::Bool => tcx.mk_layout(LayoutData::scalar( cx, diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 0ef435b1a0e21..2881f0ec62330 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -289,6 +289,7 @@ where | ty::RawPtr(..) | ty::FnDef(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnPtr(..) | ty::Tuple(_) | ty::Bound(..) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index bb25a14ef7443..7097db2bd0031 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -34,6 +34,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::FnDef(..) | ty::FnPtr(..) | ty::Array(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) @@ -377,6 +378,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index ed6416a7f55f2..2825aa1b52616 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -46,6 +46,9 @@ pub enum SimplifiedType { Function(usize), UnsafeBinder, Placeholder, + /// Field representing type. + // TODO(FRTs): should we have a `Option` here that gets filled with the Adt's DefId. + FRT, Error, } @@ -127,6 +130,7 @@ pub fn simplify_type( ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), ty::Pat(ty, ..) => simplify_type(cx, ty, treat_params), + ty::FRT(..) => Some(SimplifiedType::FRT), ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !cx.trait_is_auto(principal_def_id) => { @@ -298,6 +302,7 @@ impl matches!(rhs.kind(), ty::FRT(rhs_ty, rhs_field) if + lhs_field == rhs_field && self.types_may_unify_inner(lhs_ty, rhs_ty, depth) + ), + ty::UnsafeBinder(lhs_ty) => match rhs.kind() { ty::UnsafeBinder(rhs_ty) => { self.types_may_unify(lhs_ty.skip_binder(), rhs_ty.skip_binder()) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 2c1fc7decc3e7..e0e1db04cea4b 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -322,6 +322,10 @@ impl FlagComputation { self.add_ty_pat(pat); } + ty::FRT(ty, _field) => { + self.add_ty(ty); + } + ty::Slice(tt) => self.add_ty(tt), ty::RawPtr(ty, _) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 16f837141e97e..f503f56855f04 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -119,6 +119,8 @@ pub trait Ty>: fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; + fn new_field_representing_type(interner: I, ty: Self, field: I::FieldId) -> Self; + fn new_unsafe_binder(interner: I, ty: ty::Binder) -> Self; fn tuple_fields(self) -> I::Tys; @@ -152,6 +154,9 @@ pub trait Ty>: /// Checks whether this type is an ADT that has unsafe fields. fn has_unsafe_fields(self) -> bool; + /// Checks whether this type is an ADT that is `repr(packed)`. + fn is_packed(self) -> bool; + fn fn_sig(self, interner: I) -> ty::Binder> { self.kind().fn_sig(interner) } @@ -174,6 +179,7 @@ pub trait Ty>: | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) @@ -637,6 +643,10 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn destructor(self, interner: I) -> Option; } +pub trait FieldId: Copy + Debug + Hash + Eq { + fn ty(self, interner: I, ty: I::Ty) -> I::Ty; +} + pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { fn caller_bounds(self) -> impl SliceLike; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 03cf738c05987..4a66deea813cf 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -211,6 +211,8 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; + type FieldId: FieldId; + fn alias_ty_kind(self, alias: ty::AliasTy) -> ty::AliasTyKind; fn alias_term_kind(self, alias: ty::AliasTerm) -> ty::AliasTermKind; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 39b575ebab63b..f1c45a4d98b5e 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -9,6 +9,8 @@ pub enum SolverLangItem { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -36,6 +38,7 @@ pub enum SolverTraitLangItem { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index c7dccea6adc12..aa39f5344ba7f 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -198,6 +198,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 4954ebc51cfce..1a28d64451490 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -524,6 +524,15 @@ pub fn structurally_relate_tys>( Ok(Ty::new_pat(cx, ty, pat)) } + (ty::FRT(a_ty, a_field), ty::FRT(b_ty, b_field)) => { + let ty = relation.relate(a_ty, b_ty)?; + if a_field == b_field { + Ok(Ty::new_field_representing_type(cx, ty, a_field)) + } else { + Err(TypeError::Mismatch) + } + } + (ty::UnsafeBinder(a_binder), ty::UnsafeBinder(b_binder)) => { Ok(Ty::new_unsafe_binder(cx, relation.binders(*a_binder, *b_binder)?)) } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 498797bef653a..addbd2886dc63 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -106,6 +106,9 @@ pub enum TyKind { /// Only supports integer range patterns for now. Pat(I::Ty, I::Pat), + /// Field representing type (`field_of!(Struct, field)`). + FRT(I::Ty, I::FieldId), + /// The pointee of an array slice. Written as `[T]`. Slice(I::Ty), @@ -298,6 +301,7 @@ impl TyKind { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -350,6 +354,7 @@ impl fmt::Debug for TyKind { Str => write!(f, "str"), Array(t, c) => write!(f, "[{t:?}; {c:?}]"), Pat(t, p) => write!(f, "pattern_type!({t:?} is {p:?})"), + FRT(t, i) => write!(f, "field_of!({t:?}, {i:?})"), Slice(t) => write!(f, "[{:?}]", &t), RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index e48d598a5328d..a07fde2d00975 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -92,6 +92,9 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg push_ty_pat::(stack, pat); stack.push(ty.into()); } + ty::FRT(ty, _) => { + stack.push(ty.into()); + } ty::Array(ty, len) => { stack.push(len.into()); stack.push(ty.into()); diff --git a/library/core/src/field.rs b/library/core/src/field.rs new file mode 100644 index 0000000000000..66ae7f3a85c9b --- /dev/null +++ b/library/core/src/field.rs @@ -0,0 +1,35 @@ +//! Field Reflection + +/// Type representing a field of a `struct`, `union`, `enum` variant or tuple. +/// +/// # Safety +/// +/// Given a valid value of type `Self::Base`, there exists a valid value of type `Self::Type` at +/// byte offset `OFFSET`. +#[lang = "field"] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] +pub unsafe trait Field: Sized { + /// The type of the base where this field exists in. + #[lang = "field_base"] + type Base; + + /// The type of the field. + #[lang = "field_type"] + type Type; + + /// The offset of the field in bytes. + #[lang = "field_offset"] + const OFFSET: usize = crate::intrinsics::field_offset::(); +} + +/// Expands to the field representing type of the given field. +/// +/// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the +/// variant must also be specified. Only a single field is supported. +#[unstable(feature = "field_projections", issue = "145383")] +#[allow_internal_unstable(builtin_syntax)] +pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { + builtin # field_of($Container, $($fields)+) +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index d46d3ed9d5137..48ea5e6af27f7 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2809,6 +2809,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 962b0cea4a4f1..e2bc0ed15472a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -152,6 +152,7 @@ #![feature(extern_types)] #![feature(f16)] #![feature(f128)] +#![feature(field_projections)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] @@ -311,6 +312,8 @@ pub mod bstr; pub mod cell; pub mod char; pub mod ffi; +#[unstable(feature = "field_projections", issue = "145383")] +pub mod field; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub mod io; pub mod iter; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0dab29712f4fc..c003b19642f8c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -493,6 +493,8 @@ pub use core::cmp; pub use core::convert; #[stable(feature = "rust1", since = "1.0.0")] pub use core::default; +#[unstable(feature = "field_projections", issue = "145383")] +pub use core::field; #[stable(feature = "futures_api", since = "1.36.0")] pub use core::future; #[stable(feature = "core_hint", since = "1.27.0")] diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 707b48b355bab..ef42ebf0e120d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -47,7 +47,9 @@ use rustc_hir_analysis::hir_ty_lowering::FeedConstTy; use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{ + self, AdtKind, FieldId, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode, +}; use rustc_middle::{bug, span_bug}; use rustc_span::ExpnKind; use rustc_span::hygiene::{AstPass, MacroKind}; @@ -1788,6 +1790,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, variant, field) => { + let field_str = if let Some(variant) = variant { + format!("{variant}.{field}") + } else { + format!("{field}") + }; + Type::FRT(Box::new(clean_ty(ty, cx)), field_str.into()) + } TyKind::Array(ty, const_arg) => { // NOTE(min_const_generics): We can't use `const_eval_poly` for constants // as we currently do not supply the parent generics to anonymous constants @@ -2024,6 +2034,35 @@ pub(crate) fn clean_middle_ty<'tcx>( Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), format!("{pat:?}").into_boxed_str(), ), + ty::FRT(ty, field) => { + let field_str = match field { + FieldId::Resolved { variant, field } => match ty.kind() { + ty::Adt(def, _) => { + if def.is_enum() { + let variant = &def.variants()[variant]; + format!("{}.{}", variant.name, variant.fields[field].name) + } else { + format!("{}", def.non_enum_variant().fields[field].name) + } + } + ty::Tuple(_) => { + format!("{}", field.index()) + } + _ => bug!("unexpected ty in resolved FRT: {ty}"), + }, + FieldId::Unresolved { variant, field } => { + if let Some(variant) = variant { + format!("{variant}.{field}") + } else { + format!("{field}") + } + } + }; + Type::FRT( + Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), + field_str.into_boxed_str(), + ) + } ty::Array(ty, n) => { let n = cx.tcx.normalize_erasing_regions(cx.typing_env(), n); let n = print_const(cx, n); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index a390a03ff1144..5a5e7093b827c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1304,6 +1304,7 @@ pub(crate) enum Type { /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), Pat(Box, Box), + FRT(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` @@ -1517,6 +1518,7 @@ impl Type { Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, + Type::FRT(..) => PrimitiveType::FRT, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, @@ -1564,6 +1566,7 @@ pub(crate) enum PrimitiveType { Slice, Array, Pat, + FRT, Tuple, Unit, RawPointer, @@ -1719,6 +1722,8 @@ impl PrimitiveType { Char => sym::char, Array => sym::array, Pat => sym::pat, + // TODO(FRTs): what symbol should be here? + FRT => todo!(), Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index eee13ff2b0dc0..8d1bca67bd766 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -958,6 +958,11 @@ fn fmt_type( fmt::Display::fmt(&print_type(t, cx), f)?; write!(f, " is {pat}") } + clean::Type::FRT(t, field) => { + write!(f, "field_of!(")?; + fmt::Display::fmt(&print_type(t, cx), f)?; + write!(f, ", {field})") + } clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( f, PrimitiveType::Array, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 12b207dda5693..e738b4b605083 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2041,6 +2041,7 @@ fn get_index_type_id( } // Not supported yet clean::Type::Pat(..) + | clean::Type::FRT(..) | clean::Generic(_) | clean::SelfTy | clean::ImplTrait(_) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 892cc483dbd6f..56c4bd0b95134 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(), }, + // TODO(FRTs): implement + clean::Type::FRT(..) => todo!(), ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 3abf0fee3959a..9b1a4c958425a 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -518,6 +518,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), ty::Tuple(_) => Res::Primitive(Tuple), ty::Pat(..) => Res::Primitive(Pat), + ty::FRT(..) => Res::Primitive(FRT), ty::Array(..) => Res::Primitive(Array), ty::Slice(_) => Res::Primitive(Slice), ty::RawPtr(_, _) => Res::Primitive(RawPointer), diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 32fd4afb122e6..2bdbdfd905257 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -855,6 +855,7 @@ impl TyCoercionStability { | TyKind::Ptr(_) | TyKind::FnPtr(_) | TyKind::Pat(..) + | TyKind::FieldOf(..) | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) => Self::Deref, @@ -907,6 +908,7 @@ impl TyCoercionStability { | ty::Uint(_) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Float(_) | ty::RawPtr(..) | ty::FnPtr(..) diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index d9254fca9453d..178720c4aa540 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -532,6 +532,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { // experimental | TyKind::Pat(..) + | TyKind::FieldOf(..) // unused | TyKind::CVarArgs diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 03853b5b4af54..3e722de25efbc 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1484,6 +1484,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_ty_pat(pat); }, + TyKind::FieldOf(ty, variant, field) => { + self.hash_ty(ty); + if let Some(variant) = variant { + self.hash_name(variant.name); + } + self.hash_name(field.name); + }, TyKind::Ptr(mut_ty) => { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 2d7bc59c62788..998b9f3e08bec 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1035,6 +1035,12 @@ impl Rewrite for ast::Ty { let pat = pat.rewrite_result(context, shape)?; Ok(format!("{ty} is {pat}")) } + ast::TyKind::FieldOf(ref ty, ref fields) => { + let ty = ty.rewrite_result(context, shape)?; + let fields = fields.iter().map(|f| format!("{f}")).collect::>(); + let fields = fields.join("."); + Ok(format!("field_of!({ty}, {fields})",)) + } ast::TyKind::UnsafeBinder(ref binder) => { let mut result = String::new(); if binder.generic_params.is_empty() { diff --git a/tests/ui/feature-gates/feature-gate-field-projections.rs b/tests/ui/feature-gates/feature-gate-field-projections.rs new file mode 100644 index 0000000000000..5fa6b1ea30810 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-projections.rs @@ -0,0 +1,21 @@ +#![allow(dead_code)] + +use std::field::{Field, field_of}; //~ ERROR: use of unstable library feature `field_projections` [E0658] +//~^ ERROR: use of unstable library feature `field_projections` [E0658] +use std::ptr; + +fn project_ref( + //~^ ERROR: use of unstable library feature `field_projections` [E0658] + r: &F::Base, //~ ERROR: use of unstable library feature `field_projections` [E0658] +) -> &F::Type +//~^ ERROR: use of unstable library feature `field_projections` [E0658] +where + F::Type: Sized, //~ ERROR: use of unstable library feature `field_projections` [E0658] +{ + unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } //~ ERROR: use of unstable library feature `field_projections` [E0658] +} + +fn main() { + struct Foo(()); + let _ = project_ref::(&Foo(())); //~ ERROR: use of unstable library feature `field_projections` [E0658] +} diff --git a/tests/ui/feature-gates/feature-gate-field-projections.stderr b/tests/ui/feature-gates/feature-gate-field-projections.stderr new file mode 100644 index 0000000000000..01074d49fa862 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-projections.stderr @@ -0,0 +1,83 @@ +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:20:27 + | +LL | let _ = project_ref::(&Foo(())); + | ^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:3:18 + | +LL | use std::field::{Field, field_of}; + | ^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:3:25 + | +LL | use std::field::{Field, field_of}; + | ^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:7:19 + | +LL | fn project_ref( + | ^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:13:5 + | +LL | F::Type: Sized, + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:9:9 + | +LL | r: &F::Base, + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:10:7 + | +LL | ) -> &F::Type + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:15:42 + | +LL | unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } + | ^^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/field_representing_types/auxiliary/extern-crate.rs b/tests/ui/field_representing_types/auxiliary/extern-crate.rs new file mode 100644 index 0000000000000..8e7e6d2679b44 --- /dev/null +++ b/tests/ui/field_representing_types/auxiliary/extern-crate.rs @@ -0,0 +1,6 @@ +pub struct Point { + pub x: usize, + pub y: usize, +} + +pub trait ForeignTrait {} diff --git a/tests/ui/field_representing_types/deny-manual-impl.next.stderr b/tests/ui/field_representing_types/deny-manual-impl.next.stderr new file mode 100644 index 0000000000000..a10c403667804 --- /dev/null +++ b/tests/ui/field_representing_types/deny-manual-impl.next.stderr @@ -0,0 +1,9 @@ +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/deny-manual-impl.rs:11:1 + | +LL | unsafe impl Field for MyStruct { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0322`. diff --git a/tests/ui/field_representing_types/deny-manual-impl.old.stderr b/tests/ui/field_representing_types/deny-manual-impl.old.stderr new file mode 100644 index 0000000000000..a10c403667804 --- /dev/null +++ b/tests/ui/field_representing_types/deny-manual-impl.old.stderr @@ -0,0 +1,9 @@ +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/deny-manual-impl.rs:11:1 + | +LL | unsafe impl Field for MyStruct { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0322`. diff --git a/tests/ui/field_representing_types/deny-manual-impl.rs b/tests/ui/field_representing_types/deny-manual-impl.rs new file mode 100644 index 0000000000000..16d82c1fab0c2 --- /dev/null +++ b/tests/ui/field_representing_types/deny-manual-impl.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::Field; + +#[repr(packed)] +pub struct MyStruct(usize); + +unsafe impl Field for MyStruct { + //~^ ERROR: explicit impls for the `Field` trait are not permitted [E0322] + type Base = (); + type Type = (); + const OFFSET: usize = 0; +} + +fn main() {} diff --git a/tests/ui/field_representing_types/deny-private-field.next.stderr b/tests/ui/field_representing_types/deny-private-field.next.stderr new file mode 100644 index 0000000000000..d0dff43957a20 --- /dev/null +++ b/tests/ui/field_representing_types/deny-private-field.next.stderr @@ -0,0 +1,9 @@ +error[E0616]: field `a` of struct `A` is private + --> $DIR/deny-private-field.rs:18:25 + | +LL | let _: field_of!(A, a); + | ^ private field + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_representing_types/deny-private-field.old.stderr b/tests/ui/field_representing_types/deny-private-field.old.stderr new file mode 100644 index 0000000000000..d0dff43957a20 --- /dev/null +++ b/tests/ui/field_representing_types/deny-private-field.old.stderr @@ -0,0 +1,9 @@ +error[E0616]: field `a` of struct `A` is private + --> $DIR/deny-private-field.rs:18:25 + | +LL | let _: field_of!(A, a); + | ^ private field + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_representing_types/deny-private-field.rs b/tests/ui/field_representing_types/deny-private-field.rs new file mode 100644 index 0000000000000..6cd6d477f5dae --- /dev/null +++ b/tests/ui/field_representing_types/deny-private-field.rs @@ -0,0 +1,20 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +mod foo { + pub struct A { + a: i32, + pub b: i32, + } +} + +fn main() { + use foo::A; + + let _: field_of!(A, a); //~ ERROR: field `a` of struct `A` is private [E0616] + let _: field_of!(A, b); +} diff --git a/tests/ui/field_representing_types/deny-weird-impls.next.stderr b/tests/ui/field_representing_types/deny-weird-impls.next.stderr new file mode 100644 index 0000000000000..08988c4bcf5bd --- /dev/null +++ b/tests/ui/field_representing_types/deny-weird-impls.next.stderr @@ -0,0 +1,20 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/deny-weird-impls.rs:10:15 + | +LL | impl Drop for field_of!(MyStruct, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^ must be a struct, enum, or union in the current crate + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: impls of auto traits for field representing types not supported + --> $DIR/deny-weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^----^^^^^----------------------^^^ + | | | + | | field representing type + | auto trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0120`. diff --git a/tests/ui/field_representing_types/deny-weird-impls.old.stderr b/tests/ui/field_representing_types/deny-weird-impls.old.stderr new file mode 100644 index 0000000000000..08988c4bcf5bd --- /dev/null +++ b/tests/ui/field_representing_types/deny-weird-impls.old.stderr @@ -0,0 +1,20 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/deny-weird-impls.rs:10:15 + | +LL | impl Drop for field_of!(MyStruct, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^ must be a struct, enum, or union in the current crate + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: impls of auto traits for field representing types not supported + --> $DIR/deny-weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^----^^^^^----------------------^^^ + | | | + | | field representing type + | auto trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0120`. diff --git a/tests/ui/field_representing_types/deny-weird-impls.rs b/tests/ui/field_representing_types/deny-weird-impls.rs new file mode 100644 index 0000000000000..e25466fb83078 --- /dev/null +++ b/tests/ui/field_representing_types/deny-weird-impls.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub struct MyStruct(()); + +impl Drop for field_of!(MyStruct, 0) { + //~^ ERROR: the `Drop` trait may only be implemented for local structs, enums, and unions [E0120] + fn drop(&mut self) {} +} + +unsafe impl Send for field_of!(MyStruct, 0) {} +//~^ ERROR: + +fn main() {} diff --git a/tests/ui/field_representing_types/enum.rs b/tests/ui/field_representing_types/enum.rs new file mode 100644 index 0000000000000..694c6704d2551 --- /dev/null +++ b/tests/ui/field_representing_types/enum.rs @@ -0,0 +1,21 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[repr(C, i8)] +pub enum Foo { + A { a: i32, b: i64 }, + B { x: i64, y: i32 }, +} + +fn main() { + assert_eq!(::OFFSET, 8); + assert_eq!(::OFFSET, 16); + + assert_eq!(::OFFSET, 8); + assert_eq!(::OFFSET, 16); +} diff --git a/tests/ui/field_representing_types/incoherent-impl.next.stderr b/tests/ui/field_representing_types/incoherent-impl.next.stderr new file mode 100644 index 0000000000000..8e5450eb47bcb --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.next.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:22:1 + | +LL | impl ForeignTrait for field_of!(Point, x) {} + | ^^^^^^^^^^^^^^^^^^^^^^------------------- + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:25:1 + | +LL | impl ForeignTrait for field_of!((usize, usize), 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^---------------------------- + | | + | this is not defined in the current crate because tuples are always foreign + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_representing_types/incoherent-impl.old.stderr b/tests/ui/field_representing_types/incoherent-impl.old.stderr new file mode 100644 index 0000000000000..8e5450eb47bcb --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.old.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:22:1 + | +LL | impl ForeignTrait for field_of!(Point, x) {} + | ^^^^^^^^^^^^^^^^^^^^^^------------------- + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:25:1 + | +LL | impl ForeignTrait for field_of!((usize, usize), 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^---------------------------- + | | + | this is not defined in the current crate because tuples are always foreign + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_representing_types/incoherent-impl.rs b/tests/ui/field_representing_types/incoherent-impl.rs new file mode 100644 index 0000000000000..abc00f3426de6 --- /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 +#![allow(incomplete_features)] +#![feature(field_projections)] +extern crate extern_crate; + +use std::field::field_of; + +use extern_crate::{ForeignTrait, Point}; + +pub trait MyTrait {} + +impl MyTrait for field_of!(Point, x) {} +impl MyTrait for field_of!(Player, pos) {} +impl MyTrait for field_of!((usize, usize), 0) {} + +pub struct Player { + pos: Point, +} + +impl ForeignTrait for field_of!(Point, x) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] +impl ForeignTrait for field_of!(Player, pos) {} +impl ForeignTrait for field_of!((usize, usize), 0) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] + +fn main() {} diff --git a/tests/ui/field_representing_types/not-field-if-packed.next.stderr b/tests/ui/field_representing_types/not-field-if-packed.next.stderr new file mode 100644 index 0000000000000..f46ba9875b136 --- /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): Field` is not satisfied + --> $DIR/not-field-if-packed.rs:14:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-packed.rs:11:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_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..f46ba9875b136 --- /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): Field` is not satisfied + --> $DIR/not-field-if-packed.rs:14:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-packed.rs:11:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-packed.rs b/tests/ui/field_representing_types/not-field-if-packed.rs new file mode 100644 index 0000000000000..c434740579bc7 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.rs @@ -0,0 +1,16 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[repr(packed)] +pub struct MyStruct(usize); + +fn assert_field() {} + +fn main() { + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 0): Field` is not satisfied [E0277] +} diff --git a/tests/ui/field_representing_types/send.rs b/tests/ui/field_representing_types/send.rs new file mode 100644 index 0000000000000..478f50316873f --- /dev/null +++ b/tests/ui/field_representing_types/send.rs @@ -0,0 +1,20 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![feature(field_projections)] +#![allow(incomplete_features, dead_code)] +use std::field::field_of; + +struct Foo { + field: u32, +} +struct Bar { + bar_field: u32, +} + +fn is_send() {} + +fn main() { + is_send::(); + is_send::(); +} diff --git a/tests/ui/field_representing_types/struct.rs b/tests/ui/field_representing_types/struct.rs new file mode 100644 index 0000000000000..0f85fba95dd05 --- /dev/null +++ b/tests/ui/field_representing_types/struct.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[repr(C)] +pub struct Foo { + a: i32, + b: i64, +} + +fn main() { + assert_eq!(::OFFSET, 0); + assert_eq!(::OFFSET, 8); +} diff --git a/tests/ui/field_representing_types/union.rs b/tests/ui/field_representing_types/union.rs new file mode 100644 index 0000000000000..4a1ca0d00f90c --- /dev/null +++ b/tests/ui/field_representing_types/union.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![allow(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[expect(dead_code)] +pub union Foo { + a: i32, + b: i64, +} + +fn main() { + assert_eq!(::OFFSET, 0); + assert_eq!(::OFFSET, 0); +}