1
Fork 0

Introduce default_field_values feature

Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
This commit is contained in:
Esteban Küber 2024-08-24 17:22:48 +00:00
parent f6cb952dc1
commit 9ac95c10c0
70 changed files with 1469 additions and 392 deletions

View file

@ -3119,6 +3119,7 @@ pub struct FieldDef {
pub ident: Option<Ident>, pub ident: Option<Ident>,
pub ty: P<Ty>, pub ty: P<Ty>,
pub default: Option<AnonConst>,
pub is_placeholder: bool, pub is_placeholder: bool,
} }

View file

@ -1120,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
} }
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) { pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd; let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
visitor.visit_id(id); visitor.visit_id(id);
visit_attrs(visitor, attrs); visit_attrs(visitor, attrs);
visitor.visit_vis(vis); visitor.visit_vis(vis);
visit_safety(visitor, safety); visit_safety(visitor, safety);
visit_opt(ident, |ident| visitor.visit_ident(ident)); visit_opt(ident, |ident| visitor.visit_ident(ident));
visitor.visit_ty(ty); visitor.visit_ty(ty);
visit_opt(default, |default| visitor.visit_anon_const(default));
visitor.visit_span(span); visitor.visit_span(span);
} }

View file

@ -975,11 +975,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
} }
pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result { pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field; let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } =
field;
walk_list!(visitor, visit_attribute, attrs); walk_list!(visitor, visit_attribute, attrs);
try_visit!(visitor.visit_vis(vis)); try_visit!(visitor.visit_vis(vis));
visit_opt!(visitor, visit_ident, ident); visit_opt!(visitor, visit_ident, ident);
try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_anon_const, &*default);
V::Result::output() V::Result::output()
} }

View file

@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output =
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
ast_lowering_base_expression_double_dot =
base expression required after `..`
.suggestion = add a base expression here
ast_lowering_clobber_abi_not_supported = ast_lowering_clobber_abi_not_supported =
`clobber_abi` is not supported on this target `clobber_abi` is not supported on this target

View file

@ -114,14 +114,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
pub span: Span, pub span: Span,
} }
#[derive(Diagnostic)]
#[diag(ast_lowering_base_expression_double_dot, code = E0797)]
pub(crate) struct BaseExpressionDoubleDot {
#[primary_span]
#[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)] #[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks { pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {

View file

@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec};
use visit::{Visitor, walk_expr}; use visit::{Visitor, walk_expr};
use super::errors::{ use super::errors::{
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot, AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
ClosureCannotBeStatic, CoroutineTooManyParameters, CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, UnderscoreExprLhsAssign,
}; };
use super::{ use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
@ -357,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
), ),
ExprKind::Struct(se) => { ExprKind::Struct(se) => {
let rest = match &se.rest { let rest = match &se.rest {
StructRest::Base(e) => Some(self.lower_expr(e)), StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
StructRest::Rest(sp) => { StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp }); StructRest::None => hir::StructTailExpr::None,
Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
}
StructRest::None => None,
}; };
hir::ExprKind::Struct( hir::ExprKind::Struct(
self.arena.alloc(self.lower_qpath( self.arena.alloc(self.lower_qpath(
@ -1526,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Struct( hir::ExprKind::Struct(
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))), self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
fields, fields,
None, hir::StructTailExpr::None,
) )
} }

View file

@ -723,6 +723,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
None => Ident::new(sym::integer(index), self.lower_span(f.span)), None => Ident::new(sym::integer(index), self.lower_span(f.span)),
}, },
vis_span: self.lower_span(f.vis.span), vis_span: self.lower_span(f.vis.span),
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
ty, ty,
safety: self.lower_safety(f.safety, hir::Safety::Safe), safety: self.lower_safety(f.safety, hir::Safety::Safe),
} }

View file

@ -557,6 +557,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
gate_all!(default_field_values, "default values on `struct` fields aren't supported");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental"); gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental");

View file

@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
expr: &hir::Expr<'_>, expr: &hir::Expr<'_>,
) { ) {
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return }; let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
expr.kind
else {
return;
};
let hir::QPath::Resolved(_, path) = struct_qpath else { return }; let hir::QPath::Resolved(_, path) = struct_qpath else { return };
let hir::def::Res::Def(_, def_id) = path.res else { return }; let hir::def::Res::Def(_, def_id) = path.res else { return };
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return }; let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
expr: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>,
use_spans: Option<UseSpans<'tcx>>, use_spans: Option<UseSpans<'tcx>>,
) { ) {
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
// `Location` that covers both the `S { ... }` literal, all of its fields and the // `Location` that covers both the `S { ... }` literal, all of its fields and the
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr` // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`

View file

@ -205,7 +205,7 @@ where
let fields = fields let fields = fields
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, &(ident, span))| { .map(|(i, &(ident, span, _))| {
let arg = getarg(cx, span, ident.name, i); let arg = getarg(cx, span, ident.name, i);
cx.field_imm(span, ident, arg) cx.field_imm(span, ident, arg)
}) })

View file

@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
trait_def.expand(cx, mitem, item, push) trait_def.expand(cx, mitem, item, push)
} }
fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> {
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
cx.expr_call_global(span, default_ident, ThinVec::new())
}
fn default_struct_substructure( fn default_struct_substructure(
cx: &ExtCtxt<'_>, cx: &ExtCtxt<'_>,
trait_span: Span, trait_span: Span,
substr: &Substructure<'_>, substr: &Substructure<'_>,
summary: &StaticFields, summary: &StaticFields,
) -> BlockOrExpr { ) -> BlockOrExpr {
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new());
let expr = match summary { let expr = match summary {
Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident), Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
Unnamed(fields, IsTuple::Yes) => { Unnamed(fields, IsTuple::Yes) => {
let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
cx.expr_call_ident(trait_span, substr.type_ident, exprs) cx.expr_call_ident(trait_span, substr.type_ident, exprs)
} }
Named(fields) => { Named(fields) => {
let default_fields = fields let default_fields = fields
.iter() .iter()
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span))) .map(|(ident, span, default_val)| {
let value = match default_val {
// We use `Default::default()`.
None => default_call(cx, *span),
// We use the field default const expression.
Some(val) => {
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
}
};
cx.field_imm(*span, *ident, value)
})
.collect(); .collect();
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
} }
@ -93,10 +105,38 @@ fn default_enum_substructure(
} { } {
Ok(default_variant) => { Ok(default_variant) => {
// We now know there is exactly one unit variant with exactly one `#[default]` attribute. // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
cx.expr_path(cx.path(default_variant.span, vec![ match &default_variant.data {
Ident::new(kw::SelfUpper, default_variant.span), VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
default_variant.ident, Ident::new(kw::SelfUpper, default_variant.span),
])) default_variant.ident,
])),
VariantData::Struct { fields, .. } => {
// This only happens if `#![feature(default_field_values)]`. We have validated
// all fields have default values in the definition.
let default_fields = fields
.iter()
.map(|field| {
cx.field_imm(field.span, field.ident.unwrap(), match &field.default {
// We use `Default::default()`.
None => default_call(cx, field.span),
// We use the field default const expression.
Some(val) => {
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
}
})
})
.collect();
let path = cx.path(default_variant.span, vec![
Ident::new(kw::SelfUpper, default_variant.span),
default_variant.ident,
]);
cx.expr_struct(default_variant.span, path, default_fields)
}
// Logic error in `extract_default_variant`.
VariantData::Tuple(..) => {
cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
}
}
} }
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)), Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
}; };
@ -156,7 +196,12 @@ fn extract_default_variant<'a>(
} }
}; };
if !matches!(variant.data, VariantData::Unit(..)) { if cx.ecfg.features.default_field_values()
&& let VariantData::Struct { fields, .. } = &variant.data
&& fields.iter().all(|f| f.default.is_some())
{
// Allowed
} else if !matches!(variant.data, VariantData::Unit(..)) {
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span }); let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
return Err(guar); return Err(guar);
} }

View file

@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
pub(crate) use SubstructureFields::*; pub(crate) use SubstructureFields::*;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::{ use rustc_ast::{
self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
Mutability, PatKind, VariantData, Generics, Mutability, PatKind, VariantData,
}; };
use rustc_attr as attr; use rustc_attr as attr;
use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::base::{Annotatable, ExtCtxt};
@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
/// Tuple and unit structs/enum variants like this. /// Tuple and unit structs/enum variants like this.
Unnamed(Vec<Span>, IsTuple), Unnamed(Vec<Span>, IsTuple),
/// Normal structs/struct variants. /// Normal structs/struct variants.
Named(Vec<(Ident, Span)>), Named(Vec<(Ident, Span, Option<AnonConst>)>),
} }
/// A summary of the possible sets of fields. /// A summary of the possible sets of fields.
@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
for field in struct_def.fields() { for field in struct_def.fields() {
let sp = field.span.with_ctxt(self.span.ctxt()); let sp = field.span.with_ctxt(self.span.ctxt());
match field.ident { match field.ident {
Some(ident) => named_idents.push((ident, sp)), Some(ident) => named_idents.push((ident, sp, field.default.clone())),
_ => just_spans.push(sp), _ => just_spans.push(sp),
} }
} }

View file

@ -174,6 +174,7 @@ pub(crate) fn placeholder(
vis, vis,
is_placeholder: true, is_placeholder: true,
safety: Safety::Default, safety: Safety::Default,
default: None,
}]), }]),
AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
attrs: Default::default(), attrs: Default::default(),

View file

@ -455,6 +455,9 @@ declare_features! (
(unstable, custom_test_frameworks, "1.30.0", Some(50297)), (unstable, custom_test_frameworks, "1.30.0", Some(50297)),
/// Allows declarative macros 2.0 (`macro`). /// Allows declarative macros 2.0 (`macro`).
(unstable, decl_macro, "1.17.0", Some(39412)), (unstable, decl_macro, "1.17.0", Some(39412)),
/// Allows the use of default values on struct definitions and the construction of struct
/// literals with the functional update syntax without a base.
(unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)),
/// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait
(unstable, deprecated_safe, "1.61.0", Some(94978)), (unstable, deprecated_safe, "1.61.0", Some(94978)),
/// Allows having using `suggestion` in the `#[deprecated]` attribute. /// Allows having using `suggestion` in the `#[deprecated]` attribute.

View file

