about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-08-24 17:22:48 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-12-09 21:55:01 +0000
commit9ac95c10c09faf50cc22eb97b6e1c59d64053c28 (patch)
tree56bb7780a34d3ed47f98ec6b2637429c7259539e
parentf6cb952dc115fd1311b02b694933e31d8dc8b002 (diff)
downloadrust-9ac95c10c09faf50cc22eb97b6e1c59d64053c28.tar.gz
rust-9ac95c10c09faf50cc22eb97b6e1c59d64053c28.zip
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.
-rw-r--r--compiler/rustc_ast/src/ast.rs1
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs3
-rw-r--r--compiler/rustc_ast/src/visit.rs4
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl4
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs19
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs1
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/decodable.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs67
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs8
-rw-r--r--compiler/rustc_expand/src/placeholders.rs1
-rw-r--r--compiler/rustc_feature/src/unstable.rs3
-rw-r--r--compiler/rustc_hir/src/hir.rs71
-rw-r--r--compiler/rustc_hir/src/intravisit.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs6
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs36
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl6
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs41
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs103
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs6
-rw-r--r--compiler/rustc_lint/messages.ftl4
-rw-r--r--compiler/rustc_lint/src/default_field_always_invalid.rs91
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_lint/src/lints.rs9
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs1
-rw-r--r--compiler/rustc_middle/src/thir.rs17
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs3
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs7
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs64
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs54
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs16
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs8
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs11
-rw-r--r--compiler/rustc_passes/src/liveness.rs7
-rw-r--r--compiler/rustc_privacy/src/lib.rs59
-rw-r--r--compiler/rustc_resolve/src/late.rs27
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/init_numbered_fields.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_update.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs11
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs4
-rw-r--r--tests/ui/destructuring-assignment/struct_destructure_fail.stderr22
-rw-r--r--tests/ui/feature-gates/feature-gate-default-field-values.rs106
-rw-r--r--tests/ui/feature-gates/feature-gate-default-field-values.stderr318
-rw-r--r--tests/ui/parser/struct-default-values-and-missing-field-separator.fixed35
-rw-r--r--tests/ui/parser/struct-default-values-and-missing-field-separator.rs1
-rw-r--r--tests/ui/parser/struct-default-values-and-missing-field-separator.stderr181
-rw-r--r--tests/ui/stats/input-stats.stderr36
-rw-r--r--tests/ui/structs/default-field-values-failures.rs49
-rw-r--r--tests/ui/structs/default-field-values-failures.stderr40
-rw-r--r--tests/ui/structs/default-field-values-invalid-const.rs19
-rw-r--r--tests/ui/structs/default-field-values-invalid-const.stderr45
-rw-r--r--tests/ui/structs/default-field-values-support.rs68
-rw-r--r--tests/ui/thir-print/thir-tree-match.stdout6
70 files changed, 1446 insertions, 369 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 69ba78282f9..650525a2f52 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3119,6 +3119,7 @@ pub struct FieldDef {
     pub ident: Option<Ident>,
 
     pub ty: P<Ty>,
+    pub default: Option<AnonConst>,
     pub is_placeholder: bool,
 }
 
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 3a4a8ce266e..2c09059fe19 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -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) {
-    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);
     visit_attrs(visitor, attrs);
     visitor.visit_vis(vis);
     visit_safety(visitor, safety);
     visit_opt(ident, |ident| visitor.visit_ident(ident));
     visitor.visit_ty(ty);
+    visit_opt(default, |default| visitor.visit_anon_const(default));
     visitor.visit_span(span);
 }
 
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 0b000c8cef8..a7f7c37693a 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -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 {
-    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);
     try_visit!(visitor.visit_vis(vis));
     visit_opt!(visitor, visit_ident, ident);
     try_visit!(visitor.visit_ty(ty));
