Skip to main content

rustc_builtin_macros/deriving/
from.rs

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