diff options
| author | Jacob Pratt <jacob@jhpratt.dev> | 2025-08-15 18:13:28 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-15 18:13:28 -0400 |
| commit | 2b1a288dfc5a2cce12b8c1a2eef22f89d894c094 (patch) | |
| tree | 3209bcfa71772aeb8fc4294944742f4a91c3ed10 /tests | |
| parent | d077146a15fd1f31c30fbb37cec70e2325a05adf (diff) | |
| parent | 1f3a7471bfb05a5fd76309545de0412d265e28be (diff) | |
| download | rust-2b1a288dfc5a2cce12b8c1a2eef22f89d894c094.tar.gz rust-2b1a288dfc5a2cce12b8c1a2eef22f89d894c094.zip | |
Rollup merge of #144922 - Kobzol:derive-from, r=nnethercote
Implement `#[derive(From)]` Implements the `#[derive(From)]` feature ([tracking issue](https://github.com/rust-lang/rust/issues/144889), [RFC](https://github.com/rust-lang/rfcs/pull/3809)). It allows deriving the `From` impl on structs and tuple structs with exactly one field. Some implementation notes: - I wasn't exactly sure which spans to use in the derive generating code, so I just used `span` everywhere. I don't know if it's the Right Thing To Do. In particular the errors when `#[derive(From)]` is used on a struct with an unsized field are weirdly duplicated. - I had to solve an import stability problem, where if I just added the unstable `macro From` to `core::convert`, previously working code like `use std::convert::From` would suddenly require an unstable feature gate, because rustc would think that you're trying to import the unstable macro. `@petrochenkov` suggested that I add the macro the the core prelude instead. This has worked well, although it only works in edition 2021+. Not sure if I botched the prelude somehow and it should live elsewhere (?). - I had to add `Ty::AstTy`, because the `from` function receives an argument with the type of the single field, and the existing variants of the `Ty` enum couldn't represent an arbitrary type.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/ui/deriving/deriving-all-codegen.rs | 11 | ||||
| -rw-r--r-- | tests/ui/deriving/deriving-all-codegen.stdout | 148 | ||||
| -rw-r--r-- | tests/ui/deriving/deriving-from-wrong-target.rs | 38 | ||||
| -rw-r--r-- | tests/ui/deriving/deriving-from-wrong-target.stderr | 115 | ||||
| -rw-r--r-- | tests/ui/deriving/deriving-from.rs | 58 | ||||
| -rw-r--r-- | tests/ui/feature-gates/feature-gate-derive-from.rs | 6 | ||||
| -rw-r--r-- | tests/ui/feature-gates/feature-gate-derive-from.stderr | 13 |
7 files changed, 388 insertions, 1 deletions
diff --git a/tests/ui/deriving/deriving-all-codegen.rs b/tests/ui/deriving/deriving-all-codegen.rs index e2b6804fbd1..00a269ccb5c 100644 --- a/tests/ui/deriving/deriving-all-codegen.rs +++ b/tests/ui/deriving/deriving-all-codegen.rs @@ -16,6 +16,7 @@ #![crate_type = "lib"] #![allow(dead_code)] #![allow(deprecated)] +#![feature(derive_from)] // Empty struct. #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -38,6 +39,14 @@ struct PackedPoint { y: u32, } +#[derive(Clone, Copy, Debug, Default, From, Hash, PartialEq, Eq, PartialOrd, Ord)] +struct TupleSingleField(u32); + +#[derive(Clone, Copy, Debug, Default, From, Hash, PartialEq, Eq, PartialOrd, Ord)] +struct SingleField { + foo: bool, +} + // A large struct. Note: because this derives `Copy`, it gets the simple // `clone` implemention that just does `*self`. #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -86,7 +95,7 @@ struct PackedManualCopy(u32); impl Copy for PackedManualCopy {} // A struct with an unsized field. Some derives are not usable in this case. -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, From, Hash, PartialEq, Eq, PartialOrd, Ord)] struct Unsized([u32]); trait Trait { diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index 0e4bfa30257..78b93f39b9e 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -17,6 +17,7 @@ #![crate_type = "lib"] #![allow(dead_code)] #![allow(deprecated)] +#![feature(derive_from)] #[macro_use] extern crate std; #[prelude_import] @@ -249,6 +250,148 @@ impl ::core::cmp::Ord for PackedPoint { } } +struct TupleSingleField(u32); +#[automatically_derived] +impl ::core::clone::Clone for TupleSingleField { + #[inline] + fn clone(&self) -> TupleSingleField { + let _: ::core::clone::AssertParamIsClone<u32>; + *self + } +} +#[automatically_derived] +impl ::core::marker::Copy for TupleSingleField { } +#[automatically_derived] +impl ::core::fmt::Debug for TupleSingleField { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_tuple_field1_finish(f, + "TupleSingleField", &&self.0) + } +} +#[automatically_derived] +impl ::core::default::Default for TupleSingleField { + #[inline] + fn default() -> TupleSingleField { + TupleSingleField(::core::default::Default::default()) + } +} +#[automatically_derived] +impl ::core::convert::From<u32> for TupleSingleField { + #[inline] + fn from(value: u32) -> TupleSingleField { Self(value) } +} +#[automatically_derived] +impl ::core::hash::Hash for TupleSingleField { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + ::core::hash::Hash::hash(&self.0, state) + } +} +#[automatically_derived] +impl ::core::marker::StructuralPartialEq for TupleSingleField { } +#[automatically_derived] +impl ::core::cmp::PartialEq for TupleSingleField { + #[inline] + fn eq(&self, other: &TupleSingleField) -> bool { self.0 == other.0 } +} +#[automatically_derived] +impl ::core::cmp::Eq for TupleSingleField { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq<u32>; + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for TupleSingleField { + #[inline] + fn partial_cmp(&self, other: &TupleSingleField) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for TupleSingleField { + #[inline] + fn cmp(&self, other: &TupleSingleField) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} + +struct SingleField { + foo: bool, +} +#[automatically_derived] +impl ::core::clone::Clone for SingleField { + #[inline] + fn clone(&self) -> SingleField { + let _: ::core::clone::AssertParamIsClone<bool>; + *self + } +} +#[automatically_derived] +impl ::core::marker::Copy for SingleField { } +#[automatically_derived] +impl ::core::fmt::Debug for SingleField { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field1_finish(f, "SingleField", + "foo", &&self.foo) + } +} +#[automatically_derived] +impl ::core::default::Default for SingleField { + #[inline] + fn default() -> SingleField { + SingleField { foo: ::core::default::Default::default() } + } +} +#[automatically_derived] +impl ::core::convert::From<bool> for SingleField { + #[inline] + fn from(value: bool) -> SingleField { Self { foo: value } } +} +#[automatically_derived] +impl ::core::hash::Hash for SingleField { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + ::core::hash::Hash::hash(&self.foo, state) + } +} +#[automatically_derived] +impl ::core::marker::StructuralPartialEq for SingleField { } +#[automatically_derived] +impl ::core::cmp::PartialEq for SingleField { + #[inline] + fn eq(&self, other: &SingleField) -> bool { self.foo == other.foo } +} +#[automatically_derived] +impl ::core::cmp::Eq for SingleField { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq<bool>; + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for SingleField { + #[inline] + fn partial_cmp(&self, other: &SingleField) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::cmp::PartialOrd::partial_cmp(&self.foo, &other.foo) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for SingleField { + #[inline] + fn cmp(&self, other: &SingleField) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.foo, &other.foo) + } +} + // A large struct. Note: because this derives `Copy`, it gets the simple // `clone` implemention that just does `*self`. struct Big { @@ -572,6 +715,11 @@ impl ::core::fmt::Debug for Unsized { } } #[automatically_derived] +impl ::core::convert::From<[u32]> for Unsized { + #[inline] + fn from(value: [u32]) -> Unsized { Self(value) } +} +#[automatically_derived] impl ::core::hash::Hash for Unsized { #[inline] fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { diff --git a/tests/ui/deriving/deriving-from-wrong-target.rs b/tests/ui/deriving/deriving-from-wrong-target.rs new file mode 100644 index 00000000000..57e009cae69 --- /dev/null +++ b/tests/ui/deriving/deriving-from-wrong-target.rs @@ -0,0 +1,38 @@ +//@ edition: 2021 +//@ check-fail + +#![feature(derive_from)] +#![allow(dead_code)] + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with no fields +struct S1; + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with no fields +struct S2 {} + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with multiple fields +struct S3(u32, bool); + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with multiple fields +struct S4 { + a: u32, + b: bool, +} + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on an enum +enum E1 {} + +#[derive(From)] +//~^ ERROR the size for values of type `T` cannot be known at compilation time [E0277] +//~| ERROR the size for values of type `T` cannot be known at compilation time [E0277] +struct SUnsizedField<T: ?Sized> { + last: T, + //~^ ERROR the size for values of type `T` cannot be known at compilation time [E0277] +} + +fn main() {} diff --git a/tests/ui/deriving/deriving-from-wrong-target.stderr b/tests/ui/deriving/deriving-from-wrong-target.stderr new file mode 100644 index 00000000000..13593c95973 --- /dev/null +++ b/tests/ui/deriving/deriving-from-wrong-target.stderr @@ -0,0 +1,115 @@ +error: `#[derive(From)]` used on a struct with no fields + --> $DIR/deriving-from-wrong-target.rs:7:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S1; + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on a struct with no fields + --> $DIR/deriving-from-wrong-target.rs:11:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S2 {} + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on a struct with multiple fields + --> $DIR/deriving-from-wrong-target.rs:15:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S3(u32, bool); + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on a struct with multiple fields + --> $DIR/deriving-from-wrong-target.rs:19:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S4 { + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on an enum + --> $DIR/deriving-from-wrong-target.rs:26:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | enum E1 {} + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/deriving-from-wrong-target.rs:30:10 + | +LL | #[derive(From)] + | ^^^^ doesn't have a size known at compile-time +... +LL | struct SUnsizedField<T: ?Sized> { + | - this type parameter needs to be `Sized` + | +note: required by an implicit `Sized` bound in `From` + --> $SRC_DIR/core/src/convert/mod.rs:LL:COL +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct SUnsizedField<T: ?Sized> { +LL + struct SUnsizedField<T> { + | + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/deriving-from-wrong-target.rs:30:10 + | +LL | #[derive(From)] + | ^^^^ doesn't have a size known at compile-time +... +LL | struct SUnsizedField<T: ?Sized> { + | - this type parameter needs to be `Sized` + | +note: required because it appears within the type `SUnsizedField<T>` + --> $DIR/deriving-from-wrong-target.rs:33:8 + | +LL | struct SUnsizedField<T: ?Sized> { + | ^^^^^^^^^^^^^ + = note: the return type of a function must have a statically known size +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct SUnsizedField<T: ?Sized> { +LL + struct SUnsizedField<T> { + | + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/deriving-from-wrong-target.rs:34:11 + | +LL | struct SUnsizedField<T: ?Sized> { + | - this type parameter needs to be `Sized` +LL | last: T, + | ^ doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct SUnsizedField<T: ?Sized> { +LL + struct SUnsizedField<T> { + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | last: &T, + | + + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/deriving/deriving-from.rs b/tests/ui/deriving/deriving-from.rs new file mode 100644 index 00000000000..ff4c5b4c426 --- /dev/null +++ b/tests/ui/deriving/deriving-from.rs @@ -0,0 +1,58 @@ +//@ edition: 2021 +//@ run-pass + +#![feature(derive_from)] + +#[derive(From)] +struct TupleSimple(u32); + +#[derive(From)] +struct TupleNonPathType([u32; 4]); + +#[derive(From)] +struct TupleWithRef<'a, T>(&'a T); + +#[derive(From)] +struct TupleSWithBound<T: std::fmt::Debug>(T); + +#[derive(From)] +struct RawIdentifier { + r#use: u32, +} + +#[derive(From)] +struct Field { + foo: bool, +} + +#[derive(From)] +struct Const<const C: usize> { + foo: [u32; C], +} + +fn main() { + let a = 42u32; + let b: [u32; 4] = [0, 1, 2, 3]; + let c = true; + + let s1: TupleSimple = a.into(); + assert_eq!(s1.0, a); + + let s2: TupleNonPathType = b.into(); + assert_eq!(s2.0, b); + + let s3: TupleWithRef<u32> = (&a).into(); + assert_eq!(s3.0, &a); + + let s4: TupleSWithBound<u32> = a.into(); + assert_eq!(s4.0, a); + + let s5: RawIdentifier = a.into(); + assert_eq!(s5.r#use, a); + + let s6: Field = c.into(); + assert_eq!(s6.foo, c); + + let s7: Const<4> = b.into(); + assert_eq!(s7.foo, b); +} diff --git a/tests/ui/feature-gates/feature-gate-derive-from.rs b/tests/ui/feature-gates/feature-gate-derive-from.rs new file mode 100644 index 00000000000..12440356ddf --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-derive-from.rs @@ -0,0 +1,6 @@ +//@ edition: 2021 + +#[derive(From)] //~ ERROR use of unstable library feature `derive_from` +struct Foo(u32); + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-derive-from.stderr b/tests/ui/feature-gates/feature-gate-derive-from.stderr new file mode 100644 index 00000000000..d58dcdd7541 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-derive-from.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `derive_from` + --> $DIR/feature-gate-derive-from.rs:3:10 + | +LL | #[derive(From)] + | ^^^^ + | + = note: see issue #144889 <https://github.com/rust-lang/rust/issues/144889> for more information + = help: add `#![feature(derive_from)]` 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`. |