+    visit_opt!(visitor, visit_anon_const, &*default);
     V::Result::output()
 }
 
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index a4dbf981115..b53cb7a3822 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -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_base_expression_double_dot =
-    base expression required after `..`
-    .suggestion = add a base expression here
-
 ast_lowering_clobber_abi_not_supported =
     `clobber_abi` is not supported on this target
 
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 447af57354f..665da14e861 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -115,14 +115,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
 }
 
 #[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)]
 #[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
 pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {
     #[primary_span]
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 84e648f4923..2ad0ff3200e 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec};
 use visit::{Visitor, walk_expr};
 
 use super::errors::{
-    AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
-    ClosureCannotBeStatic, CoroutineTooManyParameters,
-    FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
-    NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
+    AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
+    CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
+    InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
+    UnderscoreExprLhsAssign,
 };
 use super::{
     GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
@@ -357,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ),
                 ExprKind::Struct(se) => {
                     let rest = match &se.rest {
-                        StructRest::Base(e) => Some(self.lower_expr(e)),
-                        StructRest::Rest(sp) => {
-                            let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
-                            Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
-                        }
-                        StructRest::None => None,
+                        StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
+                        StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
+                        StructRest::None => hir::StructTailExpr::None,
                     };
                     hir::ExprKind::Struct(
                         self.arena.alloc(self.lower_qpath(
@@ -1526,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         hir::ExprKind::Struct(
             self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
             fields,
-            None,
+            hir::StructTailExpr::None,
         )
     }
 
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index fb09f1c7fee..1078aeea22b 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -723,6 +723,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 None => Ident::new(sym::integer(index), self.lower_span(f.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,
             safety: self.lower_safety(f.safety, hir::Safety::Safe),
         }
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 390a575a186..688d4d635cf 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -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!(generic_const_items, "generic const items are experimental");
     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!(postfix_match, "postfix match is experimental");
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index b42c99e1a6d..8dcc5324fdf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         expr: &hir::Expr<'_>,
     ) {
         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::def::Res::Def(_, def_id) = path.res 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>,
         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
             // `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`
diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
index 686424770fc..469092e7b1c 100644
--- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
@@ -205,7 +205,7 @@ where
             let fields = fields
                 .iter()
                 .enumerate()
-                .map(|(i, &(ident, span))| {
+                .map(|(i, &(ident, span, _))| {
                     let arg = getarg(cx, span, ident.name, i);
                     cx.field_imm(span, ident, arg)
                 })
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index d4befd12190..12d5587b5db 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
     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(
     cx: &ExtCtxt<'_>,
     trait_span: Span,
     substr: &Substructure<'_>,
     summary: &StaticFields,
 ) -> 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 {
         Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
         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)
         }
         Named(fields) => {
             let default_fields = fields
                 .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();
             cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
         }
@@ -93,10 +105,38 @@ fn default_enum_substructure(
     } {
         Ok(default_variant) => {
             // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
-            cx.expr_path(cx.path(default_variant.span, vec![
-                Ident::new(kw::SelfUpper, default_variant.span),
-                default_variant.ident,
-            ]))
+            match &default_variant.data {
+                VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
+                    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)),
     };
@@ -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 });
         return Err(guar);
     }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index f6eea0b21ca..846d8784dea 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
 pub(crate) use SubstructureFields::*;
 use rustc_ast::ptr::P;
 use rustc_ast::{
-    self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
-    Mutability, PatKind, VariantData,
+    self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
+    Generics, Mutability, PatKind, VariantData,
 };
 use rustc_attr as attr;
 use rustc_expand::base::{Annotatable, ExtCtxt};
@@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
     /// Tuple and unit structs/enum variants like this.
     Unnamed(Vec<Span>, IsTuple),
     /// Normal structs/struct variants.
-    Named(Vec<(Ident, Span)>),
+    Named(Vec<(Ident, Span, Option<AnonConst>)>),
 }
 
 /// A summary of the possible sets of fields.
@@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
         for field in struct_def.fields() {
             let sp = field.span.with_ctxt(self.span.ctxt());
             match field.ident {
-                Some(ident) => named_idents.push((ident, sp)),
+                Some(ident) => named_idents.push((ident, sp, field.default.clone())),
                 _ => just_spans.push(sp),
             }
         }
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index f044d964f13..9e459bd81a1 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -174,6 +174,7 @@ pub(crate) fn placeholder(
             vis,
             is_placeholder: true,
             safety: Safety::Default,
+            default: None,
         }]),
         AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
             attrs: Default::default(),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index bf26b5d25d2..93a605e197c 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -455,6 +455,9 @@ declare_features! (
     (unstable, custom_test_frameworks, "1.30.0", Some(50297)),
     /// Allows declarative macros 2.0 (`macro`).
     (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
     (unstable, deprecated_safe, "1.61.0", Some(94978)),
     /// Allows having using `suggestion` in the `#[deprecated]` attribute.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index a9696627f11..365e4cbb556 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1857,7 +1857,12 @@ impl Expr<'_> {
                 base.can_have_side_effects()
             }
             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)
