rustc_builtin_macros/deriving/
default.rs1use 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: 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]),
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.fields {
StaticStruct(variant_data) => {
default_struct_substructure(cx, trait_span, substr,
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.fields {
StaticStruct(variant_data) => {
default_struct_substructure(cx, trait_span, substr,
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.fields {
40 StaticStruct(variant_data) => {
41 default_struct_substructure(cx, trait_span, substr, 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 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 variant_data: &VariantData,
71) -> BlockOrExpr {
72 let expr = match variant_data {
73 VariantData::Unit(_) => cx.expr_ident(trait_span, substr.type_ident),
74 VariantData::Tuple(fields, _) => {
75 let exprs = fields
76 .iter()
77 .map(|field| default_call(cx, field.span.with_ctxt(trait_span.ctxt())))
78 .collect();
79 cx.expr_call_ident(trait_span, substr.type_ident, exprs)
80 }
81 VariantData::Struct { fields, .. } => {
82 let default_fields = fields
83 .iter()
84 .map(|field| {
85 let span = field.span.with_ctxt(trait_span.ctxt());
86 let value = if let Some(extras) = &field.extras
87 && let Some(default_val) = &extras.default
88 {
89 cx.expr(
91 default_val.value.span,
92 ast::ExprKind::ConstBlock(default_val.clone()),
93 )
94 } else {
95 default_call(cx, span)
97 };
98 cx.field_imm(span, field.ident.unwrap(), value)
99 })
100 .collect();
101 cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
102 }
103 };
104 BlockOrExpr::new_expr(expr)
105}
106
107fn default_enum_substructure(
108 cx: &ExtCtxt<'_>,
109 trait_span: Span,
110 enum_def: &EnumDef,
111 item_span: Span,
112) -> BlockOrExpr {
113 let expr = match try {
114 let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?;
115 validate_default_attribute(cx, default_variant)?;
116 default_variant
117 } {
118 Ok(default_variant) => {
119 match &default_variant.data {
121 VariantData::Unit(_) => cx.expr_path(cx.path(
122 default_variant.span,
123 ::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],
124 )),
125 VariantData::Struct { fields, .. } => {
126 let default_fields = fields
129 .iter()
130 .map(|field| {
131 cx.field_imm(
132 field.span,
133 field.ident.unwrap(),
134 match field.default_value() {
135 None => default_call(cx, field.span),
137 Some(val) => cx.expr(
139 val.value.span,
140 ast::ExprKind::ConstBlock(val.clone()),
141 ),
142 },
143 )
144 })
145 .collect();
146 let path = cx.path(
147 default_variant.span,
148 ::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![
149 Ident::new(kw::SelfUpper, default_variant.span),
150 default_variant.ident,
151 ],
152 );
153 cx.expr_struct(default_variant.span, path, default_fields)
154 }
155 VariantData::Tuple(..) => {
157 cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
158 }
159 }
160 }
161 Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
162 };
163 BlockOrExpr::new_expr(expr)
164}
165
166fn extract_default_variant<'a>(
167 cx: &ExtCtxt<'_>,
168 enum_def: &'a EnumDef,
169 trait_span: Span,
170 item_span: Span,
171) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
172 let default_variants: SmallVec<[_; 1]> = enum_def
173 .variants
174 .iter()
175 .filter(|variant| attr::contains_name(&variant.attrs, kw::Default))
176 .collect();
177
178 let variant = match default_variants.as_slice() {
179 [variant] => variant,
180 [] => {
181 let possible_defaults = enum_def
182 .variants
183 .iter()
184 .filter(|variant| #[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)))
185 .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
186
187 let suggs = possible_defaults
188 .map(|v| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
189 .collect();
190 let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant {
191 span: trait_span,
192 item_span,
193 suggs,
194 });
195
196 return Err(guar);
197 }
198 [first, rest @ ..] => {
199 let suggs = default_variants
200 .iter()
201 .filter_map(|variant| {
202 let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
203 let spans: Vec<Span> = default_variants
204 .iter()
205 .flat_map(|v| {
206 attr::filter_by_name(&v.attrs, kw::Default)
207 .filter_map(|attr| (attr.span != keep).then_some(attr.span))
208 })
209 .collect();
210 (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg {
211 spans,
212 ident: variant.ident,
213 })
214 })
215 .collect();
216 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults {
217 span: trait_span,
218 first: first.span,
219 additional: rest.iter().map(|v| v.span).collect(),
220 suggs,
221 });
222 return Err(guar);
223 }
224 };
225
226 if cx.ecfg.features.default_field_values()
227 && let VariantData::Struct { fields, .. } = &variant.data
228 && fields.iter().all(|f| f.default_value().is_some())
229 && !fields.is_empty()
231 {
232 } else if !#[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)) {
234 let post = if cx.ecfg.features.default_field_values() {
235 " or variants where every field has a default value"
236 } else {
237 ""
238 };
239 let guar =
240 cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post });
241 return Err(guar);
242 }
243
244 if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
245 let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault {
246 span: variant.ident.span,
247 non_exhaustive: non_exhaustive_attr.span,
248 });
249
250 return Err(guar);
251 }
252
253 Ok(variant)
254}
255
256fn validate_default_attribute(
257 cx: &ExtCtxt<'_>,
258 default_variant: &rustc_ast::Variant,
259) -> Result<(), ErrorGuaranteed> {
260 let attrs: SmallVec<[_; 1]> =
261 attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
262
263 let attr = match attrs.as_slice() {
264 [attr] => attr,
265 [] => cx.dcx().bug(
266 "this method must only be called with a variant that has a `#[default]` attribute",
267 ),
268 [first, rest @ ..] => {
269 let sugg = diagnostics::MultipleDefaultAttrsSugg {
270 spans: rest.iter().map(|attr| attr.span).collect(),
271 };
272 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs {
273 span: default_variant.ident.span,
274 first: first.span,
275 first_rest: rest[0].span,
276 rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
277 only_one: rest.len() == 1,
278 sugg,
279 });
280
281 return Err(guar);
282 }
283 };
284 if !attr.is_word() {
285 let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span });
286
287 return Err(guar);
288 }
289 Ok(())
290}
291
292struct DetectNonVariantDefaultAttr<'a, 'b> {
293 cx: &'a ExtCtxt<'b>,
294}
295
296impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
297 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
298 if attr.has_name(kw::Default) {
299 let post = if self.cx.ecfg.features.default_field_values() {
300 " or variants where every field has a default value"
301 } else {
302 ""
303 };
304 self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post });
305 }
306
307 rustc_ast::visit::walk_attribute(self, attr);
308 }
309 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
310 self.visit_ident(&v.ident);
311 self.visit_vis(&v.vis);
312 self.visit_variant_data(&v.data);
313 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);
314 for attr in &v.attrs {
315 rustc_ast::visit::walk_attribute(self, attr);
316 }
317 }
318}
319
320fn has_a_default_variant(item: &ast::Item) -> bool {
321 struct HasDefaultAttrOnVariant;
322
323 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
324 type Result = ControlFlow<()>;
325 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
326 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
327 ControlFlow::Break(())
328 } else {
329 ControlFlow::Continue(())
331 }
332 }
333 }
334
335 HasDefaultAttrOnVariant.visit_item(item).is_break()
336}