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