about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2025-08-15 18:13:28 -0400
committerGitHub <noreply@github.com>2025-08-15 18:13:28 -0400
commit2b1a288dfc5a2cce12b8c1a2eef22f89d894c094 (patch)
tree3209bcfa71772aeb8fc4294944742f4a91c3ed10 /tests
parentd077146a15fd1f31c30fbb37cec70e2325a05adf (diff)
parent1f3a7471bfb05a5fd76309545de0412d265e28be (diff)
downloadrust-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.rs11
-rw-r--r--tests/ui/deriving/deriving-all-codegen.stdout148
-rw-r--r--tests/ui/deriving/deriving-from-wrong-target.rs38
-rw-r--r--tests/ui/deriving/deriving-from-wrong-target.stderr115
-rw-r--r--tests/ui/deriving/deriving-from.rs58
-rw-r--r--tests/ui/feature-gates/feature-gate-derive-from.rs6
-rw-r--r--tests/ui/feature-gates/feature-gate-derive-from.stderr13
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`.