Rollup merge of #155588 - BennoLossin:frt-traits, r=Mark-Simulacrum

Implement more traits for FRTs

From https://github.com/rust-lang/rust/pull/154927#discussion_r3068460955.

FRTs now implement the following traits: `Sized + Freeze + RefUnwindSafe + Send + Sync + Unpin + UnsafeUnpin + UnwindSafe + Copy + Debug + Default + Eq + Hash + Ord`.

Let me know if there is any trait missing.

I also removed the explicit  `Send` and `Sync` impls, since commit cb37ee2c87 ("make field representing types invariant over the base type") made the auto-trait impl work even if `T: !Send` or `T: !Sync`. Very happy to see unsafe impls get dropped :)

Note that I used the reflection feature (cc @oli-obk) to print the actual field names in the debug implementation. I think this is a cool way to use it, but if it isn't ready for that, I'm happy to change it to the alternative implementation I gave in the note comment (it's essentially Mark's suggestion but printing `T`'s name instead of `Self`'s).

Since this is a library change, I'll give this to Mark; feel free to also take a look/leave comments, Oli :)

r? @Mark-Simulacrum
This commit is contained in:
Jacob Pratt
2026-04-26 21:56:41 -04:00
committed by GitHub
5 changed files with 199 additions and 12 deletions

View File

@@ -1,11 +1,12 @@
//! Field Reflection
use crate::fmt;
use crate::hash::{Hash, Hasher};
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<T: ?Sized, const VARIANT: u32, const FIELD: u32> {
// We want this type to be invariant over `T`, because otherwise `field_of!(Struct<'short>,
@@ -14,16 +15,50 @@ pub struct FieldRepresentingType<T: ?Sized, const VARIANT: u32, const FIELD: u32
_phantom: PhantomData<fn(T) -> T>,
}
// SAFETY: `FieldRepresentingType` doesn't contain any `T`
unsafe impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Send
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
// SAFETY: `FieldRepresentingType` doesn't contain any `T`
unsafe impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Sync
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> fmt::Debug
for FieldRepresentingType<T, VARIANT, FIELD>
{
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::<T>().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::<T>() };
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::<T>() };
// write!(f, "field_of!({type_name}, {VARIANT}.{FIELD})")
// ```
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Copy
@@ -39,6 +74,51 @@ impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Clone
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Default
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn default() -> Self {
Self { _phantom: PhantomData::default() }
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Hash
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn hash<H: Hasher>(&self, state: &mut H) {
self._phantom.hash(state);
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> PartialEq
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn eq(&self, other: &Self) -> bool {
self._phantom == other._phantom
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Eq
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> PartialOrd
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn partial_cmp(&self, other: &Self) -> Option<crate::cmp::Ordering> {
self._phantom.partial_cmp(&other._phantom)
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Ord
for FieldRepresentingType<T, VARIANT, FIELD>
{
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

View File

@@ -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::<field_of!(MyStruct, 0)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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::<field_of!(MyStruct, 1)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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`.

View File

@@ -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::<field_of!(MyStruct, 0)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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::<field_of!(MyStruct, 1)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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`.

View File

@@ -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<F: Field>() {}
fn main() {
// FIXME(FRTs): this requires relaxing the `Base: ?Sized` bound in the
// `Field` trait & compiler changes.
assert_field::<field_of!(MyStruct, 0)>();
//~^ 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::<field_of!(MyStruct, 1)>();
//~^ ERROR: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied [E0277]
}

View File

@@ -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<T: Send + Sync + Unpin + Copy + Clone + Sized + Freeze>() {}
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::<field_of!(Struct, field)>();
assert_traits::<field_of!(Struct, tail)>();
assert_traits::<field_of!(Union, field)>();
assert_traits::<field_of!(Enum, Variant1.field)>();
assert_traits::<field_of!(Enum, Variant2.0)>();
assert_traits::<field_of!(Tuple, 0)>();
assert_traits::<field_of!(Tuple, 1)>();
assert_traits::<field_of!(Tuple, 2)>();
assert_traits::<field_of!(Tuple, 3)>();
}