@ -1857,7 +1857,12 @@ impl Expr<'_> {
base.can_have_side_effects() base.can_have_side_effects()
} }
ExprKind::Struct(_, fields, init) => { ExprKind::Struct(_, fields, init) => {
fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects()) let init_side_effects = match init {
StructTailExpr::Base(init) => init.can_have_side_effects(),
StructTailExpr::DefaultFields(_) | StructTailExpr::None => false,
};
fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects())
|| init_side_effects
} }
ExprKind::Array(args) ExprKind::Array(args)
@ -1926,20 +1931,52 @@ impl Expr<'_> {
ExprKind::Path(QPath::Resolved(None, path2)), ExprKind::Path(QPath::Resolved(None, path2)),
) => path1.res == path2.res, ) => path1.res == path2.res,
( (
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None), ExprKind::Struct(
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None), QPath::LangItem(LangItem::RangeTo, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeTo, _),
[val2],
StructTailExpr::None,
),
) )
| ( | (
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None), ExprKind::Struct(
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None), QPath::LangItem(LangItem::RangeToInclusive, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusive, _),
[val2],
StructTailExpr::None,
),
) )
| ( | (
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None), ExprKind::Struct(
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None), QPath::LangItem(LangItem::RangeFrom, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFrom, _),
[val2],
StructTailExpr::None,
),
) => val1.expr.equivalent_for_indexing(val2.expr), ) => val1.expr.equivalent_for_indexing(val2.expr),
( (
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None), ExprKind::Struct(
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None), QPath::LangItem(LangItem::Range, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::Range, _),
[val2, val4],
StructTailExpr::None,
),
) => { ) => {
val1.expr.equivalent_for_indexing(val2.expr) val1.expr.equivalent_for_indexing(val2.expr)
&& val3.expr.equivalent_for_indexing(val4.expr) && val3.expr.equivalent_for_indexing(val4.expr)
@ -2096,7 +2133,7 @@ pub enum ExprKind<'hir> {
/// ///
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
/// where `base` is the `Option<Expr>`. /// where `base` is the `Option<Expr>`.
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>), Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], StructTailExpr<'hir>),
/// An array literal constructed from one repeated element. /// An array literal constructed from one repeated element.
/// ///
@ -2111,6 +2148,19 @@ pub enum ExprKind<'hir> {
Err(rustc_span::ErrorGuaranteed), Err(rustc_span::ErrorGuaranteed),
} }
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum StructTailExpr<'hir> {
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
None,
/// A struct expression with a "base", an expression of the same type as the outer struct that
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
Base(&'hir Expr<'hir>),
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
/// fields' default values will be used to populate any fields not explicitly mentioned:
/// `Foo { .. }`.
DefaultFields(Span),
}
/// Represents an optionally `Self`-qualified value/type path or associated extension. /// Represents an optionally `Self`-qualified value/type path or associated extension.
/// ///
/// To resolve the path to a `DefId`, call [`qpath_res`]. /// To resolve the path to a `DefId`, call [`qpath_res`].
@ -3172,6 +3222,7 @@ pub struct FieldDef<'hir> {
pub def_id: LocalDefId, pub def_id: LocalDefId,
pub ty: &'hir Ty<'hir>, pub ty: &'hir Ty<'hir>,
pub safety: Safety, pub safety: Safety,
pub default: Option<&'hir AnonConst>,
} }
impl FieldDef<'_> { impl FieldDef<'_> {

View file

@ -748,7 +748,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::Struct(ref qpath, fields, ref optional_base) => { ExprKind::Struct(ref qpath, fields, ref optional_base) => {
try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span)); try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
walk_list!(visitor, visit_expr_field, fields); walk_list!(visitor, visit_expr_field, fields);
visit_opt!(visitor, visit_expr, optional_base); match optional_base {
StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)),
StructTailExpr::None | StructTailExpr::DefaultFields(_) => {}
}
} }
ExprKind::Tup(subexpressions) => { ExprKind::Tup(subexpressions) => {
walk_list!(visitor, visit_expr, subexpressions); walk_list!(visitor, visit_expr, subexpressions);
@ -1190,10 +1193,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
V::Result::output() V::Result::output()
} }
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result { pub fn walk_field_def<'v, V: Visitor<'v>>(
try_visit!(visitor.visit_id(field.hir_id)); visitor: &mut V,
try_visit!(visitor.visit_ident(field.ident)); FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>,
visitor.visit_ty(field.ty) ) -> V::Result {
try_visit!(visitor.visit_id(*hir_id));
try_visit!(visitor.visit_ident(*ident));
visit_opt!(visitor, visit_anon_const, default);
visitor.visit_ty(*ty)
} }
pub fn walk_enum_def<'v, V: Visitor<'v>>( pub fn walk_enum_def<'v, V: Visitor<'v>>(

View file

@ -1021,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
} }
} }
fn lower_variant( fn lower_variant<'tcx>(
tcx: TyCtxt<'_>, tcx: TyCtxt<'tcx>,
variant_did: Option<LocalDefId>, variant_did: Option<LocalDefId>,
ident: Ident, ident: Ident,
discr: ty::VariantDiscr, discr: ty::VariantDiscr,
def: &hir::VariantData<'_>, def: &hir::VariantData<'tcx>,
adt_kind: ty::AdtKind, adt_kind: ty::AdtKind,
parent_did: LocalDefId, parent_did: LocalDefId,
) -> ty::VariantDef { ) -> ty::VariantDef {
@ -1042,6 +1042,7 @@ fn lower_variant(
name: f.ident.name, name: f.ident.name,
vis: tcx.visibility(f.def_id), vis: tcx.visibility(f.def_id),
safety: f.safety, safety: f.safety,
value: f.default.map(|v| v.def_id.to_def_id()),
}) })
.collect(); .collect();
let recovered = match def { let recovered = match def {

View file

@ -125,6 +125,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
return ty; return ty;
} }
Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. })
if c.hir_id == hir_id =>
{
tcx.type_of(field_def_id).instantiate_identity()
}
_ => Ty::new_error_with_message( _ => Ty::new_error_with_message(
tcx, tcx,
span, span,

View file

@ -1080,22 +1080,36 @@ impl<'a> State<'a> {
&mut self, &mut self,
qpath: &hir::QPath<'_>, qpath: &hir::QPath<'_>,
fields: &[hir::ExprField<'_>], fields: &[hir::ExprField<'_>],
wth: Option<&hir::Expr<'_>>, wth: hir::StructTailExpr<'_>,
) { ) {
self.print_qpath(qpath, true); self.print_qpath(qpath, true);
self.word("{"); self.word("{");
self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span);
if let Some(expr) = wth { match wth {
self.ibox(INDENT_UNIT); hir::StructTailExpr::Base(expr) => {
if !fields.is_empty() { self.ibox(INDENT_UNIT);
self.word(","); if !fields.is_empty() {
self.space(); self.word(",");
self.space();
}
self.word("..");
self.print_expr(expr);
self.end();
}
hir::StructTailExpr::DefaultFields(_) => {
self.ibox(INDENT_UNIT);
if !fields.is_empty() {
self.word(",");
self.space();
}
self.word("..");
self.end();
}
hir::StructTailExpr::None => {
if !fields.is_empty() {
self.word(",");
}
} }
self.word("..");
self.print_expr(expr);
self.end();
} else if !fields.is_empty() {
self.word(",");
} }
self.word("}"); self.word("}");

View file

@ -10,6 +10,12 @@ hir_typeck_address_of_temporary_taken = cannot take address of a temporary
hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
.note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new .note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new
hir_typeck_base_expression_double_dot = base expression required after `..`
hir_typeck_base_expression_double_dot_add_expr = add a base expression here
hir_typeck_base_expression_double_dot_enable_default_field_values =
add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
hir_typeck_base_expression_double_dot_remove = remove the `..` as all the fields are already present
hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty -> hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty ->
[NONE] {""} [NONE] {""}
[implement] , perhaps you need to implement it [implement] , perhaps you need to implement it

View file

@ -15,6 +15,47 @@ use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
#[derive(Diagnostic)]
#[diag(hir_typeck_base_expression_double_dot, code = E0797)]
pub(crate) struct BaseExpressionDoubleDot {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub default_field_values: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
#[subdiagnostic]
pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
#[subdiagnostic]
pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
}
#[derive(Subdiagnostic)]
#[suggestion(
hir_typeck_base_expression_double_dot_remove,
code = "",
applicability = "machine-applicable",
style = "verbose"
)]
pub(crate) struct BaseExpressionDoubleDotRemove {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[suggestion(
hir_typeck_base_expression_double_dot_add_expr,
code = "/* expr */",
applicability = "has-placeholders",
style = "verbose"
)]
pub(crate) struct BaseExpressionDoubleDotAddExpr {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)] #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
pub(crate) struct FieldMultiplySpecifiedInInitializer { pub(crate) struct FieldMultiplySpecifiedInInitializer {

View file

@ -44,10 +44,11 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio
use crate::TupleArgumentsFlag::DontTupleArguments; use crate::TupleArgumentsFlag::DontTupleArguments;
use crate::coercion::{CoerceMany, DynamicCoerceMany}; use crate::coercion::{CoerceMany, DynamicCoerceMany};
use crate::errors::{ use crate::errors::{
AddressOfTemporaryTaken, FieldMultiplySpecifiedInInitializer, AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, ReturnLikeStatementKind, BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition,
YieldExprOutsideOfCoroutine, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
}; };
use crate::{ use crate::{
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust, BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust,
@ -723,7 +724,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_assoc_method_call(segs); self.suggest_assoc_method_call(segs);
let e = let e =
self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
Ty::new_error(tcx, e) Ty::new_error(tcx, e)
} }
Res::Def(DefKind::Variant, _) => { Res::Def(DefKind::Variant, _) => {
@ -1855,11 +1855,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_expr_struct( fn check_expr_struct(
&self, &self,
expr: &hir::Expr<'_>, expr: &hir::Expr<'tcx>,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
qpath: &QPath<'tcx>, qpath: &'tcx QPath<'tcx>,
fields: &'tcx [hir::ExprField<'tcx>], fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, base_expr: &'tcx hir::StructTailExpr<'tcx>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
// Find the relevant variant // Find the relevant variant
let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) { let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) {
@ -1899,7 +1899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span, span: Span,
variant: &'tcx ty::VariantDef, variant: &'tcx ty::VariantDef,
hir_fields: &'tcx [hir::ExprField<'tcx>], hir_fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, base_expr: &'tcx hir::StructTailExpr<'tcx>,
) { ) {
let tcx = self.tcx; let tcx = self.tcx;
@ -2023,13 +2023,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the fields with the base_expr. This could cause us to hit errors later // the fields with the base_expr. This could cause us to hit errors later
// when certain fields are assumed to exist that in fact do not. // when certain fields are assumed to exist that in fact do not.
if error_happened { if error_happened {
if let Some(base_expr) = base_expr { if let hir::StructTailExpr::Base(base_expr) = base_expr {
self.check_expr(base_expr); self.check_expr(base_expr);
} }
return; return;
} }
if let Some(base_expr) = base_expr { if let hir::StructTailExpr::DefaultFields(span) = *base_expr {
let mut missing_mandatory_fields = Vec::new();
let mut missing_optional_fields = Vec::new();
for f in &variant.fields {
let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
if let Some(_) = remaining_fields.remove(&ident) {
if f.value.is_none() {
missing_mandatory_fields.push(ident);
} else {
missing_optional_fields.push(ident);
}
}
}
if !self.tcx.features().default_field_values() {
self.dcx().emit_err(BaseExpressionDoubleDot {
span: span.shrink_to_hi(),
// We only mention enabling the feature if this is a nightly rustc *and* the
// expression would make sense with the feature enabled.
default_field_values: if self.tcx.sess.is_nightly_build()
&& missing_mandatory_fields.is_empty()
&& !missing_optional_fields.is_empty()
{
Some(BaseExpressionDoubleDotEnableDefaultFieldValues)
} else {
None
},
add_expr: if !missing_mandatory_fields.is_empty()
|| !missing_optional_fields.is_empty()
{
Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() })
} else {
None
},
remove_dots: if missing_mandatory_fields.is_empty()
&& missing_optional_fields.is_empty()
{
Some(BaseExpressionDoubleDotRemove { span })
} else {
None
},
});
return;
}
if !missing_mandatory_fields.is_empty() {
let s = pluralize!(missing_mandatory_fields.len());
let fields: Vec<_> =
missing_mandatory_fields.iter().map(|f| format!("`{f}`")).collect();
let fields = match &fields[..] {
[] => unreachable!(),
[only] => only.to_string(),
[start @ .., last] => format!("{} and {last}", start.join(", ")),
};
self.dcx()
.struct_span_err(
span.shrink_to_hi(),
format!("missing mandatory field{s} {fields}"),
)
.emit();
return;
}
let fru_tys = match adt_ty.kind() {
ty::Adt(adt, args) if adt.is_struct() => variant
.fields
.iter()
.map(|f| self.normalize(span, f.ty(self.tcx, args)))
.collect(),
ty::Adt(adt, args) if adt.is_enum() => variant
.fields
.iter()
.map(|f| self.normalize(span, f.ty(self.tcx, args)))
.collect(),
_ => {
self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span });
return;
}
};
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys);
} else if let hir::StructTailExpr::Base(base_expr) = base_expr {
// FIXME: We are currently creating two branches here in order to maintain // FIXME: We are currently creating two branches here in order to maintain
// consistency. But they should be merged as much as possible. // consistency. But they should be merged as much as possible.
let fru_tys = if self.tcx.features().type_changing_struct_update() { let fru_tys = if self.tcx.features().type_changing_struct_update() {
@ -2161,12 +2238,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_struct_fields_on_error( fn check_struct_fields_on_error(
&self, &self,
fields: &'tcx [hir::ExprField<'tcx>], fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, base_expr: &'tcx hir::StructTailExpr<'tcx>,
) { ) {
for field in fields { for field in fields {
self.check_expr(field.expr); self.check_expr(field.expr);
} }
if let Some(base) = *base_expr { if let hir::StructTailExpr::Base(base) = *base_expr {
self.check_expr(base); self.check_expr(base);
} }
} }

View file

@ -686,7 +686,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
fn walk_struct_expr<'hir>( fn walk_struct_expr<'hir>(
&self, &self,
fields: &[hir::ExprField<'_>], fields: &[hir::ExprField<'_>],
opt_with: &Option<&'hir hir::Expr<'_>>, opt_with: &hir::StructTailExpr<'hir>,
) -> Result<(), Cx::Error> { ) -> Result<(), Cx::Error> {
// Consume the expressions supplying values for each field. // Consume the expressions supplying values for each field.
for field in fields { for field in fields {
@ -702,8 +702,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
} }
let with_expr = match *opt_with { let with_expr = match *opt_with {
Some(w) => &*w, hir::StructTailExpr::Base(w) => &*w,
None => { hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => {
return Ok(()); return Ok(());
} }
}; };

View file

@ -211,6 +211,10 @@ lint_dangling_pointers_from_temporaries = a dangling pointer will be produced be
.note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned .note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
.help = for more information, see <https://doc.rust-lang.org/reference/destructors.html> .help = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
lint_default_field_always_invalid_const = default field fails const-evaluation
.label = this field's constant fails const-evaluation, as seen in the previous error
.help = you can skip const-evaluation of default fields by enabling this lint
lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance
.note = a `use rustc_data_structures::fx::{$preferred}` may be necessary .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary

View file

@ -0,0 +1,91 @@
use rustc_hir as hir;
use rustc_middle::lint::LintLevelSource;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_session::lint::Level;
use rustc_session::{declare_lint, declare_lint_pass};
use crate::lints::DefaultFieldAlwaysInvalidConst;
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `default_field_always_invalid_const` lint checks for structs with
/// default fields const values that will *always* fail to be created.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![feature(default_field_values)]
/// #[deny(default_field_always_invalid_const)]
/// struct Foo {
/// bar: u8 = 130 + 130, // `260` doesn't fit in `u8`
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Without this lint, the error would only happen only during construction
/// of the affected type. For example, given the type above, `Foo { .. }`
/// would always fail to build, but `Foo { bar: 0 }` would be accepted. This
/// lint will catch accidental cases of const values that would fail to
/// compile, but won't detect cases that are only partially evaluated.
pub DEFAULT_FIELD_ALWAYS_INVALID_CONST,
Deny,
"using this default field will always fail to compile"
}
declare_lint_pass!(DefaultFieldAlwaysInvalid => [DEFAULT_FIELD_ALWAYS_INVALID_CONST]);
impl<'tcx> LateLintPass<'tcx> for DefaultFieldAlwaysInvalid {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
let data = match item.kind {
hir::ItemKind::Struct(data, _generics) => data,
_ => return,
};
let hir::VariantData::Struct { fields, recovered: _ } = data else {
return;
};
let (level, source) =
cx.tcx.lint_level_at_node(DEFAULT_FIELD_ALWAYS_INVALID_CONST, item.hir_id());
match level {
Level::Deny | Level::Forbid => {}
Level::Warn | Level::ForceWarn(_) | Level::Expect(_) => {
// We *can't* turn the const eval error into a warning, so we make it a
// warning to not use `#[warn(default_field_always_invalid_const)]`.
let invalid_msg = "lint `default_field_always_invalid_const` can't be warned on";
#[allow(rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic)]
if let LintLevelSource::Node { span, .. } = source {
let mut err = cx.tcx.sess.dcx().struct_span_warn(span, invalid_msg);
err.span_label(
span,
"either `deny` or `allow`, no other lint level is supported for this lint",
);
err.emit();
} else {
cx.tcx.sess.dcx().warn(invalid_msg);
}
}
Level::Allow => {
// We don't even look at the fields.
return;
}
}
for field in fields {
if let Some(c) = field.default
&& let Some(_ty) = cx.tcx.type_of(c.def_id).no_bound_vars()
&& let Err(ErrorHandled::Reported(_, _)) = cx.tcx.const_eval_poly(c.def_id.into())
{
// We use the item's hir id because the const's hir id might resolve inside of a
// foreign macro, meaning the lint won't trigger.
cx.tcx.emit_node_span_lint(
DEFAULT_FIELD_ALWAYS_INVALID_CONST,
item.hir_id(),
field.span,
DefaultFieldAlwaysInvalidConst { span: field.span, help: () },
);
}
}
}
}

View file

@ -41,6 +41,7 @@ mod async_fn_in_trait;
pub mod builtin; pub mod builtin;
mod context; mod context;
mod dangling; mod dangling;
mod default_field_always_invalid;
mod deref_into_dyn_supertrait; mod deref_into_dyn_supertrait;
mod drop_forget_useless; mod drop_forget_useless;
mod early; mod early;
@ -85,6 +86,7 @@ use async_closures::AsyncClosureUsage;
use async_fn_in_trait::AsyncFnInTrait; use async_fn_in_trait::AsyncFnInTrait;
use builtin::*; use builtin::*;
use dangling::*; use dangling::*;
use default_field_always_invalid::*;
use deref_into_dyn_supertrait::*; use deref_into_dyn_supertrait::*;
use drop_forget_useless::*; use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
@ -193,6 +195,7 @@ late_lint_methods!(
DropForgetUseless: DropForgetUseless, DropForgetUseless: DropForgetUseless,
ImproperCTypesDeclarations: ImproperCTypesDeclarations, ImproperCTypesDeclarations: ImproperCTypesDeclarations,
ImproperCTypesDefinitions: ImproperCTypesDefinitions, ImproperCTypesDefinitions: ImproperCTypesDefinitions,
DefaultFieldAlwaysInvalid: DefaultFieldAlwaysInvalid,
InvalidFromUtf8: InvalidFromUtf8, InvalidFromUtf8: InvalidFromUtf8,
VariantSizeDifferences: VariantSizeDifferences, VariantSizeDifferences: VariantSizeDifferences,
PathStatements: PathStatements, PathStatements: PathStatements,

View file

@ -730,6 +730,15 @@ pub(crate) struct UndroppedManuallyDropsSuggestion {
pub end_span: Span, pub end_span: Span,
} }
#[derive(LintDiagnostic)]
#[diag(lint_default_field_always_invalid_const)]
pub(crate) struct DefaultFieldAlwaysInvalidConst {
#[label]
pub span: Span,
#[help]
pub help: (),
}
// invalid_from_utf8.rs // invalid_from_utf8.rs
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
pub(crate) enum InvalidFromUtf8Diag { pub(crate) enum InvalidFromUtf8Diag {

View file

@ -1104,6 +1104,7 @@ impl<'a> CrateMetadataRef<'a> {
name: self.item_name(did.index), name: self.item_name(did.index),
vis: self.get_visibility(did.index), vis: self.get_visibility(did.index),
safety: self.get_safety(did.index), safety: self.get_safety(did.index),
value: None,
}) })
.collect(), .collect(),
adt_kind, adt_kind,

