Skip to main content

rustc_builtin_macros/deriving/
from.rs

1use rustc_ast as ast;
2use rustc_ast::{ItemKind, Safety, VariantData};
3use rustc_errors::MultiSpan;
4use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
5use rustc_span::{Ident, Span, kw, sym};
6use thin_vec::thin_vec;
7
8use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty};
9use crate::deriving::generic::*;
10use crate::deriving::pathvec;
11use crate::diagnostics;
12
13/// Generate an implementation of the `From` trait, provided that `item`
14/// is a struct or a tuple struct with exactly one field.
15pub(crate) fn expand_deriving_from(
16    cx: &ExtCtxt<'_>,
17    span: Span,
18    mitem: &ast::MetaItem,
19    annotatable: &Annotatable,
20    push: &mut dyn FnMut(Annotatable),
21    is_const: bool,
22) {
23    let Annotatable::Item(item) = &annotatable else {
24        cx.dcx().bug("derive(From) used on something else than an item");
25    };
26
27    let err_span = || {
28        let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
29        MultiSpan::from_spans(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [span, item_span]))vec![span, item_span])
30    };
31
32    // `#[derive(From)]` is currently usable only on structs with exactly one field.
33    let field = match &item.kind {
34        ItemKind::Struct(_, _, data) => {
35            if let [field] = data.fields() {
36                Ok(field.clone())
37            } else {
38                let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongFieldCount {
39                    span: err_span(),
40                    multiple_fields: data.fields().len() > 1,
41                });
42                Err(guar)
43            }
44        }
45        ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
46            let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongTarget {
47                span: err_span(),
48                kind: &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} {1}", item.kind.article(),
                item.kind.descr()))
    })format!("{} {}", item.kind.article(), item.kind.descr()),