@@ -1926,20 +1931,52 @@ impl Expr<'_> {
                 ExprKind::Path(QPath::Resolved(None, path2)),
             ) => path1.res == path2.res,
             (
-                ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None),
-                ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None),
+                ExprKind::Struct(
+                    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(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None),
+                ExprKind::Struct(
+                    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(QPath::LangItem(LangItem::RangeFrom, _), [val2], None),
+                ExprKind::Struct(
+                    QPath::LangItem(LangItem::RangeFrom, _),
+                    [val1],
+                    StructTailExpr::None,
+                ),
+                ExprKind::Struct(
+                    QPath::LangItem(LangItem::RangeFrom, _),
+                    [val2],
+                    StructTailExpr::None,
+                ),
             ) => val1.expr.equivalent_for_indexing(val2.expr),
             (
-                ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None),
-                ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None),
+                ExprKind::Struct(
+                    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)
                     && 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}`,
     /// 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.
     ///
@@ -2111,6 +2148,19 @@ pub enum ExprKind<'hir> {
     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.
 ///
 /// To resolve the path to a `DefId`, call [`qpath_res`].
@@ -3172,6 +3222,7 @@ pub struct FieldDef<'hir> {
     pub def_id: LocalDefId,
     pub ty: &'hir Ty<'hir>,
     pub safety: Safety,
+    pub default: Option<&'hir AnonConst>,
 }
 
 impl FieldDef<'_> {
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 602aa8be740..9abb0870bf0 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -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) => {
             try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
             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) => {
             walk_list!(visitor, visit_expr, subexpressions);
@@ -1190,10 +1193,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
     V::Result::output()
 }
 
-pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result {
-    try_visit!(visitor.visit_id(field.hir_id));
-    try_visit!(visitor.visit_ident(field.ident));
-    visitor.visit_ty(field.ty)
+pub fn walk_field_def<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>,
+) -> 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>>(
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index a4636da3f62..8f9b0626031 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1021,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
     }
 }
 
-fn lower_variant(
-    tcx: TyCtxt<'_>,
+fn lower_variant<'tcx>(
+    tcx: TyCtxt<'tcx>,
     variant_did: Option<LocalDefId>,
     ident: Ident,
     discr: ty::VariantDiscr,
-    def: &hir::VariantData<'_>,
+    def: &hir::VariantData<'tcx>,
     adt_kind: ty::AdtKind,
     parent_did: LocalDefId,
 ) -> ty::VariantDef {
@@ -1042,6 +1042,7 @@ fn lower_variant(
             name: f.ident.name,
             vis: tcx.visibility(f.def_id),
             safety: f.safety,
+            value: f.default.map(|v| v.def_id.to_def_id()),
         })
         .collect();
     let recovered = match def {
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 72d5b3ac4f5..5595504c3d9 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -125,6 +125,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             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(
             tcx,
             span,
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 0f3dcebc092..a74e36c45c6 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1080,22 +1080,36 @@ impl<'a> State<'a> {
         &mut self,
         qpath: &hir::QPath<'_>,
         fields: &[hir::ExprField<'_>],
-        wth: Option<&hir::Expr<'_>>,
+        wth: hir::StructTailExpr<'_>,
     ) {
         self.print_qpath(qpath, true);
         self.word("{");
         self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span);
-        if let Some(expr) = wth {
-            self.ibox(INDENT_UNIT);
-            if !fields.is_empty() {
-                self.word(",");
-                self.space();
+        match wth {
+            hir::StructTailExpr::Base(expr) => {
+                self.ibox(INDENT_UNIT);
+                if !fields.is_empty() {
+                    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("}");
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index b27f7215ae4..a93da52b270 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -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
     .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 ->
     [NONE] {""}
     [implement] , perhaps you need to implement it
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index a2e00859307..7746a5a7132 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -16,6 +16,47 @@ use rustc_span::{Span, Symbol};
 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)]
 #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
 pub(crate) struct FieldMultiplySpecifiedInInitializer {
     #[primary_span]
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 04c06169d33..0e079b03769 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -44,10 +44,11 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio
 use crate::TupleArgumentsFlag::DontTupleArguments;
 use crate::coercion::{CoerceMany, DynamicCoerceMany};
 use crate::errors::{
-    AddressOfTemporaryTaken, FieldMultiplySpecifiedInInitializer,
-    FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, ReturnLikeStatementKind,
-    ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo,
-    YieldExprOutsideOfCoroutine,
+    AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
+    BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
+    FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition,
+    ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
+    TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
 };
 use crate::{
     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);
                 let e =
                     self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
-                self.set_tainted_by_errors(e);
                 Ty::new_error(tcx, e)
             }
             Res::Def(DefKind::Variant, _) => {
@@ -1855,11 +1855,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn check_expr_struct(
         &self,
-        expr: &hir::Expr<'_>,
+        expr: &hir::Expr<'tcx>,
         expected: Expectation<'tcx>,
-        qpath: &QPath<'tcx>,
+        qpath: &'tcx QPath<'tcx>,
         fields: &'tcx [hir::ExprField<'tcx>],
-        base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
+        base_expr: &'tcx hir::StructTailExpr<'tcx>,
     ) -> Ty<'tcx> {
         // Find the relevant variant
         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,
         variant: &'tcx ty::VariantDef,
         hir_fields: &'tcx [hir::ExprField<'tcx>],
-        base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
+        base_expr: &'tcx hir::StructTailExpr<'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
         // when certain fields are assumed to exist that in fact do not.
         if error_happened {
-            if let Some(base_expr) = base_expr {
+            if let hir::StructTailExpr::Base(base_expr) = base_expr {
                 self.check_expr(base_expr);
             }
             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
             // consistency. But they should be merged as much as possible.
             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(
         &self,
         fields: &'tcx [hir::ExprField<'tcx>],
-        base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
+        base_expr: &'tcx hir::StructTailExpr<'tcx>,
     ) {
         for field in fields {
             self.check_expr(field.expr);
         }
-        if let Some(base) = *base_expr {
+        if let hir::StructTailExpr::Base(base) = *base_expr {
             self.check_expr(base);
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 774d00edea0..27ec2e9e0d4 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -686,7 +686,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     fn walk_struct_expr<'hir>(
         &self,
         fields: &[hir::ExprField<'_>],
-        opt_with: &Option<&'hir hir::Expr<'_>>,
+        opt_with: &hir::StructTailExpr<'hir>,
     ) -> Result<(), Cx::Error> {
         // Consume the expressions supplying values for each field.
         for field in fields {
@@ -702,8 +702,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         }
 
         let with_expr = match *opt_with {
-            Some(w) => &*w,
-            None => {
+            hir::StructTailExpr::Base(w) => &*w,
+            hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => {
                 return Ok(());
             }
         };
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 422629cd11d..f5d2ebc3e87 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -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
     .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
     .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary
 
diff --git a/compiler/rustc_lint/src/default_field_always_invalid.rs b/compiler/rustc_lint/src/default_field_always_invalid.rs
new file mode 100644
index 00000000000..46cffb53b4b
--- /dev/null
+++ b/compiler/rustc_lint/src/default_field_always_invalid.rs
@@ -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: () },
+                );
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index a99c94592b3..baf2703511e 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -41,6 +41,7 @@ mod async_fn_in_trait;
 pub mod builtin;
 mod context;
 mod dangling;
+mod default_field_always_invalid;
 mod deref_into_dyn_supertrait;
 mod drop_forget_useless;
 mod early;
@@ -85,6 +86,7 @@ use async_closures::AsyncClosureUsage;
 use async_fn_in_trait::AsyncFnInTrait;
 use builtin::*;
 use dangling::*;
+use default_field_always_invalid::*;
 use deref_into_dyn_supertrait::*;
 use drop_forget_useless::*;
 use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
@@ -193,6 +195,7 @@ late_lint_methods!(
             DropForgetUseless: DropForgetUseless,
             ImproperCTypesDeclarations: ImproperCTypesDeclarations,
             ImproperCTypesDefinitions: ImproperCTypesDefinitions,
+            DefaultFieldAlwaysInvalid: DefaultFieldAlwaysInvalid,
             InvalidFromUtf8: InvalidFromUtf8,
             VariantSizeDifferences: VariantSizeDifferences,
             PathStatements: PathStatements,
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 9fa263799eb..07f1c48bafb 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -730,6 +730,15 @@ pub(crate) struct UndroppedManuallyDropsSuggestion {
     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
 #[derive(LintDiagnostic)]
 pub(crate) enum InvalidFromUtf8Diag {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index f3f5af49412..4a4930fff9d 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1104,6 +1104,7 @@ impl<'a> CrateMetadataRef<'a> {
                         name: self.item_name(did.index),
                         vis: self.get_visibility(did.index),
                         safety: self.get_safety(did.index),
+                        value: None,
                     })
                     .collect(),
                 adt_kind,
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 9cf6bc1b777..86014c34b45 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> {
     pub user_ty: UserTy<'tcx>,
 
     pub fields: Box<[FieldExpr]>,
-    /// The base, e.g. `Foo {x: 1, .. base}`.
-    pub base: Option<FruInfo<'tcx>>,
+    /// The base, e.g. `Foo {x: 1, ..base}`.
+    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)]
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index 81202a6eaad..64bac12b266 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -2,6 +2,7 @@ use super::{
     AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
     PatKind, Stmt, StmtKind, Thir,
 };
+use crate::thir::AdtExprBase;
 
 pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
     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 {
                 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]);
             }
         }
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 447cbc8932e..9678863ee4d 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -259,10 +259,10 @@ impl Into<DataTypeKind> for AdtKind {
     }
 }
 
-impl AdtDefData {
+impl<'tcx> AdtDefData {
     /// Creates a new `AdtDefData`.
     pub(super) fn new(
-        tcx: TyCtxt<'_>,
+        tcx: TyCtxt<'tcx>,
         did: DefId,
         kind: AdtKind,
         variants: IndexVec<VariantIdx, VariantDef>,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c7a2223ecd7..70e0568b202 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1364,6 +1364,7 @@ pub struct FieldDef {
     pub name: Symbol,
     pub vis: Visibility<DefId>,
     pub safety: hir::Safety,
+    pub value: Option<DefId>,
 }
 
 impl PartialEq for FieldDef {
@@ -1376,9 +1377,9 @@ impl PartialEq for FieldDef {
         // of `FieldDef` changes, a compile-error will be produced, reminding
         // 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;
 
@@ -1405,7 +1406,7 @@ impl Hash for FieldDef {
         // of `FieldDef` changes, a compile-error will be produced, reminding
         // us to revisit this assumption.
 
-        let Self { did, name: _, vis: _, safety: _ } = &self;
+        let Self { did, name: _, vis: _, safety: _, value: _ } = &self;
 
         did.hash(s)
     }
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index c3e9bd302de..67114efdff5 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
                     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 active_field_index = is_union.then(|| fields[0].name);
 
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index bebb44faba6..b31f61a75ff 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -1,7 +1,5 @@
 //! See docs in build/expr/mod.rs
 
-use std::iter;
-
 use rustc_ast::{AsmMacro, InlineAsmOptions};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -344,25 +342,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     })
                     .collect();
 
-                let field_names = adt_def.variant(variant_index).fields.indices();
-
-                let fields = if let Some(FruInfo { base, field_types }) = base {
-                    let place_builder = unpack!(block = this.as_place_builder(block, *base));
+                let variant = adt_def.variant(variant_index);
+                let field_names = variant.fields.indices();
 
-                    // MIR does not natively support FRU, so for each
-                    // base-supplied field, generate an operand that
-                    // reads it from the base.
-                    iter::zip(field_names, &**field_types)
-                        .map(|(n, ty)| match fields_map.get(&n) {
-                            Some(v) => v.clone(),
-                            None => {
-                                let place = place_builder.clone_project(PlaceElem::Field(n, *ty));
-                                this.consume_by_copy_or_move(place.to_place(this))
-                            }
-                        })
-                        .collect()
-                } else {
-                    field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
+                let fields = match 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
+                        // base-supplied field, generate an operand that
+                        // reads it from the base.
+                        itertools::zip_eq(field_names, &**field_types)
+                            .map(|(n, ty)| match fields_map.get(&n) {
+                                Some(v) => v.clone(),
+                                None => {
+                                    let place =
+                                        place_builder.clone_project(PlaceElem::Field(n, *ty));
+                                    this.consume_by_copy_or_move(place.to_place(this))
+                                }
+                            })
+                            .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;
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index ee9bcce104e..d75f01dfba0 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -222,7 +222,7 @@ impl<'tcx> Cx<'tcx> {
                     args,
                     fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
                     user_ty: None,
-                    base: None,
+                    base: AdtExprBase::None,
                 }));
 
                 debug!(?kind);
@@ -464,7 +464,7 @@ impl<'tcx> Cx<'tcx> {
                             variant_index: index,
                             fields: field_refs,
                             user_ty,
-                            base: None,
+                            base: AdtExprBase::None,
                         }))
                     } else {
                         ExprKind::Call {
@@ -594,20 +594,36 @@ impl<'tcx> Cx<'tcx> {
                             args,
                             user_ty,
                             fields: self.field_refs(fields),
-                            base: base.map(|base| FruInfo {
-                                base: self.mirror_expr(base),
-                                field_types: self.typeck_results().fru_field_types()[expr.hir_id]
-                                    .iter()
-                                    .copied()
-                                    .collect(),
-                            }),
+                            base: match base {
+                                hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo {
+                                    base: self.mirror_expr(base),
+                                    field_types: self.typeck_results().fru_field_types()
+                                        [expr.hir_id]
+                                        .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 => {
                         let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
                         match res {
                             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 user_provided_types =
@@ -621,7 +637,21 @@ impl<'tcx> Cx<'tcx> {
                                     args,
                                     user_ty,
                                     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,
                         user_ty,
                         fields: Box::new([]),
-                        base: None,
+                        base: AdtExprBase::None,
                     })),
                     _ => bug!("unexpected ty: {:?}", ty),
                 }
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index 6be0ed5fb31..2bcdb67c58a 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -566,11 +566,17 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
             self.print_expr(field_expr.expr, depth_lvl + 2);
         }
 
-        if let Some(ref base) = adt_expr.base {
-            print_indented!(self, "base:", depth_lvl + 1);
-            self.print_fru_info(base, depth_lvl + 2);
-        } else {
-            print_indented!(self, "base: None", depth_lvl + 1);
+        match adt_expr.base {
+            AdtExprBase::Base(ref base) => {
+                print_indented!(self, "base:", depth_lvl + 1);
+                self.print_fru_info(base, depth_lvl + 2);
+            }
+            AdtExprBase::DefaultFields(_) => {
+                print_indented!(self, "base: {{ defaulted fields }}", depth_lvl + 1);
+            }
+            AdtExprBase::None => {
+                print_indented!(self, "base: None", depth_lvl + 1);
+            }
         }
     }
 
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index e5cd4622dae..d50bd18a1d7 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -169,9 +169,6 @@ parse_enum_struct_mutually_exclusive = `enum` and `struct` are mutually exclusiv
 parse_eq_field_init = expected `:`, found `=`
     .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 ->
     [true] byte
     *[false] character
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 14f2dd32e92..4c4e03cdfa3 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3067,14 +3067,6 @@ pub(crate) struct SingleColonStructType {
 }
 
 #[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)]
 #[diag(parse_macro_rules_missing_bang)]
 pub(crate) struct MacroRulesMissingBang {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 3a9e9b480ec..eeb83a85e59 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3533,7 +3533,7 @@ impl<'a> Parser<'a> {
                 let exp_span = self.prev_token.span;
                 // We permit `.. }` on the left-hand side of a destructuring assignment.
                 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;
                 }
                 match self.parse_expr() {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 475cd09147f..f12b4ca249d 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1845,6 +1845,7 @@ impl<'a> Parser<'a> {
                         ident: None,
                         id: DUMMY_NODE_ID,
                         ty,
+                        default: None,
                         attrs,
                         is_placeholder: false,
                     },
@@ -2024,12 +2025,15 @@ impl<'a> Parser<'a> {
         if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) {
             self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span });
         }
-        if self.token == token::Eq {
+        let default = if self.token == token::Eq {
             self.bump();
             let const_expr = self.parse_expr_anon_const()?;
             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 {
             span: lo.to(self.prev_token.span),
             ident: Some(name),
@@ -2037,6 +2041,7 @@ impl<'a> Parser<'a> {
             safety,
             id: DUMMY_NODE_ID,
             ty,
+            default,
             attrs,
             is_placeholder: false,
         })
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 9cd95a0b02d..09cbb648f9b 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -1007,7 +1007,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ),
 
             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
                     .iter()
                     .rev()
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index c845d60ac07..3057f13e3a7 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -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> {
@@ -966,25 +985,29 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
             let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
             let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
             let variant = adt.variant_of_res(res);
-            if let Some(base) = *base {
-                // If the expression uses FRU we need to make sure all the unmentioned fields
-                // are checked for privacy (RFC 736). Rather than computing the set of
-                // unmentioned fields, just check them all.
-                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 => (base.hir_id, base.span, base.span),
-                    };
-                    self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
+            match *base {
+                hir::StructTailExpr::Base(base) => {
+                    // If the expression uses FRU we need to make sure all the unmentioned fields
+                    // are checked for privacy (RFC 736). Rather than computing the set of
+                    // unmentioned fields, just check them all.
+                    self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span);
                 }
-            } else {
-                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);
+                hir::StructTailExpr::DefaultFields(span) => {
+                    self.check_expanded_fields(adt, variant, fields, expr.hir_id, span);
+                }
+                hir::StructTailExpr::None => {
+                    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,
+                        );
+                    }
                 }
             }
         }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index fc92dc8b4ed..f5e1a598864 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -13,7 +13,9 @@ use std::collections::hash_map::Entry;
 use std::mem::{replace, swap, take};
 
 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_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::codes::*;
@@ -749,8 +751,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
         self.resolve_block(block);
         self.parent_scope.macro_rules = old_macro_rules;
     }
-    fn visit_anon_const(&mut self, _constant: &'ast AnonConst) {
-        bug!("encountered anon const without a manual call to `resolve_anon_const`");
+    fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
+        bug!("encountered anon const without a manual call to `resolve_anon_const` {constant:#?}");
     }
     fn visit_expr(&mut self, expr: &'ast Expr) {
         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) {
         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));
+        }
     }
 }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 818d4afffc6..d30b17c9cd8 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -728,6 +728,7 @@ symbols! {
         declare_lint_pass,
         decode,
         default_alloc_error_handler,
+        default_field_values,
         default_fn,
         default_lib_allocator,
         default_method_body_is_const,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index de775b64795..ffdd946aadb 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -5,7 +5,7 @@ use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 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_middle::ty;
 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 }`
 fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
     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
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index ef6b141920d..6819ad547f8 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
 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_middle::lint::in_external_macro;
 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.
