Skip to main content

rustc_builtin_macros/deriving/
default.rs

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