Implement #[derive(From)]

This commit is contained in:
Jakub Beránek 2025-08-15 12:07:15 +02:00
parent c0839ea7d2
commit 1f3a7471bf
No known key found for this signature in database
GPG key ID: 909CD0D26483516B
10 changed files with 530 additions and 6 deletions

View file

@ -222,6 +222,15 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
builtin_macros_format_use_positional = consider using a positional formatting argument instead
builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind}
builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields ->
[true] multiple fields
*[false] no fields
}
builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
.note = only one `#[default]` attribute is needed
.label = `#[default]` used here

View file

@ -1,13 +1,132 @@
use rustc_ast as ast;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Span, sym};
use rustc_ast::{ItemKind, VariantData};
use rustc_errors::MultiSpan;
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::{Ident, Span, kw, sym};
use thin_vec::thin_vec;
use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty};
use crate::deriving::generic::{
BlockOrExpr, FieldlessVariantsStrategy, MethodDef, SubstructureFields, TraitDef,
combine_substructure,
};
use crate::deriving::pathvec_std;
use crate::errors;
/// Generate an implementation of the `From` trait, provided that `item`
/// is a struct or a tuple struct with exactly one field.
pub(crate) fn expand_deriving_from(
cx: &ExtCtxt<'_>,
span: Span,
mitem: &ast::MetaItem,
item: &Annotatable,
annotatable: &Annotatable,
push: &mut dyn FnMut(Annotatable),
is_const: bool,
) {
let Annotatable::Item(item) = &annotatable else {
cx.dcx().bug("derive(From) used on something else than an item");
};
// #[derive(From)] is currently usable only on structs with exactly one field.
let field = if let ItemKind::Struct(_, _, data) = &item.kind
&& let [field] = data.fields()
{
Some(field.clone())
} else {
None
};
let from_type = match &field {
Some(field) => Ty::AstTy(field.ty.clone()),
// We don't have a type to put into From<...> if we don't have a single field, so just put
// unit there.
None => Ty::Unit,
};
let path =
Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
// Generate code like this:
//
// struct S(u32);
// #[automatically_derived]
// impl ::core::convert::From<u32> for S {
// #[inline]
// fn from(value: u32) -> S {
// Self(value)
// }
// }
let from_trait_def = TraitDef {
span,
path,
skip_path_as_bound: true,
needs_copy_as_bound_if_packed: false,
additional_bounds: Vec::new(),
supports_unions: false,
methods: vec![MethodDef {
name: sym::from,
generics: Bounds { bounds: vec![] },
explicit_self: false,
nonself_args: vec![(from_type, sym::value)],
ret_ty: Ty::Self_,
attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(Box::new(|cx, span, substructure| {
let Some(field) = &field else {
let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
let err_span = MultiSpan::from_spans(vec![span, item_span]);
let error = match &item.kind {
ItemKind::Struct(_, _, data) => {
cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
span: err_span,
multiple_fields: data.fields().len() > 1,
})
}
ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
cx.dcx().emit_err(errors::DeriveFromWrongTarget {
span: err_span,
kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
})
}
_ => cx.dcx().bug("Invalid derive(From) ADT input"),
};
return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error)));
};
let self_kw = Ident::new(kw::SelfUpper, span);
let expr: Box<ast::Expr> = match substructure.fields {
SubstructureFields::StaticStruct(variant, _) => match variant {
// Self {
// field: value
// }
VariantData::Struct { .. } => cx.expr_struct_ident(
span,
self_kw,
thin_vec![cx.field_imm(
span,
field.ident.unwrap(),
cx.expr_ident(span, Ident::new(sym::value, span))
)],
),
// Self(value)
VariantData::Tuple(_, _) => cx.expr_call_ident(
span,
self_kw,
thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))],
),
variant => {
cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}"));
}
},
_ => cx.dcx().bug("Invalid derive(From) ADT input"),
};
BlockOrExpr::new_expr(expr)
})),
}],
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
};
from_trait_def.expand(cx, mitem, annotatable, push);
}

View file

@ -2,7 +2,7 @@
//! when specifying impls to be derived.
pub(crate) use Ty::*;
use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind};
use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind, TyKind};
use rustc_expand::base::ExtCtxt;
use rustc_span::source_map::respan;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
@ -65,7 +65,7 @@ impl Path {
}
}
/// A type. Supports pointers, Self, and literals.
/// A type. Supports pointers, Self, literals, unit or an arbitrary AST path.
#[derive(Clone)]
pub(crate) enum Ty {
Self_,
@ -76,6 +76,8 @@ pub(crate) enum Ty {
Path(Path),
/// For () return types.
Unit,
/// An arbitrary type.
AstTy(Box<ast::Ty>),
}
pub(crate) fn self_ref() -> Ty {
@ -101,6 +103,7 @@ impl Ty {
let ty = ast::TyKind::Tup(ThinVec::new());
cx.ty(span, ty)
}
AstTy(ty) => ty.clone(),
}
}
@ -132,6 +135,10 @@ impl Ty {
cx.path_all(span, false, vec![self_ty], params)
}
Path(p) => p.to_path(cx, span, self_ty, generics),
AstTy(ty) => match &ty.kind {
TyKind::Path(_, path) => path.clone(),
_ => cx.dcx().span_bug(span, "non-path in a path in generic `derive`"),
},
Ref(..) => cx.dcx().span_bug(span, "ref in a path in generic `derive`"),
Unit => cx.dcx().span_bug(span, "unit in a path in generic `derive`"),
}

View file

@ -446,6 +446,24 @@ pub(crate) struct DefaultHasArg {
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_derive_from_wrong_target)]
#[note(builtin_macros_derive_from_usage_note)]
pub(crate) struct DeriveFromWrongTarget<'a> {
#[primary_span]
pub(crate) span: MultiSpan,
pub(crate) kind: &'a str,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_derive_from_wrong_field_count)]
#[note(builtin_macros_derive_from_usage_note)]
pub(crate) struct DeriveFromWrongFieldCount {
#[primary_span]
pub(crate) span: MultiSpan,
pub(crate) multiple_fields: bool,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_derive_macro_call)]
pub(crate) struct DeriveMacroCall {

View file

@ -392,6 +392,7 @@ symbols! {
__D,
__H,
__S,
__T,
__awaitee,
__try_var,
_t,
@ -745,6 +746,7 @@ symbols! {
contracts_ensures,
contracts_internals,
contracts_requires,
convert,
convert_identity,
copy,
copy_closures,
@ -2331,6 +2333,7 @@ symbols! {
va_start,
val,
validity,
value,
values,
var,
variant_count,

View file

@ -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 {

View file

@ -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) -> () {

View file

@ -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() {}

View file

@ -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`.

View file

@ -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);
}