-                    if let Some(base) = base {
+                    if let StructTailExpr::Base(base) = base {
                         self.ty_bounds.push(ExplicitTyBound(false));
                         self.visit_expr(base);
                         self.ty_bounds.pop();
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index d386bfca6ba..4fcd2abb769 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -3,7 +3,7 @@ use clippy_utils::fulfill_or_allowed;
 use clippy_utils::source::snippet;
 use rustc_data_structures::fx::FxHashMap;
 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_session::declare_lint_pass;
 use rustc_span::symbol::Symbol;
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
             }
             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, ".."))
             } else {
                 String::new()
diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
index 7f183bb601e..7a14bbfb9e8 100644
--- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::SyntaxContext;
@@ -43,7 +43,7 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
 
 impl<'tcx> LateLintPass<'tcx> for NumberedFields {
     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.
             && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
             // Type aliases can't be used as functions.
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 1c55e3e22e8..ed9879de13b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -5,7 +5,7 @@ use clippy_utils::higher::ForLoop;
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::snippet;
 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_span::{Span, sym};
 use std::iter::once;
@@ -164,7 +164,7 @@ fn never_loop_expr<'tcx>(
         },
         ExprKind::Struct(_, fields, base) => {
             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))
             } else {
                 fields
diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs
index 6a2893cefbd..0cba72bd2c6 100644
--- a/src/tools/clippy/clippy_lints/src/needless_update.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_update.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
@@ -51,7 +51,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
 
 impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
     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);
             if let ty::Adt(def, _) = ty.kind() {
                 if fields.len() == def.non_enum_variant().fields.len()
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 8ecff9c3f9b..9e44bb02c56 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -8,7 +8,7 @@ use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     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_lint::{LateContext, LateLintPass, LintContext};
@@ -238,7 +238,10 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         ExprKind::Struct(_, fields, ref base) => {
             !has_drop(cx, cx.typeck_results().expr_ty(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) => {
             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)) {
                 None
             } 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())
             }
         },
diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
index 44e585953bf..bb11daecc07 100644
--- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use rustc_ast::{LitIntType, LitKind, UintTy};
 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_session::declare_lint_pass;
 use std::fmt::{self, Display, Formatter};
@@ -86,7 +86,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
             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;
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index afdd3505cdd..0a90d31db7e 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_copy;
 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_session::declare_lint_pass;
 
@@ -59,15 +59,15 @@ impl LateLintPass<'_> for UnnecessaryStruct {
         let field_path = same_path_in_all_fields(cx, expr, fields);
 
         let sugg = match (field_path, base) {
-            (Some(&path), None) => {
+            (Some(&path), StructTailExpr::None | StructTailExpr::DefaultFields(_)) => {
                 // all fields match, no base given
                 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
                 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
                 base.span
             },
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 51001d374b4..311ed427cb9 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{
     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_session::declare_lint_pass;
@@ -598,7 +598,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             },
             ExprKind::Struct(qpath, fields, base) => {
                 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})");
                 self.qpath(qpath);
                 self.slice(fields, |field| {
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 11bbe734844..d216879cbd2 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -8,7 +8,7 @@ use crate::ty::is_type_diagnostic_item;
 
 use rustc_ast::ast;
 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_span::{Span, sym, symbol};
 
@@ -236,7 +236,7 @@ impl<'a> Range<'a> {
                     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 {
                     start: None,
                     end: None,
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 7f3e331e7f6..4be4340862d 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -10,7 +10,7 @@ use rustc_hir::{
     AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
     ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
     LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
-    TyKind,
+    TyKind, StructTailExpr,
 };
 use rustc_lexer::{TokenKind, tokenize};
 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::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
                 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))
             },
             (&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);
                 }
 
-                if let Some(e) = *expr {
+                if let StructTailExpr::Base(e) = *expr {
                     self.hash_expr(e);
                 }
             },
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index a79be5ca7d4..351e619d7b1 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
 use rustc_hir::{
     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_middle::hir::nested_filter;
@@ -663,7 +663,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
                 for field in fields {
                     helper(typeck, true, field.expr, f)?;
                 }
-                if let Some(default) = default {
+                if let StructTailExpr::Base(default) = default {
                     helper(typeck, false, default, f)?;
                 }
             },
diff --git a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr
index 4c4f0663eeb..58f8e97dea0 100644
--- a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr
+++ b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr
@@ -12,17 +12,6 @@ error: functional record updates are not allowed in destructuring assignments
 LL |     Struct { a, ..d } = Struct { a: 1, b: 2 };
    |                   ^ 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`
   --> $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 };
    |               ~~~~~~
 
+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
 
 Some errors have detailed explanations: E0026, E0027, E0797.
diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.rs b/tests/ui/feature-gates/feature-gate-default-field-values.rs
new file mode 100644
index 00000000000..01441de67e0
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-default-field-values.rs
@@ -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 `..`
+}
diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.stderr b/tests/ui/feature-gates/feature-gate-default-field-values.stderr
new file mode 100644
index 00000000000..a24217c0727
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-default-field-values.stderr
@@ -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`.
diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.fixed b/tests/ui/parser/struct-default-values-and-missing-field-separator.fixed
deleted file mode 100644
index be6ed053c6e..00000000000
--- a/tests/ui/parser/struct-default-values-and-missing-field-separator.fixed
+++ /dev/null
@@ -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() {}
diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.rs b/tests/ui/parser/struct-default-values-and-missing-field-separator.rs
index 7900d397a5d..8ecf042ad38 100644
--- a/tests/ui/parser/struct-default-values-and-missing-field-separator.rs
+++ b/tests/ui/parser/struct-default-values-and-missing-field-separator.rs
@@ -1,4 +1,3 @@
-//@ run-rustfix
 #![allow(dead_code)]
 
 enum E {
diff --git a/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr b/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr
index 1fb57ab11f9..669147c5685 100644
--- a/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr
+++ b/tests/ui/parser/struct-default-values-and-missing-field-separator.stderr
@@ -1,152 +1,133 @@
-error: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:9:16
+error: expected `,`, or `}`, found `field2`
+  --> $DIR/struct-default-values-and-missing-field-separator.rs:17:16
    |
-LL |     field1: i32 = 42,
-   |                ^^^^^
+LL |     field1: i32
+   |                ^ help: try adding a comma: `,`
+
+error: expected `,`, or `}`, found `field3`
+  --> $DIR/struct-default-values-and-missing-field-separator.rs:18:14
+   |
+LL |     field2: E
+   |              ^ help: try adding a comma: `,`
+
+error: expected `:`, found `=`
+  --> $DIR/struct-default-values-and-missing-field-separator.rs:26:12
+   |
+LL |     field1 = i32,
+   |            ^
+   |            |
+   |            expected `:`
+   |            help: field names and their types are separated with `:`
+
+error: expected `:`, found `;`
+  --> $DIR/struct-default-values-and-missing-field-separator.rs:27:11
    |
-help: remove this unsupported default value
+LL |     field2; E,
+   |           ^
+   |           |
+   |           expected `:`
+   |           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,
-LL +     field1: i32,
+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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:10:14
+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,
    |              ^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field2: E = E::A,
-LL +     field2: E,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:11:16
+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,
    |                ^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field3: i32 = 1 + 2,
-LL +     field3: i32,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:12:16
+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 },
    |                ^^^^^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field4: i32 = { 1 + 2 },
-LL +     field4: i32,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:13:14
+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),
    |              ^^^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field5: E = foo(42),
-LL +     field5: E,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:14:14
+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) },
    |              ^^^^^^^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field6: E = { foo(42) },
