@@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
54
54
trait_def. expand ( cx, mitem, item, push)
55
55
}
56
56
57
+ fn default_call ( cx : & ExtCtxt < ' _ > , span : Span ) -> ast:: ptr:: P < ast:: Expr > {
58
+ // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
59
+ let default_ident = cx. std_path ( & [ kw:: Default , sym:: Default , kw:: Default ] ) ;
60
+ cx. expr_call_global ( span, default_ident, ThinVec :: new ( ) )
61
+ }
62
+
57
63
fn default_struct_substructure (
58
64
cx : & ExtCtxt < ' _ > ,
59
65
trait_span : Span ,
60
66
substr : & Substructure < ' _ > ,
61
67
summary : & StaticFields ,
62
68
) -> BlockOrExpr {
63
- // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
64
- let default_ident = cx. std_path ( & [ kw:: Default , sym:: Default , kw:: Default ] ) ;
65
- let default_call = |span| cx. expr_call_global ( span, default_ident. clone ( ) , ThinVec :: new ( ) ) ;
66
-
67
69
let expr = match summary {
68
70
Unnamed ( _, IsTuple :: No ) => cx. expr_ident ( trait_span, substr. type_ident ) ,
69
71
Unnamed ( fields, IsTuple :: Yes ) => {
70
- let exprs = fields. iter ( ) . map ( |sp| default_call ( * sp) ) . collect ( ) ;
72
+ let exprs = fields. iter ( ) . map ( |sp| default_call ( cx , * sp) ) . collect ( ) ;
71
73
cx. expr_call_ident ( trait_span, substr. type_ident , exprs)
72
74
}
73
75
Named ( fields) => {
74
76
let default_fields = fields
75
77
. iter ( )
76
- . map ( |& ( ident, span) | cx. field_imm ( span, ident, default_call ( span) ) )
78
+ . map ( |( ident, span, default_val) | {
79
+ let value = match default_val {
80
+ // We use `Default::default()`.
81
+ None => default_call ( cx, * span) ,
82
+ // We use the field default const expression.
83
+ Some ( val) => {
84
+ cx. expr ( val. value . span , ast:: ExprKind :: ConstBlock ( val. clone ( ) ) )
85
+ }
86
+ } ;
87
+ cx. field_imm ( * span, * ident, value)
88
+ } )
77
89
. collect ( ) ;
78
90
cx. expr_struct_ident ( trait_span, substr. type_ident , default_fields)
79
91
}
@@ -93,10 +105,38 @@ fn default_enum_substructure(
93
105
} {
94
106
Ok ( default_variant) => {
95
107
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
96
- cx. expr_path ( cx. path ( default_variant. span , vec ! [
97
- Ident :: new( kw:: SelfUpper , default_variant. span) ,
98
- default_variant. ident,
99
- ] ) )
108
+ match & default_variant. data {
109
+ VariantData :: Unit ( _) => cx. expr_path ( cx. path ( default_variant. span , vec ! [
110
+ Ident :: new( kw:: SelfUpper , default_variant. span) ,
111
+ default_variant. ident,
112
+ ] ) ) ,
113
+ VariantData :: Struct { fields, .. } => {
114
+ // This only happens if `#![feature(default_field_values)]`. We have validated
115
+ // all fields have default values in the definition.
116
+ let default_fields = fields
117
+ . iter ( )
118
+ . map ( |field| {
119
+ cx. field_imm ( field. span , field. ident . unwrap ( ) , match & field. default {
120
+ // We use `Default::default()`.
121
+ None => default_call ( cx, field. span ) ,
122
+ // We use the field default const expression.
123
+ Some ( val) => {
124
+ cx. expr ( val. value . span , ast:: ExprKind :: ConstBlock ( val. clone ( ) ) )
125
+ }
126
+ } )
127
+ } )
128
+ . collect ( ) ;
129
+ let path = cx. path ( default_variant. span , vec ! [
130
+ Ident :: new( kw:: SelfUpper , default_variant. span) ,
131
+ default_variant. ident,
132
+ ] ) ;
133
+ cx. expr_struct ( default_variant. span , path, default_fields)
134
+ }
135
+ // Logic error in `extract_default_variant`.
136
+ VariantData :: Tuple ( ..) => {
137
+ cx. dcx ( ) . bug ( "encountered tuple variant annotated with `#[default]`" )
138
+ }
139
+ }
100
140
}
101
141
Err ( guar) => DummyResult :: raw_expr ( trait_span, Some ( guar) ) ,
102
142
} ;
@@ -156,8 +196,20 @@ fn extract_default_variant<'a>(
156
196
}
157
197
} ;
158
198
159
- if !matches ! ( variant. data, VariantData :: Unit ( ..) ) {
160
- let guar = cx. dcx ( ) . emit_err ( errors:: NonUnitDefault { span : variant. ident . span } ) ;
199
+ if cx. ecfg . features . default_field_values ( )
200
+ && let VariantData :: Struct { fields, .. } = & variant. data
201
+ && fields. iter ( ) . all ( |f| f. default . is_some ( ) )
202
+ // Disallow `#[default] Variant {}`
203
+ && !fields. is_empty ( )
204
+ {
205
+ // Allowed
206
+ } else if !matches ! ( variant. data, VariantData :: Unit ( ..) ) {
207
+ let post = if cx. ecfg . features . default_field_values ( ) {
208
+ " or variants where every field has a default value"
209
+ } else {
210
+ ""
211
+ } ;
212
+ let guar = cx. dcx ( ) . emit_err ( errors:: NonUnitDefault { span : variant. ident . span , post } ) ;
161
213
return Err ( guar) ;
162
214
}
163
215
@@ -216,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> {
216
268
impl < ' a , ' b > rustc_ast:: visit:: Visitor < ' a > for DetectNonVariantDefaultAttr < ' a , ' b > {
217
269
fn visit_attribute ( & mut self , attr : & ' a rustc_ast:: Attribute ) {
218
270
if attr. has_name ( kw:: Default ) {
219
- self . cx . dcx ( ) . emit_err ( errors:: NonUnitDefault { span : attr. span } ) ;
271
+ let post = if self . cx . ecfg . features . default_field_values ( ) {
272
+ " or variants where every field has a default value"
273
+ } else {
274
+ ""
275
+ } ;
276
+ self . cx . dcx ( ) . emit_err ( errors:: NonUnitDefault { span : attr. span , post } ) ;
220
277
}
221
278
222
279
rustc_ast:: visit:: walk_attribute ( self , attr) ;
0 commit comments