View file

@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> {
pub user_ty: UserTy<'tcx>, pub user_ty: UserTy<'tcx>,
pub fields: Box<[FieldExpr]>, pub fields: Box<[FieldExpr]>,
/// The base, e.g. `Foo {x: 1, .. base}`. /// The base, e.g. `Foo {x: 1, ..base}`.
pub base: Option<FruInfo<'tcx>>, pub base: AdtExprBase<'tcx>,
}
#[derive(Clone, Debug, HashStable)]
pub enum AdtExprBase<'tcx> {
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
None,
/// A struct expression with a "base", an expression of the same type as the outer struct that
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
Base(FruInfo<'tcx>),
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
/// fields' default values will be used to populate any fields not explicitly mentioned:
/// `Foo { .. }`.
DefaultFields(Box<[Ty<'tcx>]>),
} }
#[derive(Clone, Debug, HashStable)] #[derive(Clone, Debug, HashStable)]

View file

@ -2,6 +2,7 @@ use super::{
AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat, AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
PatKind, Stmt, StmtKind, Thir, PatKind, Stmt, StmtKind, Thir,
}; };
use crate::thir::AdtExprBase;
pub trait Visitor<'thir, 'tcx: 'thir>: Sized { pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
fn thir(&self) -> &'thir Thir<'tcx>; fn thir(&self) -> &'thir Thir<'tcx>;
@ -127,7 +128,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
for field in &**fields { for field in &**fields {
visitor.visit_expr(&visitor.thir()[field.expr]); visitor.visit_expr(&visitor.thir()[field.expr]);
} }
if let Some(base) = base { if let AdtExprBase::Base(base) = base {
visitor.visit_expr(&visitor.thir()[base.base]); visitor.visit_expr(&visitor.thir()[base.base]);
} }
} }

View file