-LL +     field6: E,
-   |
-
-error: expected `,`, or `}`, found `field2`
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:18:16
-   |
-LL |     field1: i32
-   |                ^ help: try adding a comma: `,`
-
-error: expected `,`, or `}`, found `field3`
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:19:14
-   |
-LL |     field2: E
-   |              ^ help: try adding a comma: `,`
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:20:16
+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,
    |                ^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field3: i32 = 1 + 2,
-LL +     field3: i32,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:21:16
+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 },
    |                ^^^^^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field4: i32 = { 1 + 2 },
-LL +     field4: i32,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:22:14
+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),
    |              ^^^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field5: E = foo(42),
-LL +     field5: E,
-   |
+   = 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: default values on `struct` fields aren't supported
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:23:14
+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) },
    |              ^^^^^^^^^^^^^^
    |
-help: remove this unsupported default value
-   |
-LL -     field6: E = { foo(42) },
-LL +     field6: E,
-   |
-
-error: expected `:`, found `=`
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:27:12
-   |
-LL |     field1 = i32,
-   |            ^
-   |            |
-   |            expected `:`
-   |            help: field names and their types are separated with `:`
-
-error: expected `:`, found `;`
-  --> $DIR/struct-default-values-and-missing-field-separator.rs:28:11
-   |
-LL |     field2; E,
-   |           ^
-   |           |
-   |           expected `:`
-   |           help: field names and their types are separated with `:`
+   = 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
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr
index 2adbcfab612..e3bc68a2134 100644
--- a/tests/ui/stats/input-stats.stderr
+++ b/tests/ui/stats/input-stats.stderr
@@ -20,8 +20,8 @@ ast-stats-1 Stmt                     160 ( 2.4%)             5            32
 ast-stats-1 - Let                       32 ( 0.5%)             1
 ast-stats-1 - MacCall                   32 ( 0.5%)             1
 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 FieldDef                 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 - 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 - Trait                    352 ( 5.3%)             4
 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 - Wild                      72 ( 1.1%)             1
 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 - Lit                      144 ( 2.2%)             2
 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 - Ref                       64 ( 1.0%)             1
 ast-stats-1 - Ptr                       64 ( 1.0%)             1
 ast-stats-1 - ImplicitSelf             128 ( 1.9%)             2
 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 - Trait                    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 - Use                      408 ( 6.1%)             3
 ast-stats-1 ----------------------------------------------------------------
