Skip to main content

rustc_builtin_macros/deriving/
default.rs

1use core::ops::ControlFlow;
2
3use rustc_ast::visit::visit_opt;
4use rustc_ast::{self as ast, EnumDef, Safety, VariantData, attr};
5use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
6use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
7use smallvec::SmallVec;
8use thin_vec::{ThinVec, thin_vec};
9
10use crate::deriving::generic::ty::*;
11use crate::deriving::generic::*;
12use crate::diagnostics;
13
14pub(crate) fn expand_deriving_default(
15    cx: &ExtCtxt<'_>,
16    span: Span,
17    mitem: &ast::MetaItem,
18    item: &Annotatable,
19    push: &mut dyn FnMut(Annotatable),
20    is_const: bool,
21) {
22    item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
23
24    let trait_def = TraitDef {
25        span,
26        path: Path::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [kw::Default, sym::Default]))vec![kw::Default, sym::Default]),
27        skip_path_as_bound: has_a_default_variant(item),
28        needs_copy_as_bound_if_packed: false,
29        additional_bounds: Vec::new(),
30        supports_unions: false,
31        methods: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [MethodDef {
                    name: kw::Default,
                    generics: Bounds::empty(),
                    explicit_self: false,
                    nonself_args: Vec::new(),
                    ret_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(Box::new(|cx,
                                trait_span, substr|
                                {
                                    match substr.fields {
                                        StaticStruct(_, fields) => {
                                            default_struct_substructure(cx, trait_span, substr, fields)
                                        }
                                        StaticEnum(enum_def) => {
                                            default_enum_substructure(cx, trait_span, enum_def,
                                                item.span())
                                        }
                                        _ =>
                                            cx.dcx().span_bug(trait_span,
                                                "method in `derive(Default)`"),
                                    }
                                })),
                }]))vec![MethodDef {
32            name: kw::Default,
33            generics: Bounds::empty(),
34            explicit_self: false,
35            nonself_args: Vec::new(),
36            ret_ty: Self_,
37            attributes: thin_vec![cx.attr_word(sym::inline, span)],
38            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
39            combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
40                match substr.fields {
41                    StaticStruct(_, fields) => {
42                        default_struct_substructure(cx, trait_span, substr, fields)
43                    }
44                    StaticEnum(enum_def) => {
45                        default_enum_substructure(cx, trait_span, enum_def, item.span())
46                    }
47                    _ => cx.dcx().span_bug(trait_span, "method in `derive(Default)`"),
48                }
49            })),
50        }],
51        associated_types: Vec::new(),
52        is_const,
53        is_staged_api_crate: cx.ecfg.features.staged_api(),
54        safety: Safety::Default,
55        document: true,
56    };
57    trait_def.expand(cx, mitem, item, push)
58}
59
60fn default_call(cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Expr> {
61    // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
62    let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
63    cx.expr_call_global(span, default_ident, ThinVec::new())
64}
65
66fn default_struct_substructure(
67    cx: &ExtCtxt<'_>,
68    trait_span: Span,
69    substr: &Substructure<'_>,
70    summary: &StaticFields<'_>,
71) -> BlockOrExpr {
72    let expr = match summary {
73        Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
74        Unnamed(fields, IsTuple::Yes) => {
75            let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
76            cx.expr_call_ident(trait_span, substr.type_ident, exprs)
77        }
78        Named(fields) => {
79            let default_fields = fields
80                .iter()
81                .map(|&(ident, span, default_val)| {
82                    let value = match default_val {
83                        // We use `Default::default()`.
84                        None => default_call(cx, span),
85                        // We use the field default const expression.
86                        Some(val) => {
87                            cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
88                        }
89                    };
90                    cx.field_imm(span, ident, value)
91                })
92                .collect();
93            cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
94        }
95    };
96    BlockOrExpr::new_expr(expr)
97}
98
99fn default_enum_substructure(
100    cx: &ExtCtxt<'_>,
101    trait_span: Span,
102    enum_def: &EnumDef,
103    item_span: Span,
104) -> BlockOrExpr {
105    let expr = match try {
106        let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?;
107        validate_default_attribute(cx, default_variant)?;
108        default_variant
109    } {
110        Ok(default_variant) => {
111            // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
112            match &default_variant.data {
113                VariantData::Unit(_) => cx.expr_path(cx.path(
114                    default_variant.span,
115                    ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ident::new(kw::SelfUpper, default_variant.span),
                default_variant.ident]))vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
116                )),
117                VariantData::Struct { fields, .. } => {
118                    // This only happens if `#![feature(default_field_values)]`. We have validated
119                    // all fields have default values in the definition.
120                    let default_fields = fields
121                        .iter()
122                        .map(|field| {
123                            cx.field_imm(
124                                field.span,
125                                field.ident.unwrap(),
126                                match &field.default {
127                                    // We use `Default::default()`.
128                                    None => default_call(cx, field.span),
129                                    // We use the field default const expression.
130                                    Some(val) => cx.expr(
131                                        val.value.span,
132                                        ast::ExprKind::ConstBlock(val.clone()),
133                                    ),
134                                },
135                            )
136                        })
137                        .collect();
138                    let path = cx.path(
139                        default_variant.span,
140                        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ident::new(kw::SelfUpper, default_variant.span),
                default_variant.ident]))vec![
141                            Ident::new(kw::SelfUpper, default_variant.span),
142                            default_variant.ident,
143                        ],
144                    );
145                    cx.expr_struct(default_variant.span, path, default_fields)
146                }
147                // Logic error in `extract_default_variant`.
148                VariantData::Tuple(..) => {
149                    cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
150                }
151            }
152        }
153        Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
154    };
155    BlockOrExpr::new_expr(expr)
156}
157
158fn extract_default_variant<'a>(
159    cx: &ExtCtxt<'_>,
160    enum_def: &'a EnumDef,
161    trait_span: Span,
162    item_span: Span,
163) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
164    let default_variants: SmallVec<[_; 1]> = enum_def
165        .variants
166        .iter()
167        .filter(|variant| attr::contains_name(&variant.attrs, kw::Default))
168        .collect();
169
170    let variant = match default_variants.as_slice() {
171        [variant] => variant,
172        [] => {
173            let possible_defaults = enum_def
174                .variants
175                .iter()
176                .filter(|variant| #[allow(non_exhaustive_omitted_patterns)] match variant.data {
    VariantData::Unit(..) => true,
    _ => false,
}matches!(variant.data, VariantData::Unit(..)))
177                .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
178
179            let suggs = possible_defaults
180                .map(|v| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
181                .collect();
182            let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant {
183                span: trait_span,
184                item_span,
185                suggs,
186            });
187
188            return Err(guar);
189        }
190        [first, rest @ ..] => {
191            let suggs = default_variants
192                .iter()
193                .filter_map(|variant| {
194                    let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
195                    let spans: Vec<Span> = default_variants
196                        .iter()
197                        .flat_map(|v| {
198                            attr::filter_by_name(&v.attrs, kw::Default)
199                                .filter_map(|attr| (attr.span != keep).then_some(attr.span))
200                        })
201                        .collect();
202                    (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg {
203                        spans,
204                        ident: variant.ident,
205                    })
206                })
207                .collect();
208            let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults {
209                span: trait_span,
210                first: first.span,
211                additional: rest.iter().map(|v| v.span).collect(),
212                suggs,
213            });
214            return Err(guar);
215        }
216    };
217
218    if cx.ecfg.features.default_field_values()
219        && let VariantData::Struct { fields, .. } = &variant.data
220        && fields.iter().all(|f| f.default.is_some())
221        // Disallow `#[default] Variant {}`
222        && !fields.is_empty()
223    {
224        // Allowed
225    } else if !#[allow(non_exhaustive_omitted_patterns)] match variant.data {
    VariantData::Unit(..) => true,
    _ => false,
}matches!(variant.data, VariantData::Unit(..)) {
226        let post = if cx.ecfg.features.default_field_values() {
227            " or variants where every field has a default value"
228        } else {
229            ""
230        };
231        let guar =
232            cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post });
233        return Err(guar);
234    }
235
236    if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
237        let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault {
238            span: variant.ident.span,
239            non_exhaustive: non_exhaustive_attr.span,
240        });
241
242        return Err(guar);
243    }
244
245    Ok(variant)
246}
247
248fn validate_default_attribute(
249    cx: &ExtCtxt<'_>,
250    default_variant: &rustc_ast::Variant,
251) -> Result<(), ErrorGuaranteed> {
252    let attrs: SmallVec<[_; 1]> =
253        attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
254
255    let attr = match attrs.as_slice() {
256        [attr] => attr,
257        [] => cx.dcx().bug(
258            "this method must only be called with a variant that has a `#[default]` attribute",
259        ),
260        [first, rest @ ..] => {
261            let sugg = diagnostics::MultipleDefaultAttrsSugg {
262                spans: rest.iter().map(|attr| attr.span).collect(),
263            };
264            let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs {
265                span: default_variant.ident.span,
266                first: first.span,
267                first_rest: rest[0].span,
268                rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
269                only_one: rest.len() == 1,
270                sugg,
271            });
272
273            return Err(guar);
274        }
275    };
276    if !attr.is_word() {
277        let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span });
278
279        return Err(guar);
280    }
281    Ok(())
282}
283
284struct DetectNonVariantDefaultAttr<'a, 'b> {
285    cx: &'a ExtCtxt<'b>,
286}
287
288impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
289    fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
290        if attr.has_name(kw::Default) {
291            let post = if self.cx.ecfg.features.default_field_values() {
292                " or variants where every field has a default value"
293            } else {
294                ""
295            };
296            self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post });
297        }
298
299        rustc_ast::visit::walk_attribute(self, attr);
300    }
301    fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
302        self.visit_ident(&v.ident);
303        self.visit_vis(&v.vis);
304        self.visit_variant_data(&v.data);
305        if let Some(x) = &v.disr_expr {
    match ::rustc_ast_ir::visit::VisitorResult::branch(self.visit_anon_const(x))
        {
        core::ops::ControlFlow::Continue(()) =>
            (),
            #[allow(unreachable_code)]
            core::ops::ControlFlow::Break(r) => {
            return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
        }
    };
};visit_opt!(self, visit_anon_const, &v.disr_expr);
306        for attr in &v.attrs {
307            rustc_ast::visit::walk_attribute(self, attr);
308        }
309    }
310}
311
312fn has_a_default_variant(item: &Annotatable) -> bool {
313    struct HasDefaultAttrOnVariant;
314
315    impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
316        type Result = ControlFlow<()>;
317        fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
318            if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
319                ControlFlow::Break(())
320            } else {
321                // no need to walk the variant, we are only looking for top level variants
322                ControlFlow::Continue(())
323            }
324        }
325    }
326
327    item.visit_with(&mut HasDefaultAttrOnVariant).is_break()
328}