From 2c79213bc5c0c0e071fa73c144efaf563f469234 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:20:02 +0200 Subject: [PATCH 1/6] field representing types: remove explicit `Send` and `Sync` impls Commit cb37ee2c87be ("make field representing types invariant over the base type") made the auto-trait impl work even if `T: !Send` or `T: !Sync` thus we can remove these explicit unsafe impls while FRTs still implement `Send` and `Sync`. --- library/core/src/field.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 0e537e2f92fcf..4c479546a977f 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -14,18 +14,6 @@ pub struct FieldRepresentingType T>, } -// SAFETY: `FieldRepresentingType` doesn't contain any `T` -unsafe impl Send - for FieldRepresentingType -{ -} - -// SAFETY: `FieldRepresentingType` doesn't contain any `T` -unsafe impl Sync - for FieldRepresentingType -{ -} - impl Copy for FieldRepresentingType { From 59f18c21b08eccd2429a4496d26f0e83b959c09c Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:22:28 +0200 Subject: [PATCH 2/6] field representing types: implement Debug through reflection Using the reflection experiment, we can print the actual names of the field that an FRT is referring to. In case this breaks, there is an alternative implementation in the note comment. --- library/core/src/field.rs | 48 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 4c479546a977f..dfab5a836db3f 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -1,11 +1,11 @@ //! Field Reflection +use crate::fmt; use crate::marker::PhantomData; /// Field Representing Type #[unstable(feature = "field_representing_type_raw", issue = "none")] #[lang = "field_representing_type"] -#[expect(missing_debug_implementations)] #[fundamental] pub struct FieldRepresentingType { // We want this type to be invariant over `T`, because otherwise `field_of!(Struct<'short>, @@ -14,6 +14,52 @@ pub struct FieldRepresentingType T>, } +impl fmt::Debug + for FieldRepresentingType +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + enum Member { + Name(&'static str), + Index(u32), + } + impl fmt::Display for Member { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(name) => fmt::Display::fmt(name, f), + Self::Index(idx) => fmt::Display::fmt(idx, f), + } + } + } + let (variant, field) = const { + use crate::mem::type_info::{Type, TypeKind}; + match Type::of::().kind { + TypeKind::Struct(struct_) => { + (None, Member::Name(struct_.fields[FIELD as usize].name)) + } + TypeKind::Tuple(_) => (None, Member::Index(FIELD)), + TypeKind::Enum(enum_) => { + let variant = &enum_.variants[VARIANT as usize]; + (Some(variant.name), Member::Name(variant.fields[FIELD as usize].name)) + } + TypeKind::Union(union) => (None, Member::Name(union.fields[FIELD as usize].name)), + _ => unreachable!(), + } + }; + let type_name = const { crate::any::type_name::() }; + match variant { + Some(variant) => write!(f, "field_of!({type_name}, {variant}.{field})"), + None => write!(f, "field_of!({type_name}, {field})"), + } + // NOTE: if there are changes in the reflection work and the above no + // longer compiles, then the following debug impl could also work in + // the meantime: + // ```rust + // let type_name = const { type_name::() }; + // write!(f, "field_of!({type_name}, {VARIANT}.{FIELD})") + // ``` + } +} + impl Copy for FieldRepresentingType { From f3719b1686fdf28f7c2e198525cf7487152b4134 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:27:12 +0200 Subject: [PATCH 3/6] field representing types: implement Default --- library/core/src/field.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index dfab5a836db3f..0af1d9902c425 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -73,6 +73,14 @@ impl Clone } } +impl Default + for FieldRepresentingType +{ + fn default() -> Self { + Self { _phantom: PhantomData::default() } + } +} + /// Expands to the field representing type of the given field. /// /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the From 104914e614ee0f90fa6ac5f25dbb7512ffcc1651 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:28:14 +0200 Subject: [PATCH 4/6] field representing types: implement Hash, Eq & Ord --- library/core/src/field.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 0af1d9902c425..2289ce3889557 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -1,6 +1,7 @@ //! Field Reflection use crate::fmt; +use crate::hash::{Hash, Hasher}; use crate::marker::PhantomData; /// Field Representing Type @@ -81,6 +82,43 @@ impl Default } } +impl Hash + for FieldRepresentingType +{ + fn hash(&self, state: &mut H) { + self._phantom.hash(state); + } +} + +impl PartialEq + for FieldRepresentingType +{ + fn eq(&self, other: &Self) -> bool { + self._phantom == other._phantom + } +} + +impl Eq + for FieldRepresentingType +{ +} + +impl PartialOrd + for FieldRepresentingType +{ + fn partial_cmp(&self, other: &Self) -> Option { + self._phantom.partial_cmp(&other._phantom) + } +} + +impl Ord + for FieldRepresentingType +{ + fn cmp(&self, other: &Self) -> crate::cmp::Ordering { + self._phantom.cmp(&other._phantom) + } +} + /// Expands to the field representing type of the given field. /// /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the From ca700edee3aaf5d9f8a63ed41585c32c8a0ac990 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:29:10 +0200 Subject: [PATCH 5/6] field representing types: test all implemented traits --- tests/ui/field_representing_types/traits.rs | 36 +++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/tests/ui/field_representing_types/traits.rs b/tests/ui/field_representing_types/traits.rs index 6b5bb15f9ee9a..bc31376e60ea2 100644 --- a/tests/ui/field_representing_types/traits.rs +++ b/tests/ui/field_representing_types/traits.rs @@ -1,13 +1,17 @@ //@ revisions: old next //@ [next] compile-flags: -Znext-solver //@ run-pass -#![feature(field_projections, freeze)] +#![feature(field_projections, freeze, unsafe_unpin)] #![expect(incomplete_features, dead_code)] use std::field::field_of; -use std::marker::{Freeze, Unpin}; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::{Freeze, Unpin, UnsafeUnpin}; +use std::panic::{RefUnwindSafe, UnwindSafe}; struct Struct { field: u32, + tail: [u32], } union Union { @@ -19,11 +23,37 @@ enum Enum { Variant2(u32), } -fn assert_traits() {} +type Tuple = ((), usize, String, dyn Debug); + +fn assert_traits< + T: Sized + + Freeze + + RefUnwindSafe + + Send + + Sync + + Unpin + + UnsafeUnpin + + UnwindSafe + + Copy + + Debug + + Default + + Eq + + Hash + + Ord, +>() { +} fn main() { assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); assert_traits::(); + + assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); } From a041a7609740acc81c8437cb13d95b7320c44bc4 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:10:24 +0200 Subject: [PATCH 6/6] field representing types: add test for unsized types --- .../not-field-if-unsized.next.stderr | 27 +++++++++++++++++++ .../not-field-if-unsized.old.stderr | 27 +++++++++++++++++++ .../not-field-if-unsized.rs | 23 ++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 tests/ui/field_representing_types/not-field-if-unsized.next.stderr create mode 100644 tests/ui/field_representing_types/not-field-if-unsized.old.stderr create mode 100644 tests/ui/field_representing_types/not-field-if-unsized.rs diff --git a/tests/ui/field_representing_types/not-field-if-unsized.next.stderr b/tests/ui/field_representing_types/not-field-if-unsized.next.stderr new file mode 100644 index 0000000000000..e3cee80907720 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:17:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:21:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 1)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.old.stderr b/tests/ui/field_representing_types/not-field-if-unsized.old.stderr new file mode 100644 index 0000000000000..e3cee80907720 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.old.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:17:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:21:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 1)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.rs b/tests/ui/field_representing_types/not-field-if-unsized.rs new file mode 100644 index 0000000000000..739fc91c29423 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.rs @@ -0,0 +1,23 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub trait Trait {} + +pub struct MyStruct(usize, dyn Trait); + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): this requires relaxing the `Base: ?Sized` bound in the + // `Field` trait & compiler changes. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied [E0277] + + // FIXME(FRTs): improve this error message, point to the `dyn Trait` span. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied [E0277] +}