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:
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
@@ -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
tests/ui/field_representing_types/not-field-if-unsized.rs
Normal file
23
tests/ui/field_representing_types/not-field-if-unsized.rs
Normal 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]
|
||||
}
|
||||
@@ -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)>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user