@ -259,10 +259,10 @@ impl Into<DataTypeKind> for AdtKind {
} }
} }
impl AdtDefData { impl<'tcx> AdtDefData {
/// Creates a new `AdtDefData`. /// Creates a new `AdtDefData`.
pub(super) fn new( pub(super) fn new(
tcx: TyCtxt<'_>, tcx: TyCtxt<'tcx>,
did: DefId, did: DefId,
kind: AdtKind, kind: AdtKind,
variants: IndexVec<VariantIdx, VariantDef>, variants: IndexVec<VariantIdx, VariantDef>,

View file

@ -1364,6 +1364,7 @@ pub struct FieldDef {
pub name: Symbol, pub name: Symbol,
pub vis: Visibility<DefId>, pub vis: Visibility<DefId>,
pub safety: hir::Safety, pub safety: hir::Safety,
pub value: Option<DefId>,
} }
impl PartialEq for FieldDef { impl PartialEq for FieldDef {
@ -1376,9 +1377,9 @@ impl PartialEq for FieldDef {
// of `FieldDef` changes, a compile-error will be produced, reminding // of `FieldDef` changes, a compile-error will be produced, reminding
// us to revisit this assumption. // us to revisit this assumption.
let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self; let Self { did: lhs_did, name: _, vis: _, safety: _, value: _ } = &self;
let Self { did: rhs_did, name: _, vis: _, safety: _ } = other; let Self { did: rhs_did, name: _, vis: _, safety: _, value: _ } = other;
let res = lhs_did == rhs_did; let res = lhs_did == rhs_did;
@ -1405,7 +1406,7 @@ impl Hash for FieldDef {
// of `FieldDef` changes, a compile-error will be produced, reminding // of `FieldDef` changes, a compile-error will be produced, reminding
// us to revisit this assumption. // us to revisit this assumption.
let Self { did, name: _, vis: _, safety: _ } = &self; let Self { did, name: _, vis: _, safety: _, value: _ } = &self;
did.hash(s) did.hash(s)
} }

View file

@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()? fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()?
)) ))
}, },
ExprKind::Adt(box AdtExpr{ adt_def, variant_index, args, fields, .. }) => { ExprKind::Adt(box AdtExpr { adt_def, variant_index, args, fields, .. }) => {
let is_union = adt_def.is_union(); let is_union = adt_def.is_union();
let active_field_index = is_union.then(|| fields[0].name); let active_field_index = is_union.then(|| fields[0].name);

View file

@ -1,7 +1,5 @@
//! See docs in build/expr/mod.rs //! See docs in build/expr/mod.rs
use std::iter;
use rustc_ast::{AsmMacro, InlineAsmOptions}; use rustc_ast::{AsmMacro, InlineAsmOptions};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
@ -344,25 +342,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}) })
.collect(); .collect();
let field_names = adt_def.variant(variant_index).fields.indices(); let variant = adt_def.variant(variant_index);
let field_names = variant.fields.indices();
let fields = if let Some(FruInfo { base, field_types }) = base { let fields = match base {
let place_builder = unpack!(block = this.as_place_builder(block, *base)); AdtExprBase::None => {
field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
}
AdtExprBase::Base(FruInfo { base, field_types }) => {
let place_builder = unpack!(block = this.as_place_builder(block, *base));
// MIR does not natively support FRU, so for each // MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that // base-supplied field, generate an operand that
// reads it from the base. // reads it from the base.
iter::zip(field_names, &**field_types) itertools::zip_eq(field_names, &**field_types)
.map(|(n, ty)| match fields_map.get(&n) { .map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(), Some(v) => v.clone(),
None => { None => {
let place = place_builder.clone_project(PlaceElem::Field(n, *ty)); let place =
this.consume_by_copy_or_move(place.to_place(this)) place_builder.clone_project(PlaceElem::Field(n, *ty));
} this.consume_by_copy_or_move(place.to_place(this))
}) }
.collect() })
} else { .collect()
field_names.filter_map(|n| fields_map.get(&n).cloned()).collect() }
AdtExprBase::DefaultFields(field_types) => {
itertools::zip_eq(field_names, &**field_types)
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => match variant.fields[n].value {
Some(def) => {
let value = Const::from_unevaluated(this.tcx, def)
.instantiate(this.tcx, args);
this.literal_operand(expr_span, value)
}
None => {
let name = variant.fields[n].name;
span_bug!(
expr_span,
"missing mandatory field `{name}` of type `{ty}`",
);
}
},
})
.collect()
}
}; };
let inferred_ty = expr.ty; let inferred_ty = expr.ty;

View file

@ -222,7 +222,7 @@ impl<'tcx> Cx<'tcx> {
args, args,
fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]), fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
user_ty: None, user_ty: None,
base: None, base: AdtExprBase::None,
})); }));
debug!(?kind); debug!(?kind);
@ -464,7 +464,7 @@ impl<'tcx> Cx<'tcx> {
variant_index: index, variant_index: index,
fields: field_refs, fields: field_refs,
user_ty, user_ty,
base: None, base: AdtExprBase::None,
})) }))
} else { } else {
ExprKind::Call { ExprKind::Call {
@ -594,20 +594,36 @@ impl<'tcx> Cx<'tcx> {
args, args,
user_ty, user_ty,
fields: self.field_refs(fields), fields: self.field_refs(fields),
base: base.map(|base| FruInfo { base: match base {
base: self.mirror_expr(base), hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo {
field_types: self.typeck_results().fru_field_types()[expr.hir_id] base: self.mirror_expr(base),
.iter() field_types: self.typeck_results().fru_field_types()
.copied() [expr.hir_id]
.collect(), .iter()
}), .copied()
.collect(),
}),
hir::StructTailExpr::DefaultFields(_) => {
AdtExprBase::DefaultFields(
self.typeck_results().fru_field_types()[expr.hir_id]
.iter()
.copied()
.collect(),
)
}
hir::StructTailExpr::None => AdtExprBase::None,
},
})) }))
} }
AdtKind::Enum => { AdtKind::Enum => {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id); let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
match res { match res {
Res::Def(DefKind::Variant, variant_id) => { Res::Def(DefKind::Variant, variant_id) => {
assert!(base.is_none()); assert!(matches!(
base,
hir::StructTailExpr::None
| hir::StructTailExpr::DefaultFields(_)
));
let index = adt.variant_index_with_id(variant_id); let index = adt.variant_index_with_id(variant_id);
let user_provided_types = let user_provided_types =
@ -621,7 +637,21 @@ impl<'tcx> Cx<'tcx> {
args, args,
user_ty, user_ty,
fields: self.field_refs(fields), fields: self.field_refs(fields),
base: None, base: match base {
hir::StructTailExpr::DefaultFields(_) => {
AdtExprBase::DefaultFields(
self.typeck_results().fru_field_types()
[expr.hir_id]
.iter()
.copied()
.collect(),
)
}
hir::StructTailExpr::Base(base) => {
span_bug!(base.span, "unexpected res: {:?}", res);
}
hir::StructTailExpr::None => AdtExprBase::None,
},
})) }))
} }
_ => { _ => {
@ -1029,7 +1059,7 @@ impl<'tcx> Cx<'tcx> {
args, args,
user_ty, user_ty,
fields: Box::new([]), fields: Box::new([]),
base: None, base: AdtExprBase::None,
})), })),
_ => bug!("unexpected ty: {:?}", ty), _ => bug!("unexpected ty: {:?}", ty),
} }

View file

@ -566,11 +566,17 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_expr(field_expr.expr, depth_lvl + 2); self.print_expr(field_expr.expr, depth_lvl + 2);
} }
if let Some(ref base) = adt_expr.base { match adt_expr.base {
print_indented!(self, "base:", depth_lvl + 1); AdtExprBase::Base(ref base) => {
self.print_fru_info(base, depth_lvl + 2); print_indented!(self, "base:", depth_lvl + 1);
} else { self.print_fru_info(base, depth_lvl + 2);
print_indented!(self, "base: None", depth_lvl + 1); }
AdtExprBase::DefaultFields(_) => {
print_indented!(self, "base: {{ defaulted fields }}", depth_lvl + 1);
}
AdtExprBase::None => {
print_indented!(self, "base: None", depth_lvl + 1);
}
} }
} }

View file

@ -169,9 +169,6 @@ parse_enum_struct_mutually_exclusive = `enum` and `struct` are mutually exclusiv
parse_eq_field_init = expected `:`, found `=` parse_eq_field_init = expected `:`, found `=`
.suggestion = replace equals symbol with a colon .suggestion = replace equals symbol with a colon
parse_equals_struct_default = default values on `struct` fields aren't supported
.suggestion = remove this unsupported default value
parse_escape_only_char = {$byte -> parse_escape_only_char = {$byte ->
[true] byte [true] byte
*[false] character *[false] character

View file

@ -3066,14 +3066,6 @@ pub(crate) struct SingleColonStructType {
pub span: Span, pub span: Span,
} }
#[derive(Diagnostic)]
#[diag(parse_equals_struct_default)]
pub(crate) struct EqualsStructDefault {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable", style = "verbose")]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(parse_macro_rules_missing_bang)] #[diag(parse_macro_rules_missing_bang)]
pub(crate) struct MacroRulesMissingBang { pub(crate) struct MacroRulesMissingBang {

View file

@ -3533,7 +3533,7 @@ impl<'a> Parser<'a> {
let exp_span = self.prev_token.span; let exp_span = self.prev_token.span;
// We permit `.. }` on the left-hand side of a destructuring assignment. // We permit `.. }` on the left-hand side of a destructuring assignment.
if self.check(&token::CloseDelim(close_delim)) { if self.check(&token::CloseDelim(close_delim)) {
base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); base = ast::StructRest::Rest(self.prev_token.span);
break; break;
} }
match self.parse_expr() { match self.parse_expr() {

View file

@ -1845,6 +1845,7 @@ impl<'a> Parser<'a> {
ident: None, ident: None,
id: DUMMY_NODE_ID, id: DUMMY_NODE_ID,
ty, ty,
default: None,
attrs, attrs,
is_placeholder: false, is_placeholder: false,
}, },
@ -2024,12 +2025,15 @@ impl<'a> Parser<'a> {
if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) { if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) {
self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span }); self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span });
} }
if self.token == token::Eq { let default = if self.token == token::Eq {
self.bump(); self.bump();
let const_expr = self.parse_expr_anon_const()?; let const_expr = self.parse_expr_anon_const()?;
let sp = ty.span.shrink_to_hi().to(const_expr.value.span); let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
self.dcx().emit_err(errors::EqualsStructDefault { span: sp }); self.psess.gated_spans.gate(sym::default_field_values, sp);
} Some(const_expr)
} else {
None
};
Ok(FieldDef { Ok(FieldDef {
span: lo.to(self.prev_token.span), span: lo.to(self.prev_token.span),
ident: Some(name), ident: Some(name),
@ -2037,6 +2041,7 @@ impl<'a> Parser<'a> {
safety, safety,
id: DUMMY_NODE_ID, id: DUMMY_NODE_ID,
ty, ty,
default,
attrs, attrs,
is_placeholder: false, is_placeholder: false,
}) })

View file

@ -1007,7 +1007,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ), hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ),
hir::ExprKind::Struct(_, fields, ref with_expr) => { hir::ExprKind::Struct(_, fields, ref with_expr) => {
let succ = self.propagate_through_opt_expr(with_expr.as_deref(), succ); let succ = match with_expr {
hir::StructTailExpr::Base(base) => {
self.propagate_through_opt_expr(Some(base), succ)
}
hir::StructTailExpr::None | hir::StructTailExpr::DefaultFields(_) => succ,
};
fields fields
.iter() .iter()
.rev() .rev()

View file

@ -947,6 +947,25 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
}); });
} }
} }
fn check_expanded_fields(
&mut self,
adt: ty::AdtDef<'tcx>,
variant: &'tcx ty::VariantDef,
fields: &[hir::ExprField<'tcx>],
hir_id: hir::HirId,
span: Span,
) {
for (vf_index, variant_field) in variant.fields.iter_enumerated() {
let field =
fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
let (hir_id, use_ctxt, span) = match field {
Some(field) => (field.hir_id, field.ident.span, field.span),
None => (hir_id, span, span),
};
self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
}
}
} }
impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
@ -966,25 +985,29 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id); let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap(); let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res); let variant = adt.variant_of_res(res);
if let Some(base) = *base { match *base {
// If the expression uses FRU we need to make sure all the unmentioned fields hir::StructTailExpr::Base(base) => {
// are checked for privacy (RFC 736). Rather than computing the set of // If the expression uses FRU we need to make sure all the unmentioned fields
// unmentioned fields, just check them all. // are checked for privacy (RFC 736). Rather than computing the set of
for (vf_index, variant_field) in variant.fields.iter_enumerated() { // unmentioned fields, just check them all.
let field = fields self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span);
.iter()
.find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
let (hir_id, use_ctxt, span) = match field {
Some(field) => (field.hir_id, field.ident.span, field.span),
None => (base.hir_id, base.span, base.span),
};
self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
} }
} else { hir::StructTailExpr::DefaultFields(span) => {
for field in fields { self.check_expanded_fields(adt, variant, fields, expr.hir_id, span);
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); }
let index = self.typeck_results().field_index(field.hir_id); hir::StructTailExpr::None => {
self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); for field in fields {
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(
hir_id,
use_ctxt,
span,
adt,
&variant.fields[index],
false,
);
}
} }
} }
} }