-ast-stats-1 Total                  6_664                   116
+ast-stats-1 Total                  6_696                   116
 ast-stats-1
 ast-stats-2 POST EXPANSION AST STATS
 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 FnDecl                   120 ( 1.6%)             5            24
 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 - Normal                    96 ( 1.3%)             3
 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 - Semi                      32 ( 0.4%)             1
 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 FieldDef                 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 - Type                     176 ( 2.4%)             2
 ast-stats-2 - Fn                       176 ( 2.4%)             2
 ast-stats-2 GenericBound             352 ( 4.8%)             4            88
 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 - Struct                    72 ( 1.0%)             1
 ast-stats-2 - Wild                      72 ( 1.0%)             1
 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 - Match                     72 ( 1.0%)             1
 ast-stats-2 - Struct                    72 ( 1.0%)             1
 ast-stats-2 - InlineAsm                 72 ( 1.0%)             1
 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 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 - Ptr                       64 ( 0.9%)             1
-ast-stats-2 - ImplicitSelf             128 ( 1.8%)             2
-ast-stats-2 - Path                     640 ( 8.8%)            10
-ast-stats-2 Item                   1_496 (20.5%)            11           136
+ast-stats-2 - ImplicitSelf             128 ( 1.7%)             2
+ast-stats-2 - Path                     640 ( 8.7%)            10
+ast-stats-2 Item                   1_496 (20.4%)            11           136
 ast-stats-2 - Enum                     136 ( 1.9%)             1
 ast-stats-2 - Trait                    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 - Use                      544 ( 7.4%)             4
 ast-stats-2 ----------------------------------------------------------------