49            });
50            Err(guar)
51        }
52        _ => cx.dcx().bug("Invalid derive(From) ADT input"),
53    };
54
55    let from_type = Ty::AstTy(match field {
56        Ok(ref field) => field.ty.clone(),
57        Err(guar) => cx.ty(span, ast::TyKind::Err(guar)),
58    });
59
60    let path =
61        Path::new_({
    ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
            [sym::convert, sym::From]))
}pathvec!(convert::From), ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Box::new(from_type.clone())]))vec![Box::new(from_type.clone())], PathKind::Std);
62
63    // Generate code like this:
64    //
65    // struct S(u32);
66    // #[automatically_derived]
67    // impl ::core::convert::From<u32> for S {
68    //     #[inline]
69    //     fn from(value: u32) -> S {
70    //         Self(value)
71    //     }
72    // }
73    let from_trait_def = TraitDef {
74        span,
75        path,
76        skip_path_as_bound: true,
77        needs_copy_as_bound_if_packed: false,
78        additional_bounds: SmallVec::new(),
79        supports_unions: false,
80        methods: {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(MethodDef {
                name: sym::from,
                generics: Bounds { bounds: ::alloc::vec::Vec::new() },
                explicit_self: false,
                nonself_args: {
                    let count = 0usize + 1usize;
                    let mut vec = ::smallvec::SmallVec::new();
                    if count <= vec.inline_size() {
                        vec.push((from_type, sym::value));
                        vec
                    } else {
                        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                                    [(from_type, sym::value)])))
                    }
                },
                ret_ty: Ty::Self_,
                attributes: {
                    let len = [()].len();
                    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
                    vec.push(cx.attr_word(sym::inline, span));
                    vec
                },
                fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
                combine_substructure: combine_substructure(|cx, span,
                        substructure|
                        {
                            let field =
                                match field {
                                    Ok(ref field) => field,
                                    Err(guar) => {
                                        return BlockOrExpr::new_expr(DummyResult::raw_expr(span,
                                                    Some(guar)));
                                    }
                                };
                            let self_kw = Ident::new(kw::SelfUpper, span);
                            let expr: Box<ast::Expr> =
                                match substructure.fields {
                                    SubstructureFields::StaticStruct(variant, _) =>
                                        match variant {
                                            VariantData::Struct { .. } =>
                                                cx.expr_struct_ident(span, self_kw,
                                                    {
                                                        let len = [()].len();
                                                        let mut vec = ::thin_vec::ThinVec::with_capacity(len);
                                                        vec.push(cx.field_imm(span, field.ident.unwrap(),
                                                                cx.expr_ident(span, Ident::new(sym::value, span))));
                                                        vec
                                                    }),
                                            VariantData::Tuple(_, _) =>
                                                cx.expr_call_ident(span, self_kw,
                                                    {
                                                        let len = [()].len();
                                                        let mut vec = ::thin_vec::ThinVec::with_capacity(len);
                                                        vec.push(cx.expr_ident(span, Ident::new(sym::value, span)));
                                                        vec
                                                    }),
                                            variant => {
                                                cx.dcx().bug(::alloc::__export::must_use({
                                                            ::alloc::fmt::format(format_args!("Invalid derive(From) ADT variant: {0:?}",
                                                                    variant))
                                                        }));
                                            }
                                        },
                                    _ => cx.dcx().bug("Invalid derive(From) ADT input"),
                                };
                            BlockOrExpr::new_expr(expr)
                        }),
            });
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [MethodDef {
                                name: sym::from,
                                generics: Bounds { bounds: ::alloc::vec::Vec::new() },
                                explicit_self: false,
                                nonself_args: {
                                    let count = 0usize + 1usize;
                                    let mut vec = ::smallvec::SmallVec::new();
                                    if count <= vec.inline_size() {
                                        vec.push((from_type, sym::value));
                                        vec
                                    } else {
                                        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                                                    [(from_type, sym::value)])))
                                    }
                                },
                                ret_ty: Ty::Self_,
                                attributes: {
                                    let len = [()].len();
                                    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
                                    vec.push(cx.attr_word(sym::inline, span));
                                    vec
                                },
                                fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
                                combine_substructure: combine_substructure(|cx, span,
                                        substructure|
                                        {
                                            let field =
                                                match field {
                                                    Ok(ref field) => field,
                                                    Err(guar) => {
                                                        return BlockOrExpr::new_expr(DummyResult::raw_expr(span,
                                                                    Some(guar)));
                                                    }
                                                };
                                            let self_kw = Ident::new(kw::SelfUpper, span);
                                            let expr: Box<ast::Expr> =
                                                match substructure.fields {
                                                    SubstructureFields::StaticStruct(variant, _) =>
                                                        match variant {
                                                            VariantData::Struct { .. } =>
                                                                cx.expr_struct_ident(span, self_kw,
                                                                    {
                                                                        let len = [()].len();
                                                                        let mut vec = ::thin_vec::ThinVec::with_capacity(len);
                                                                        vec.push(cx.field_imm(span, field.ident.unwrap(),
                                                                                cx.expr_ident(span, Ident::new(sym::value, span))));
                                                                        vec
                                                                    }),
                                                            VariantData::Tuple(_, _) =>
                                                                cx.expr_call_ident(span, self_kw,
                                                                    {
                                                                        let len = [()].len();
                                                                        let mut vec = ::thin_vec::ThinVec::with_capacity(len);
                                                                        vec.push(cx.expr_ident(span, Ident::new(sym::value, span)));
                                                                        vec
                                                                    }),
                                                            variant => {
                                                                cx.dcx().bug(::alloc::__export::must_use({
                                                                            ::alloc::fmt::format(format_args!("Invalid derive(From) ADT variant: {0:?}",
                                                                                    variant))
                                                                        }));
                                                            }
                                                        },
                                                    _ => cx.dcx().bug("Invalid derive(From) ADT input"),
                                                };
                                            BlockOrExpr::new_expr(expr)
                                        }),
                            }])))
    }
}smallvec![MethodDef {
81            name: sym::from,
82            generics: Bounds { bounds: vec![] },
83            explicit_self: false,
84            nonself_args: smallvec![(from_type, sym::value)],
85            ret_ty: Ty::Self_,
86            attributes: thin_vec![cx.attr_word(sym::inline, span)],
87            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
88            combine_substructure: combine_substructure(|cx, span, substructure| {
89                let field = match field {
90                    Ok(ref field) => field,
91                    Err(guar) => {
92                        return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar)));
93                    }
94                };
95
96                let self_kw = Ident::new(kw::SelfUpper, span);
97                let expr: Box<ast::Expr> = match substructure.fields {
98                    SubstructureFields::StaticStruct(variant, _) => match variant {
99                        // Self { field: value }
100                        VariantData::Struct { .. } => cx.expr_struct_ident(
101                            span,
102                            self_kw,
103                            thin_vec![cx.field_imm(
104                                span,
105                                field.ident.unwrap(),
106                                cx.expr_ident(span, Ident::new(sym::value, span))
107                            )],
108                        ),
109                        // Self(value)
110                        VariantData::Tuple(_, _) => cx.expr_call_ident(
111                            span,
112                            self_kw,
113                            thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))],
114                        ),
115                        variant => {
116                            cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}"));
117                        }
118                    },
119                    _ => cx.dcx().bug("Invalid derive(From) ADT input"),
120                };
121                BlockOrExpr::new_expr(expr)
122            }),
123        }],
124        associated_types: SmallVec::new(),
125        is_const,
126        safety: Safety::Default,
127        document: true,
128    };
129
130    from_trait_def.expand(cx, mitem, annotatable, push);
131}