View file

@ -13,7 +13,9 @@ use std::collections::hash_map::Entry;
use std::mem::{replace, swap, take}; use std::mem::{replace, swap, take};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, visit_opt, walk_list}; use rustc_ast::visit::{
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
};
use rustc_ast::*; use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::codes::*; use rustc_errors::codes::*;
@ -749,8 +751,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
self.resolve_block(block); self.resolve_block(block);
self.parent_scope.macro_rules = old_macro_rules; self.parent_scope.macro_rules = old_macro_rules;
} }
fn visit_anon_const(&mut self, _constant: &'ast AnonConst) { fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
bug!("encountered anon const without a manual call to `resolve_anon_const`"); bug!("encountered anon const without a manual call to `resolve_anon_const` {constant:#?}");
} }
fn visit_expr(&mut self, expr: &'ast Expr) { fn visit_expr(&mut self, expr: &'ast Expr) {
self.resolve_expr(expr, None); self.resolve_expr(expr, None);
@ -1346,7 +1348,24 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
fn visit_field_def(&mut self, f: &'ast FieldDef) { fn visit_field_def(&mut self, f: &'ast FieldDef) {
self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id)); self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id));
visit::walk_field_def(self, f) let FieldDef {
attrs,
id: _,
span: _,
vis,
ident,
ty,
is_placeholder: _,
default,
safety: _,
} = f;
walk_list!(self, visit_attribute, attrs);
try_visit!(self.visit_vis(vis));
visit_opt!(self, visit_ident, ident);
try_visit!(self.visit_ty(ty));
if let Some(v) = &default {
self.resolve_anon_const(v, AnonConstKind::ConstArg(IsRepeatExpr::No));
}
} }
} }

View file

