Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 89 additions & 9 deletions library/core/src/field.rs
Original file line number Diff line number Diff line change
@@ -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>,
Expand All @@ -14,16 +15,50 @@ pub struct FieldRepresentingType<T: ?Sized, const VARIANT: u32, const FIELD: u32
_phantom: PhantomData<fn(T) -> T>,
Copy link
Copy Markdown
Contributor

@GrigorenkoPV GrigorenkoPV Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PhantomInvariant<T>? Or was this already discussed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't notice that that type was finally added! I'll change this the next time I make some changes here

}

// 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
Expand All @@ -39,6 +74,51 @@ impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Clone
}
}

impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Default
Comment thread
BennoLossin marked this conversation as resolved.
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
Expand Down
27 changes: 27 additions & 0 deletions tests/ui/field_representing_types/not-field-if-unsized.next.stderr
Original file line number Diff line number Diff line change
@@ -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`.
27 changes: 27 additions & 0 deletions tests/ui/field_representing_types/not-field-if-unsized.old.stderr
Original file line number Diff line number Diff line change
@@ -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`.
23 changes: 23 additions & 0 deletions tests/ui/field_representing_types/not-field-if-unsized.rs
Original file line number Diff line number Diff line change
@@ -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]
}
36 changes: 33 additions & 3 deletions tests/ui/field_representing_types/traits.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)>();
}
Loading