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::{DummyResult, ExtCtxt};
5use rustc_span::{Ident, Span, kw, sym};
6use thin_vec::thin_vec;
7
8use crate::deriving::generic::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    item: &ast::Item,
19    push: &mut dyn FnMut(Box<ast::Item>),
20    is_const: bool,
21) {
22    let err_span = || {
23        let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
24        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])
25    };
26
27    // `#[derive(From)]` is currently usable only on structs with exactly one field.
28    let field = match &item.kind {
29        ItemKind::Struct(_, _, data) => {
30            if let [field] = data.fields() {
31                Ok(field.clone())
32            } else {
33                let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongFieldCount {
34                    span: err_span(),
35                    multiple_fields: data.fields().len() > 1,
36                });
37                Err(guar)
38            }
39        }
40        ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
41            let guar = cx.dcx().emit_err(diagnostics::DeriveFromWrongTarget {
42                span: err_span(),
43                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()),
44            });
45            Err(guar)
46        }
47        _ => cx.dcx().bug("Invalid derive(From) ADT input"),
48    };
49
50    let from_type = Ty::AstTy(match field {
51        Ok(ref field) => field.ty.clone(),
52        Err(guar) => cx.ty(span, ast::TyKind::Err(guar)),
53    });
54
55    let path = new_path(cx, span, { &[sym::convert, sym::From] }pathvec!(convert::From), &[from_type.clone()]);
56
57    // Generate code like this:
58    //
59    // struct S(u32);
60    // #[automatically_derived]
61    // impl ::core::convert::From<u32> for S {
62    //     #[inline]
63    //     fn from(value: u32) -> S {
64    //         Self(value)
65    //     }
66    // }
67    let from_trait_def = TraitDef {
68        span,
69        path,
70        skip_path_as_bound: true,
71        needs_copy_as_bound_if_packed: false,
72        additional_bounds: SmallVec::new(),
73        supports_unions: false,
74        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)])))
                    }
                },
                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 {
                                    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: 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)])))
                                    }
                                },
                                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 {
                                                    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 {
75            name: sym::from,
76            generics: cx.empty_generics(span),
77            explicit_self: false,
78            nonself_args: smallvec![(from_type, sym::value)],
79            ret_ty: Ty::Self_,
80            attributes: thin_vec![cx.attr_word(sym::inline, span)],
81            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
82            combine_substructure: combine_substructure(|cx, span, substructure| {
83                let field = match field {
84                    Ok(ref field) => field,
85                    Err(guar) => {
86                        return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar)));
87                    }
88                };
89
90                let self_kw = Ident::new(kw::SelfUpper, span);
91                let expr: Box<ast::Expr> = match substructure {
92                    StaticStruct(variant) => match variant {
93                        // Self { field: value }
94                        VariantData::Struct { .. } => cx.expr_struct_ident(
95                            span,
96                            self_kw,
97                            thin_vec![cx.field_imm(
98                                span,
99                                field.ident.unwrap(),
100                                cx.expr_ident(span, Ident::new(sym::value, span))
101                            )],
102                        ),
103                        // Self(value)
104                        VariantData::Tuple(_, _) => cx.expr_call_ident(
105                            span,
106                            self_kw,
107                            thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))],
108                        ),
109                        variant => {
110                            cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}"));
111                        }
112                    },
113                    _ => cx.dcx().bug("Invalid derive(From) ADT input"),
114                };
115                BlockOrExpr::new_expr(expr)
116            }),
117        }],
118        associated_types: SmallVec::new(),
119        is_const,
120        safety: Safety::Default,
121        document: true,
122    };
123
124    from_trait_def.expand(cx, item, push);
125}