@ -728,6 +728,7 @@ symbols! {
declare_lint_pass, declare_lint_pass,
decode, decode,
default_alloc_error_handler, default_alloc_error_handler,
default_field_values,
default_fn, default_fn,
default_lib_allocator, default_lib_allocator,
default_method_body_is_const, default_method_body_is_const,

View file

@ -5,7 +5,7 @@ use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::print::with_forced_trimmed_paths;
@ -285,7 +285,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }` /// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
if let Some(parent) = get_parent_expr(cx, expr) if let Some(parent) = get_parent_expr(cx, expr)
&& let ExprKind::Struct(_, _, Some(base)) = parent.kind && let ExprKind::Struct(_, _, StructTailExpr::Base(base)) = parent.kind
{ {
base.hir_id == expr.hir_id base.hir_id == expr.hir_id
} else { } else {

View file

@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt}; use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind}; use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
} }
// Visit base with no bound. // Visit base with no bound.
if let Some(base) = base { if let StructTailExpr::Base(base) = base {
self.ty_bounds.push(ExplicitTyBound(false)); self.ty_bounds.push(ExplicitTyBound(false));
self.visit_expr(base); self.visit_expr(base);
self.ty_bounds.pop(); self.ty_bounds.pop();

View file

@ -3,7 +3,7 @@ use clippy_utils::fulfill_or_allowed;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind}; use rustc_hir::{self as hir, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
} }
fields_snippet.push_str(&last_ident.to_string()); fields_snippet.push_str(&last_ident.to_string());
let base_snippet = if let Some(base) = base { let base_snippet = if let StructTailExpr::Base(base) = base {
format!(", ..{}", snippet(cx, base.span, "..")) format!(", ..{}", snippet(cx, base.span, ".."))
} else { } else {
String::new() String::new()

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::SyntaxContext; use rustc_span::SyntaxContext;
@ -43,7 +43,7 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
impl<'tcx> LateLintPass<'tcx> for NumberedFields { impl<'tcx> LateLintPass<'tcx> for NumberedFields {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind if let ExprKind::Struct(path, fields @ [field, ..], StructTailExpr::None) = e.kind
// If the first character of any field is a digit it has to be a tuple. // If the first character of any field is a digit it has to be a tuple.
&& field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit) && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
// Type aliases can't be used as functions. // Type aliases can't be used as functions.

View file

@ -5,7 +5,7 @@ use clippy_utils::higher::ForLoop;
use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::{Span, sym}; use rustc_span::{Span, sym};
use std::iter::once; use std::iter::once;
@ -164,7 +164,7 @@ fn never_loop_expr<'tcx>(
}, },
ExprKind::Struct(_, fields, base) => { ExprKind::Struct(_, fields, base) => {
let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id); let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id);
if let Some(base) = base { if let StructTailExpr::Base(base) = base {
combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id)) combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id))
} else { } else {
fields fields

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
@ -51,7 +51,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Struct(_, fields, Some(base)) = expr.kind { if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
if let ty::Adt(def, _) = ty.kind() { if let ty::Adt(def, _) = ty.kind() {
if fields.len() == def.non_enum_variant().fields.len() if fields.len() == def.non_enum_variant().fields.len()

View file

@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{ use rustc_hir::{
BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind,
UnsafeSource, is_range_literal, UnsafeSource, StructTailExpr, is_range_literal,
}; };
use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -238,7 +238,10 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
ExprKind::Struct(_, fields, ref base) => { ExprKind::Struct(_, fields, ref base) => {
!has_drop(cx, cx.typeck_results().expr_ty(expr)) !has_drop(cx, cx.typeck_results().expr_ty(expr))
&& fields.iter().all(|field| has_no_effect(cx, field.expr)) && fields.iter().all(|field| has_no_effect(cx, field.expr))
&& base.as_ref().is_none_or(|base| has_no_effect(cx, base)) && match &base {
StructTailExpr::None | StructTailExpr::DefaultFields(_) => true,
StructTailExpr::Base(base) => has_no_effect(cx, base),
}
}, },
ExprKind::Call(callee, args) => { ExprKind::Call(callee, args) => {
if let ExprKind::Path(ref qpath) = callee.kind { if let ExprKind::Path(ref qpath) = callee.kind {
@ -342,6 +345,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
if has_drop(cx, cx.typeck_results().expr_ty(expr)) { if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
None None
} else { } else {
let base = match base {
StructTailExpr::Base(base) => Some(base),
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
};
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
} }
}, },

View file

@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
@ -86,7 +86,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
return; return;
}; };
let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], None) = inner_expr.kind else { let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind else {
return; return;
}; };

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_copy; use clippy_utils::ty::is_copy;
use clippy_utils::{get_parent_expr, path_to_local}; use clippy_utils::{get_parent_expr, path_to_local};
use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp}; use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
@ -59,15 +59,15 @@ impl LateLintPass<'_> for UnnecessaryStruct {
let field_path = same_path_in_all_fields(cx, expr, fields); let field_path = same_path_in_all_fields(cx, expr, fields);
let sugg = match (field_path, base) { let sugg = match (field_path, base) {
(Some(&path), None) => { (Some(&path), StructTailExpr::None | StructTailExpr::DefaultFields(_)) => {
// all fields match, no base given // all fields match, no base given
path.span path.span
}, },
(Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => { (Some(path), StructTailExpr::Base(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
// all fields match, has base: ensure that the path of the base matches // all fields match, has base: ensure that the path of the base matches
base.span base.span
}, },
(None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => { (None, StructTailExpr::Base(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
// just the base, no explicit fields // just the base, no explicit fields
base.span base.span
}, },

View file

@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{ use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind,
ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
@ -598,7 +598,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
}, },
ExprKind::Struct(qpath, fields, base) => { ExprKind::Struct(qpath, fields, base) => {
bind!(self, qpath, fields); bind!(self, qpath, fields);
opt_bind!(self, base); let base = OptionPat::new(match base {
StructTailExpr::Base(base) => Some(self.bind("base", base)),
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
});
kind!("Struct({qpath}, {fields}, {base})"); kind!("Struct({qpath}, {fields}, {base})");
self.qpath(qpath); self.qpath(qpath);
self.slice(fields, |field| { self.slice(fields, |field| {

View file

@ -8,7 +8,7 @@ use crate::ty::is_type_diagnostic_item;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_hir::{Arm, Block, Expr, ExprKind, StructTailExpr, HirId, LoopSource, MatchSource, Node, Pat, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::{Span, sym, symbol}; use rustc_span::{Span, sym, symbol};
@ -236,7 +236,7 @@ impl<'a> Range<'a> {
limits: ast::RangeLimits::Closed, limits: ast::RangeLimits::Closed,
}) })
}, },
ExprKind::Struct(path, fields, None) => match (path, fields) { ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
(QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range { (QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
start: None, start: None,
end: None, end: None,

View file

@ -10,7 +10,7 @@ use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
TyKind, TyKind, StructTailExpr,
}; };
use rustc_lexer::{TokenKind, tokenize}; use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -380,7 +380,12 @@ impl HirEqInterExpr<'_, '_, '_> {
(ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)), (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)),
(&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
self.eq_qpath(l_path, r_path) self.eq_qpath(l_path, r_path)
&& both(lo.as_ref(), ro.as_ref(), |l, r| self.eq_expr(l, r)) && match (lo, ro) {
(StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r),
(StructTailExpr::None, StructTailExpr::None) => true,
(StructTailExpr::DefaultFields(_), StructTailExpr::DefaultFields(_)) => true,
_ => false,
}
&& over(lf, rf, |l, r| self.eq_expr_field(l, r)) && over(lf, rf, |l, r| self.eq_expr_field(l, r))
}, },
(&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
@ -1017,7 +1022,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(f.expr); self.hash_expr(f.expr);
} }
if let Some(e) = *expr { if let StructTailExpr::Base(e) = *expr {
self.hash_expr(e); self.hash_expr(e);
} }
}, },

View file

@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
use rustc_hir::{ use rustc_hir::{
AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath,
Safety, Stmt, UnOp, UnsafeSource, Safety, Stmt, UnOp, UnsafeSource, StructTailExpr,
}; };
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
@ -663,7 +663,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
for field in fields { for field in fields {
helper(typeck, true, field.expr, f)?; helper(typeck, true, field.expr, f)?;
} }
if let Some(default) = default { if let StructTailExpr::Base(default) = default {
helper(typeck, false, default, f)?; helper(typeck, false, default, f)?;
} }
}, },

View file

@ -12,17 +12,6 @@ error: functional record updates are not allowed in destructuring assignments
LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; LL | Struct { a, ..d } = Struct { a: 1, b: 2 };
| ^ help: consider removing the trailing pattern | ^ help: consider removing the trailing pattern
error[E0797]: base expression required after `..`
--> $DIR/struct_destructure_fail.rs:15:19
|
LL | Struct { a, .. };
| ^
|
help: add a base expression here
|
LL | Struct { a, ../* expr */ };
| ++++++++++
error[E0026]: struct `Struct` does not have a field named `c` error[E0026]: struct `Struct` does not have a field named `c`
--> $DIR/struct_destructure_fail.rs:10:20 --> $DIR/struct_destructure_fail.rs:10:20
| |
@ -48,6 +37,17 @@ help: or always ignore missing fields here
LL | Struct { a, .. } = Struct { a: 1, b: 2 }; LL | Struct { a, .. } = Struct { a: 1, b: 2 };
| ~~~~~~ | ~~~~~~
error[E0797]: base expression required after `..`
--> $DIR/struct_destructure_fail.rs:15:19
|
LL | Struct { a, .. };
| ^
|
help: add a base expression here
|
LL | Struct { a, ../* expr */ };
| ++++++++++
error: aborting due to 5 previous errors error: aborting due to 5 previous errors
Some errors have detailed explanations: E0026, E0027, E0797. Some errors have detailed explanations: E0026, E0027, E0797.

View file

@ -0,0 +1,106 @@
#![feature(generic_const_exprs)]
#![allow(unused_variables, dead_code, incomplete_features)]
pub struct S;
#[derive(Default)]
pub struct Foo {
pub bar: S = S, //~ ERROR default values on `struct` fields aren't supported
pub baz: i32 = 42 + 3, //~ ERROR default values on `struct` fields aren't supported
}
#[derive(Default)]
pub enum Bar {
#[default]
Foo { //~ ERROR the `#[default]` attribute may only be used on unit enum variants
bar: S = S, //~ ERROR default values on `struct` fields aren't supported
baz: i32 = 42 + 3, //~ ERROR default values on `struct` fields aren't supported
}
}
#[derive(Default)]
pub struct Qux<A, const C: i32> {
bar: S = Qux::<A, C>::S, //~ ERROR default values on `struct` fields aren't supported
baz: i32 = foo(), //~ ERROR default values on `struct` fields aren't supported
bat: i32 = <Qux<A, C> as T>::K, //~ ERROR default values on `struct` fields aren't supported
bay: i32 = C, //~ ERROR default values on `struct` fields aren't supported
bak: Vec<A> = Vec::new(), //~ ERROR default values on `struct` fields aren't supported
}
impl<A, const C: i32> Qux<A, C> {
const S: S = S;
}
trait T {
const K: i32;
}
impl<A, const C: i32> T for Qux<A, C> {
const K: i32 = 2;
}
const fn foo() -> i32 {
42
}
#[derive(Default)]
pub struct Opt {
mandatory: Option<()>,
optional: () = (), //~ ERROR default values on `struct` fields aren't supported
}
#[derive(Default)]
pub enum OptEnum {
#[default]
Variant { //~ ERROR the `#[default]` attribute may only be used on unit enum variants
mandatory: Option<()>,
optional: () = (), //~ ERROR default values on `struct` fields aren't supported
}
}
fn main () {
let x = Foo { .. }; //~ ERROR base expression required after `..`
let y = Foo::default();
let z = Foo { baz: 1, .. }; //~ ERROR base expression required after `..`
assert_eq!(45, x.baz);
assert_eq!(45, y.baz);
assert_eq!(1, z.baz);
let x = Bar::Foo { .. }; //~ ERROR base expression required after `..`
let y = Bar::default();
let z = Bar::Foo { baz: 1, .. }; //~ ERROR base expression required after `..`
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, x));
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, y));
assert!(matches!(Bar::Foo { bar: S, baz: 1 }, z));
let x = Qux::<i32, 4> { .. }; //~ ERROR base expression required after `..`
assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
//~^ ERROR base expression required after `..`
assert!(x.bak.is_empty());
let y = Opt { mandatory: None, .. };
//~^ ERROR base expression required after `..`
assert!(matches!(Opt::default(), y));
let z = Opt::default();
assert!(matches!(Opt { mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(Opt { .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(Opt { optional: (), .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
let y = OptEnum::Variant { mandatory: None, .. };
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::default(), y));
let z = OptEnum::default();
assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::Variant { .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
//~^ ERROR base expression required after `..`
assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
//~^ ERROR base expression required after `..`
}

View file

@ -0,0 +1,318 @@
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/feature-gate-default-field-values.rs:15:5
|
LL | Foo {
| ^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/feature-gate-default-field-values.rs:55:5
|
LL | Variant {
| ^^^^^^^
|
= help: consider a manual implementation of `Default`
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:8:15
|
LL | pub bar: S = S,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:9:17
|
LL | pub baz: i32 = 42 + 3,
| ^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:16:15
|
LL | bar: S = S,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:17:17
|
LL | baz: i32 = 42 + 3,
| ^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:23:11
|
LL | bar: S = Qux::<A, C>::S,
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:24:13
|
LL | baz: i32 = foo(),
| ^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:25:13
|
LL | bat: i32 = <Qux<A, C> as T>::K,
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:26:13
|
LL | bay: i32 = C,
| ^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:27:16
|
LL | bak: Vec<A> = Vec::new(),
| ^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:49:17
|
LL | optional: () = (),
| ^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/feature-gate-default-field-values.rs:57:21
|
LL | optional: () = (),
| ^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:62:21
|
LL | let x = Foo { .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let x = Foo { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:64:29
|
LL | let z = Foo { baz: 1, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let z = Foo { baz: 1, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:70:26
|
LL | let x = Bar::Foo { .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let x = Bar::Foo { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:72:34
|
LL | let z = Bar::Foo { baz: 1, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let z = Bar::Foo { baz: 1, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:78:31
|
LL | let x = Qux::<i32, 4> { .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let x = Qux::<i32, 4> { ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:79:73
|
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, .. }, x));
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, bay: 4, ../* expr */ }, x));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:82:38
|
LL | let y = Opt { mandatory: None, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let y = Opt { mandatory: None, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:86:47
|
LL | assert!(matches!(Opt { mandatory: None, .. }, z));
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | assert!(matches!(Opt { mandatory: None, ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:88:30
|
LL | assert!(matches!(Opt { .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(Opt { ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:90:44
|
LL | assert!(matches!(Opt { optional: (), .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(Opt { optional: (), ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:92:61
|
LL | assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
| ^
|
help: remove the `..` as all the fields are already present
|
LL - assert!(matches!(Opt { optional: (), mandatory: None, .. }, z));
LL + assert!(matches!(Opt { optional: (), mandatory: None, }, z));
|
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:94:51
|
LL | let y = OptEnum::Variant { mandatory: None, .. };
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | let y = OptEnum::Variant { mandatory: None, ../* expr */ };
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:98:60
|
LL | assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z));
| ^
|
= help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
help: add a base expression here
|
LL | assert!(matches!(OptEnum::Variant { mandatory: None, ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:100:43
|
LL | assert!(matches!(OptEnum::Variant { .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(OptEnum::Variant { ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:102:57
|
LL | assert!(matches!(OptEnum::Variant { optional: (), .. }, z));
| ^
|
help: add a base expression here
|
LL | assert!(matches!(OptEnum::Variant { optional: (), ../* expr */ }, z));
| ++++++++++
error[E0797]: base expression required after `..`
--> $DIR/feature-gate-default-field-values.rs:104:74
|
LL | assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
| ^
|
help: remove the `..` as all the fields are already present
|
LL - assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z));
LL + assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, }, z));
|
error: aborting due to 29 previous errors
Some errors have detailed explanations: E0658, E0797.
For more information about an error, try `rustc --explain E0658`.

View file

@ -1,35 +0,0 @@
//@ run-rustfix
#![allow(dead_code)]
enum E {
A,
}
struct S {
field1: i32, //~ ERROR default values on `struct` fields aren't supported
field2: E, //~ ERROR default values on `struct` fields aren't supported
field3: i32, //~ ERROR default values on `struct` fields aren't supported
field4: i32, //~ ERROR default values on `struct` fields aren't supported
field5: E, //~ ERROR default values on `struct` fields aren't supported
field6: E, //~ ERROR default values on `struct` fields aren't supported
}
struct S1 {
field1: i32, //~ ERROR expected `,`, or `}`, found `field2`
field2: E, //~ ERROR expected `,`, or `}`, found `field3`
field3: i32, //~ ERROR default values on `struct` fields aren't supported
field4: i32, //~ ERROR default values on `struct` fields aren't supported
field5: E, //~ ERROR default values on `struct` fields aren't supported
field6: E, //~ ERROR default values on `struct` fields aren't supported
}
struct S2 {
field1 : i32, //~ ERROR expected `:`, found `=`
field2: E, //~ ERROR expected `:`, found `;`
}
const fn foo(_: i32) -> E {
E::A
}
fn main() {}

View file

@ -1,4 +1,3 @@
//@ run-rustfix
#![allow(dead_code)] #![allow(dead_code)]
enum E { enum E {

View file

@ -1,137 +1,17 @@
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:9:16
|
LL | field1: i32 = 42,
| ^^^^^
|
help: remove this unsupported default value
|
LL - field1: i32 = 42,
LL + field1: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:10:14
|
LL | field2: E = E::A,
| ^^^^^^^
|
help: remove this unsupported default value
|
LL - field2: E = E::A,
LL + field2: E,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
help: remove this unsupported default value
|
LL - field3: i32 = 1 + 2,
LL + field3: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:12:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field4: i32 = { 1 + 2 },
LL + field4: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field5: E = foo(42),
LL + field5: E,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:14:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field6: E = { foo(42) },
LL + field6: E,
|
error: expected `,`, or `}`, found `field2` error: expected `,`, or `}`, found `field2`
--> $DIR/struct-default-values-and-missing-field-separator.rs:18:16 --> $DIR/struct-default-values-and-missing-field-separator.rs:17:16
| |
LL | field1: i32 LL | field1: i32
| ^ help: try adding a comma: `,` | ^ help: try adding a comma: `,`
error: expected `,`, or `}`, found `field3` error: expected `,`, or `}`, found `field3`
--> $DIR/struct-default-values-and-missing-field-separator.rs:19:14 --> $DIR/struct-default-values-and-missing-field-separator.rs:18:14
| |
LL | field2: E LL | field2: E
| ^ help: try adding a comma: `,` | ^ help: try adding a comma: `,`
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
help: remove this unsupported default value
|
LL - field3: i32 = 1 + 2,
LL + field3: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:21:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field4: i32 = { 1 + 2 },
LL + field4: i32,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field5: E = foo(42),
LL + field5: E,
|
error: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:23:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
help: remove this unsupported default value
|
LL - field6: E = { foo(42) },
LL + field6: E,
|
error: expected `:`, found `=` error: expected `:`, found `=`
--> $DIR/struct-default-values-and-missing-field-separator.rs:27:12 --> $DIR/struct-default-values-and-missing-field-separator.rs:26:12
| |
LL | field1 = i32, LL | field1 = i32,
| ^ | ^
@ -140,7 +20,7 @@ LL | field1 = i32,
| help: field names and their types are separated with `:` | help: field names and their types are separated with `:`
error: expected `:`, found `;` error: expected `:`, found `;`
--> $DIR/struct-default-values-and-missing-field-separator.rs:28:11 --> $DIR/struct-default-values-and-missing-field-separator.rs:27:11
| |
LL | field2; E, LL | field2; E,
| ^ | ^
@ -148,5 +28,106 @@ LL | field2; E,
| expected `:` | expected `:`
| help: field names and their types are separated with `:` | help: field names and their types are separated with `:`
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:8:16
|
LL | field1: i32 = 42,
| ^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:9:14
|
LL | field2: E = E::A,
| ^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:10:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:12:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:19:16
|
LL | field3: i32 = 1 + 2,
| ^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
|
LL | field4: i32 = { 1 + 2 },
| ^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:21:14
|
LL | field5: E = foo(42),
| ^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: default values on `struct` fields aren't supported
--> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
|
LL | field6: E = { foo(42) },
| ^^^^^^^^^^^^^^
|
= note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
= help: add `#![feature(default_field_values)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 14 previous errors error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -20,8 +20,8 @@ ast-stats-1 Stmt 160 ( 2.4%) 5 32
ast-stats-1 - Let 32 ( 0.5%) 1 ast-stats-1 - Let 32 ( 0.5%) 1
ast-stats-1 - MacCall 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1
ast-stats-1 - Expr 96 ( 1.4%) 3 ast-stats-1 - Expr 96 ( 1.4%) 3
ast-stats-1 FieldDef 176 ( 2.6%) 2 88
ast-stats-1 Block 192 ( 2.9%) 6 32 ast-stats-1 Block 192 ( 2.9%) 6 32
ast-stats-1 FieldDef 208 ( 3.1%) 2 104
ast-stats-1 Variant 208 ( 3.1%) 2 104 ast-stats-1 Variant 208 ( 3.1%) 2 104
ast-stats-1 AssocItem 352 ( 5.3%) 4 88 ast-stats-1 AssocItem 352 ( 5.3%) 4 88
ast-stats-1 - Type 176 ( 2.6%) 2 ast-stats-1 - Type 176 ( 2.6%) 2
@ -29,7 +29,7 @@ ast-stats-1 - Fn 176 ( 2.6%) 2
ast-stats-1 GenericBound 352 ( 5.3%) 4 88 ast-stats-1 GenericBound 352 ( 5.3%) 4 88
ast-stats-1 - Trait 352 ( 5.3%) 4 ast-stats-1 - Trait 352 ( 5.3%) 4
ast-stats-1 GenericParam 480 ( 7.2%) 5 96 ast-stats-1 GenericParam 480 ( 7.2%) 5 96
ast-stats-1 Pat 504 ( 7.6%) 7 72 ast-stats-1 Pat 504 ( 7.5%) 7 72
ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Wild 72 ( 1.1%) 1 ast-stats-1 - Wild 72 ( 1.1%) 1
ast-stats-1 - Ident 360 ( 5.4%) 5 ast-stats-1 - Ident 360 ( 5.4%) 5
@ -39,13 +39,13 @@ ast-stats-1 - Match 72 ( 1.1%) 1
ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Lit 144 ( 2.2%) 2 ast-stats-1 - Lit 144 ( 2.2%) 2
ast-stats-1 - Block 216 ( 3.2%) 3 ast-stats-1 - Block 216 ( 3.2%) 3
ast-stats-1 PathSegment 744 (11.2%) 31 24 ast-stats-1 PathSegment 744 (11.1%) 31 24
ast-stats-1 Ty 896 (13.4%) 14 64 ast-stats-1 Ty 896 (13.4%) 14 64
ast-stats-1 - Ref 64 ( 1.0%) 1 ast-stats-1 - Ref 64 ( 1.0%) 1
ast-stats-1 - Ptr 64 ( 1.0%) 1 ast-stats-1 - Ptr 64 ( 1.0%) 1
ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2 ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2
ast-stats-1 - Path 640 ( 9.6%) 10 ast-stats-1 - Path 640 ( 9.6%) 10
ast-stats-1 Item 1_224 (18.4%) 9 136 ast-stats-1 Item 1_224 (18.3%) 9 136
ast-stats-1 - ForeignMod 136 ( 2.0%) 1 ast-stats-1 - ForeignMod 136 ( 2.0%) 1
ast-stats-1 - Trait 136 ( 2.0%) 1 ast-stats-1 - Trait 136 ( 2.0%) 1
ast-stats-1 - Impl 136 ( 2.0%) 1 ast-stats-1 - Impl 136 ( 2.0%) 1
@ -53,7 +53,7 @@ ast-stats-1 - Enum 136 ( 2.0%) 1
ast-stats-1 - Fn 272 ( 4.1%) 2 ast-stats-1 - Fn 272 ( 4.1%) 2
ast-stats-1 - Use 408 ( 6.1%) 3 ast-stats-1 - Use 408 ( 6.1%) 3
ast-stats-1 ---------------------------------------------------------------- ast-stats-1 ----------------------------------------------------------------
ast-stats-1 Total 6_664 116 ast-stats-1 Total 6_696 116
ast-stats-1 ast-stats-1
ast-stats-2 POST EXPANSION AST STATS ast-stats-2 POST EXPANSION AST STATS
ast-stats-2 Name Accumulated Size Count Item Size ast-stats-2 Name Accumulated Size Count Item Size
@ -70,7 +70,7 @@ ast-stats-2 - Fn 88 ( 1.2%) 1
ast-stats-2 Arm 96 ( 1.3%) 2 48 ast-stats-2 Arm 96 ( 1.3%) 2 48
ast-stats-2 FnDecl 120 ( 1.6%) 5 24 ast-stats-2 FnDecl 120 ( 1.6%) 5 24
ast-stats-2 InlineAsm 120 ( 1.6%) 1 120 ast-stats-2 InlineAsm 120 ( 1.6%) 1 120
ast-stats-2 Attribute 128 ( 1.8%) 4 32 ast-stats-2 Attribute 128 ( 1.7%) 4 32
ast-stats-2 - DocComment 32 ( 0.4%) 1 ast-stats-2 - DocComment 32 ( 0.4%) 1
ast-stats-2 - Normal 96 ( 1.3%) 3 ast-stats-2 - Normal 96 ( 1.3%) 3
ast-stats-2 Param 160 ( 2.2%) 4 40 ast-stats-2 Param 160 ( 2.2%) 4 40
@ -78,33 +78,33 @@ ast-stats-2 Stmt 160 ( 2.2%) 5 32
ast-stats-2 - Let 32 ( 0.4%) 1 ast-stats-2 - Let 32 ( 0.4%) 1
ast-stats-2 - Semi 32 ( 0.4%) 1 ast-stats-2 - Semi 32 ( 0.4%) 1
ast-stats-2 - Expr 96 ( 1.3%) 3 ast-stats-2 - Expr 96 ( 1.3%) 3
ast-stats-2 FieldDef 176 ( 2.4%) 2 88
ast-stats-2 Block 192 ( 2.6%) 6 32 ast-stats-2 Block 192 ( 2.6%) 6 32
ast-stats-2 FieldDef 208 ( 2.8%) 2 104
ast-stats-2 Variant 208 ( 2.8%) 2 104 ast-stats-2 Variant 208 ( 2.8%) 2 104
ast-stats-2 AssocItem 352 ( 4.8%) 4 88 ast-stats-2 AssocItem 352 ( 4.8%) 4 88
ast-stats-2 - Type 176 ( 2.4%) 2 ast-stats-2 - Type 176 ( 2.4%) 2
ast-stats-2 - Fn 176 ( 2.4%) 2 ast-stats-2 - Fn 176 ( 2.4%) 2
ast-stats-2 GenericBound 352 ( 4.8%) 4 88 ast-stats-2 GenericBound 352 ( 4.8%) 4 88
ast-stats-2 - Trait 352 ( 4.8%) 4 ast-stats-2 - Trait 352 ( 4.8%) 4
ast-stats-2 GenericParam 480 ( 6.6%) 5 96 ast-stats-2 GenericParam 480 ( 6.5%) 5 96
ast-stats-2 Pat 504 ( 6.9%) 7 72 ast-stats-2 Pat 504 ( 6.9%) 7 72
ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - Wild 72 ( 1.0%) 1 ast-stats-2 - Wild 72 ( 1.0%) 1
ast-stats-2 - Ident 360 ( 4.9%) 5 ast-stats-2 - Ident 360 ( 4.9%) 5
ast-stats-2 Expr 648 ( 8.9%) 9 72 ast-stats-2 Expr 648 ( 8.8%) 9 72
ast-stats-2 - Path 72 ( 1.0%) 1 ast-stats-2 - Path 72 ( 1.0%) 1
ast-stats-2 - Match 72 ( 1.0%) 1 ast-stats-2 - Match 72 ( 1.0%) 1
ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - InlineAsm 72 ( 1.0%) 1
ast-stats-2 - Lit 144 ( 2.0%) 2 ast-stats-2 - Lit 144 ( 2.0%) 2
ast-stats-2 - Block 216 ( 3.0%) 3 ast-stats-2 - Block 216 ( 2.9%) 3
ast-stats-2 PathSegment 864 (11.8%) 36 24 ast-stats-2 PathSegment 864 (11.8%) 36 24
ast-stats-2 Ty 896 (12.3%) 14 64 ast-stats-2 Ty 896 (12.2%) 14 64
ast-stats-2 - Ref 64 ( 0.9%) 1 ast-stats-2 - Ref 64 ( 0.9%) 1
ast-stats-2 - Ptr 64 ( 0.9%) 1 ast-stats-2 - Ptr 64 ( 0.9%) 1
ast-stats-2 - ImplicitSelf 128 ( 1.8%) 2 ast-stats-2 - ImplicitSelf 128 ( 1.7%) 2
ast-stats-2 - Path 640 ( 8.8%) 10 ast-stats-2 - Path 640 ( 8.7%) 10
ast-stats-2 Item 1_496 (20.5%) 11 136 ast-stats-2 Item 1_496 (20.4%) 11 136
ast-stats-2 - Enum 136 ( 1.9%) 1 ast-stats-2 - Enum 136 ( 1.9%) 1
ast-stats-2 - Trait 136 ( 1.9%) 1 ast-stats-2 - Trait 136 ( 1.9%) 1
ast-stats-2 - Impl 136 ( 1.9%) 1 ast-stats-2 - Impl 136 ( 1.9%) 1
@ -113,7 +113,7 @@ ast-stats-2 - ForeignMod 136 ( 1.9%) 1
ast-stats-2 - Fn 272 ( 3.7%) 2 ast-stats-2 - Fn 272 ( 3.7%) 2
ast-stats-2 - Use 544 ( 7.4%) 4 ast-stats-2 - Use 544 ( 7.4%) 4
ast-stats-2 ---------------------------------------------------------------- ast-stats-2 ----------------------------------------------------------------
ast-stats-2 Total 7_312 127 ast-stats-2 Total 7_344 127
ast-stats-2 ast-stats-2
hir-stats HIR STATS hir-stats HIR STATS
hir-stats Name Accumulated Size Count Item Size hir-stats Name Accumulated Size Count Item Size
@ -138,9 +138,9 @@ hir-stats Stmt 96 ( 1.1%) 3 32
hir-stats - Let 32 ( 0.4%) 1 hir-stats - Let 32 ( 0.4%) 1
hir-stats - Semi 32 ( 0.4%) 1 hir-stats - Semi 32 ( 0.4%) 1
hir-stats - Expr 32 ( 0.4%) 1 hir-stats - Expr 32 ( 0.4%) 1
hir-stats FieldDef 112 ( 1.3%) 2 56
hir-stats FnDecl 120 ( 1.3%) 3 40 hir-stats FnDecl 120 ( 1.3%) 3 40
hir-stats Attribute 128 ( 1.4%) 4 32 hir-stats Attribute 128 ( 1.4%) 4 32
hir-stats FieldDef 128 ( 1.4%) 2 64
hir-stats GenericArgs 144 ( 1.6%) 3 48 hir-stats GenericArgs 144 ( 1.6%) 3 48
hir-stats Variant 144 ( 1.6%) 2 72 hir-stats Variant 144 ( 1.6%) 2 72
hir-stats GenericBound 256 ( 2.9%) 4 64 hir-stats GenericBound 256 ( 2.9%) 4 64
@ -163,7 +163,7 @@ hir-stats - Struct 64 ( 0.7%) 1
hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - InlineAsm 64 ( 0.7%) 1
hir-stats - Lit 128 ( 1.4%) 2 hir-stats - Lit 128 ( 1.4%) 2
hir-stats - Block 384 ( 4.3%) 6 hir-stats - Block 384 ( 4.3%) 6
hir-stats Item 968 (10.9%) 11 88 hir-stats Item 968 (10.8%) 11 88
hir-stats - Enum 88 ( 1.0%) 1 hir-stats - Enum 88 ( 1.0%) 1
hir-stats - Trait 88 ( 1.0%) 1 hir-stats - Trait 88 ( 1.0%) 1
hir-stats - Impl 88 ( 1.0%) 1 hir-stats - Impl 88 ( 1.0%) 1
@ -174,5 +174,5 @@ hir-stats - Use 352 ( 3.9%) 4
hir-stats Path 1_240 (13.9%) 31 40 hir-stats Path 1_240 (13.9%) 31 40
hir-stats PathSegment 1_920 (21.5%) 40 48 hir-stats PathSegment 1_920 (21.5%) 40 48
hir-stats ---------------------------------------------------------------- hir-stats ----------------------------------------------------------------
hir-stats Total 8_920 180 hir-stats Total 8_936 180
hir-stats hir-stats

View file

@ -0,0 +1,49 @@
#![feature(default_field_values)]
#[derive(Debug)]
pub struct S;
#[derive(Debug, Default)]
pub struct Foo {
pub bar: S = S,
pub baz: i32 = 42 + 3,
}
#[derive(Debug, Default)]
pub struct Bar {
pub bar: S, //~ ERROR the trait bound `S: Default` is not satisfied
pub baz: i32 = 42 + 3,
}
#[derive(Default)]
pub struct Qux<const C: i32> {
bar: S = Self::S, //~ ERROR generic `Self` types are currently not permitted in anonymous constants
baz: i32 = foo(),
bat: i32 = <Qux<{ C }> as T>::K, //~ ERROR generic parameters may not be used in const operations
bay: i32 = C,
}
impl<const C: i32> Qux<C> {
const S: S = S;
}
trait T {
const K: i32;
}
impl<const C: i32> T for Qux<C> {
const K: i32 = 2;
}
const fn foo() -> i32 {
42
}
fn main () {
let _ = Foo { .. }; // ok
let _ = Foo::default(); // ok
let _ = Bar { .. }; //~ ERROR mandatory field
let _ = Bar::default(); // silenced
let _ = Bar { bar: S, .. }; // ok
let _ = Qux::<4> { .. };
}

View file

@ -0,0 +1,40 @@
error: generic parameters may not be used in const operations
--> $DIR/default-field-values-failures.rs:22:23
|
LL | bat: i32 = <Qux<{ C }> as T>::K,
| ^ cannot perform const operation using `C`
|
= help: const parameters may only be used as standalone arguments, i.e. `C`
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
error[E0277]: the trait bound `S: Default` is not satisfied
--> $DIR/default-field-values-failures.rs:14:5
|
LL | #[derive(Debug, Default)]
| ------- in this derive macro expansion
LL | pub struct Bar {
LL | pub bar: S,
| ^^^^^^^^^^ the trait `Default` is not implemented for `S`
|
= note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `S` with `#[derive(Default)]`
|
LL + #[derive(Default)]
LL | pub struct S;
|
error: missing mandatory field `bar`
--> $DIR/default-field-values-failures.rs:45:21
|
LL | let _ = Bar { .. };
| ^
error: generic `Self` types are currently not permitted in anonymous constants
--> $DIR/default-field-values-failures.rs:20:14
|
LL | bar: S = Self::S,
| ^^^^
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,19 @@
#![feature(default_field_values, generic_const_exprs)]
#![allow(incomplete_features)]
#[warn(default_field_always_invalid_const)] //~ WARN lint `default_field_always_invalid_const` can't be warned on
pub struct Bat {
pub bax: u8 = panic!("asdf"),
//~^ ERROR evaluation of constant value failed
//~| WARN default field fails const-evaluation
}
pub struct Baz<const C: u8> {
pub bax: u8 = 130 + C, // ok
pub bat: u8 = 130 + 130,
//~^ ERROR evaluation of `Baz::<C>::bat::{constant#0}` failed
//~| ERROR default field fails const-evaluation
pub bay: u8 = 1, // ok
}
fn main() {}

View file

@ -0,0 +1,45 @@
warning: lint `default_field_always_invalid_const` can't be warned on
--> $DIR/default-field-values-invalid-const.rs:4:8
|
LL | #[warn(default_field_always_invalid_const)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ either `deny` or `allow`, no other lint level is supported for this lint
error[E0080]: evaluation of constant value failed
--> $DIR/default-field-values-invalid-const.rs:6:19
|
LL | pub bax: u8 = panic!("asdf"),
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'asdf', $DIR/default-field-values-invalid-const.rs:6:19
|
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: default field fails const-evaluation
--> $DIR/default-field-values-invalid-const.rs:6:5
|
LL | pub bax: u8 = panic!("asdf"),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this field's constant fails const-evaluation, as seen in the previous error
|
= help: you can skip const-evaluation of default fields by enabling this lint
note: the lint level is defined here
--> $DIR/default-field-values-invalid-const.rs:4:8
|
LL | #[warn(default_field_always_invalid_const)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of `Baz::<C>::bat::{constant#0}` failed
--> $DIR/default-field-values-invalid-const.rs:13:19
|
LL | pub bat: u8 = 130 + 130,
| ^^^^^^^^^ attempt to compute `130_u8 + 130_u8`, which would overflow
error: default field fails const-evaluation
--> $DIR/default-field-values-invalid-const.rs:13:5
|
LL | pub bat: u8 = 130 + 130,
| ^^^^^^^^^^^^^^^^^^^^^^^ this field's constant fails const-evaluation, as seen in the previous error
|
= help: you can skip const-evaluation of default fields by enabling this lint
= note: `#[deny(default_field_always_invalid_const)]` on by default
error: aborting due to 3 previous errors; 2 warnings emitted
For more information about this error, try `rustc --explain E0080`.

View file

@ -0,0 +1,68 @@
//@ run-pass
#![feature(default_field_values, generic_const_exprs)]
#![allow(unused_variables, dead_code, incomplete_features)]
pub struct S;
#[derive(Default)]
pub struct Foo {
pub bar: S = S,
pub baz: i32 = 42 + 3,
}
#[derive(Default)]
pub enum Bar {
#[default]
Foo {
bar: S = S,
baz: i32 = 42 + 3,
}
}
#[derive(Default)]
pub struct Qux<A, const C: i32> {
bar: S = Qux::<A, C>::S,
baz: i32 = foo(),
bat: i32 = <Qux<A, C> as T>::K,
baq: i32 = Self::K,
bay: i32 = C,
bak: Vec<A> = Vec::new(),
}
impl<A, const C: i32> Qux<A, C> {
const S: S = S;
}
trait T {
const K: i32;
}
impl<A, const C: i32> T for Qux<A, C> {
const K: i32 = 2;
}
const fn foo() -> i32 {
42
}
fn main () {
let x = Foo { .. };
let y = Foo::default();
let z = Foo { baz: 1, .. };
assert_eq!(45, x.baz);
assert_eq!(45, y.baz);
assert_eq!(1, z.baz);
let x = Bar::Foo { .. };
let y = Bar::default();
let z = Bar::Foo { baz: 1, .. };
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, x));
assert!(matches!(Bar::Foo { bar: S, baz: 45 }, y));
assert!(matches!(Bar::Foo { bar: S, baz: 1 }, z));
let x = Qux::<i32, 4> { .. };
assert!(matches!(Qux::<i32, 4> { bar: S, baz: 42, bat: 2, baq: 2, bay: 4, .. }, x));
assert!(x.bak.is_empty());
}

View file

@ -92,7 +92,7 @@ body:
adt_def: adt_def:
AdtDef { AdtDef {
did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo)
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
flags: IS_ENUM flags: IS_ENUM
repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
args: [] args: []
@ -154,7 +154,7 @@ body:
adt_def: adt_def:
AdtDef { AdtDef {
did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo)
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
flags: IS_ENUM flags: IS_ENUM
repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
args: [] args: []
@ -206,7 +206,7 @@ body:
adt_def: adt_def:
AdtDef { AdtDef {
did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo)
variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }]
flags: IS_ENUM flags: IS_ENUM
repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
args: [] args: []