-ast-stats-2 Total                  7_312                   127
+ast-stats-2 Total                  7_344                   127
 ast-stats-2
 hir-stats HIR STATS
 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 - Semi                      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 Attribute                128 ( 1.4%)             4            32
+hir-stats FieldDef                 128 ( 1.4%)             2            64
 hir-stats GenericArgs              144 ( 1.6%)             3            48
 hir-stats Variant                  144 ( 1.6%)             2            72
 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 - Lit                      128 ( 1.4%)             2
 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 - Trait                     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 PathSegment            1_920 (21.5%)            40            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  8_920                   180
+hir-stats Total                  8_936                   180
 hir-stats
diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs
new file mode 100644
index 00000000000..c4de4f5cdad
--- /dev/null
+++ b/tests/ui/structs/default-field-values-failures.rs
@@ -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> { .. };
+}
diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr
new file mode 100644
index 00000000000..bd96777f865
--- /dev/null
+++ b/tests/ui/structs/default-field-values-failures.stderr
@@ -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`.
diff --git a/tests/ui/structs/default-field-values-invalid-const.rs b/tests/ui/structs/default-field-values-invalid-const.rs
new file mode 100644
index 00000000000..6838238064b
--- /dev/null
+++ b/tests/ui/structs/default-field-values-invalid-const.rs
@@ -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() {}
diff --git a/tests/ui/structs/default-field-values-invalid-const.stderr b/tests/ui/structs/default-field-values-invalid-const.stderr
new file mode 100644
index 00000000000..a0d84271f4b
--- /dev/null
+++ b/tests/ui/structs/default-field-values-invalid-const.stderr
@@ -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`.
diff --git a/tests/ui/structs/default-field-values-support.rs b/tests/ui/structs/default-field-values-support.rs
new file mode 100644
index 00000000000..bdf21e1272d
--- /dev/null
+++ b/tests/ui/structs/default-field-values-support.rs
@@ -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());
+}
diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout
index d56f15fb221..916f296ccfc 100644
--- a/tests/ui/thir-print/thir-tree-match.stdout
+++ b/tests/ui/thir-print/thir-tree-match.stdout
@@ -92,7 +92,7 @@ body:
                                                                                         adt_def: 
                                                                                             AdtDef {
                                                                                                 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
                                                                                                 repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
                                                                                         args: []
@@ -154,7 +154,7 @@ body:
                                                                                         adt_def: 
                                                                                             AdtDef {
                                                                                                 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
                                                                                                 repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
                                                                                         args: []
@@ -206,7 +206,7 @@ body:
                                                                                         adt_def: 
                                                                                             AdtDef {
                                                                                                 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
                                                                                                 repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 }
                                                                                         args: []