From 27f079ae24175a60edae59afdc34099d80e898c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 18 Jan 2025 21:04:52 +0000 Subject: [PATCH] Disallow `A { .. }` if `A` has no fields ``` error: `A` has no fields, `..` needs at least one default field in the struct definition --> $DIR/empty-struct.rs:16:17 | LL | let _ = A { .. }; | - ^^ | | | this type has no fields ``` --- compiler/rustc_hir_typeck/src/expr.rs | 33 ++++++++++++++----- .../default-field-values/empty-struct.rs | 21 ++++++++++++ .../default-field-values/empty-struct.stderr | 26 +++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 tests/ui/structs/default-field-values/empty-struct.rs create mode 100644 tests/ui/structs/default-field-values/empty-struct.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 3bb518e7f9711..dc6a2adf6220c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1991,18 +1991,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adt_ty: Ty<'tcx>, expected: Expectation<'tcx>, expr: &hir::Expr<'_>, - span: Span, + path_span: Span, variant: &'tcx ty::VariantDef, hir_fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx hir::StructTailExpr<'tcx>, ) { let tcx = self.tcx; - let adt_ty = self.try_structurally_resolve_type(span, adt_ty); + let adt_ty = self.try_structurally_resolve_type(path_span, adt_ty); let adt_ty_hint = expected.only_has_type(self).and_then(|expected| { self.fudge_inference_if_ok(|| { let ocx = ObligationCtxt::new(self); - ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?; + ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?; if !ocx.select_where_possible().is_empty() { return Err(TypeError::Mismatch); } @@ -2012,11 +2012,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if let Some(adt_ty_hint) = adt_ty_hint { // re-link the variables that the fudging above can create. - self.demand_eqtype(span, adt_ty_hint, adt_ty); + self.demand_eqtype(path_span, adt_ty_hint, adt_ty); } let ty::Adt(adt, args) = adt_ty.kind() else { - span_bug!(span, "non-ADT passed to check_expr_struct_fields"); + span_bug!(path_span, "non-ADT passed to check_expr_struct_fields"); }; let adt_kind = adt.adt_kind(); @@ -2107,7 +2107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if adt_kind == AdtKind::Union && hir_fields.len() != 1 { struct_span_code_err!( self.dcx(), - span, + path_span, E0784, "union expressions should have exactly one field", ) @@ -2167,6 +2167,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); return; } + if variant.fields.is_empty() { + let mut err = self.dcx().struct_span_err( + span, + format!( + "`{adt_ty}` has no fields, `..` needs at least one default field in the \ + struct definition", + ), + ); + err.span_label(path_span, "this type has no fields"); + err.emit(); + } if !missing_mandatory_fields.is_empty() { let s = pluralize!(missing_mandatory_fields.len()); let fields: Vec<_> = @@ -2316,11 +2327,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields); + self.report_private_fields( + adt_ty, + path_span, + expr.span, + private_fields, + hir_fields, + ); } else { self.report_missing_fields( adt_ty, - span, + path_span, remaining_fields, variant, hir_fields, diff --git a/tests/ui/structs/default-field-values/empty-struct.rs b/tests/ui/structs/default-field-values/empty-struct.rs new file mode 100644 index 0000000000000..c9cb861ae27cb --- /dev/null +++ b/tests/ui/structs/default-field-values/empty-struct.rs @@ -0,0 +1,21 @@ +#![feature(default_field_values)] + +// If an API wants users to always use `..` even if they specify all the fields, they should use a +// sentinel field. As of now, that field can't be made private so it is only constructable with this +// syntax, but this might change in the future. + +struct A {} +struct B(); +struct C; +struct D { + x: i32, +} +struct E(i32); + +fn main() { + let _ = A { .. }; //~ ERROR has no fields + let _ = B { .. }; //~ ERROR has no fields + let _ = C { .. }; //~ ERROR has no fields + let _ = D { x: 0, .. }; + let _ = E { 0: 0, .. }; +} diff --git a/tests/ui/structs/default-field-values/empty-struct.stderr b/tests/ui/structs/default-field-values/empty-struct.stderr new file mode 100644 index 0000000000000..079e83415b4b8 --- /dev/null +++ b/tests/ui/structs/default-field-values/empty-struct.stderr @@ -0,0 +1,26 @@ +error: `A` has no fields, `..` needs at least one default field in the struct definition + --> $DIR/empty-struct.rs:16:17 + | +LL | let _ = A { .. }; + | - ^^ + | | + | this type has no fields + +error: `B` has no fields, `..` needs at least one default field in the struct definition + --> $DIR/empty-struct.rs:17:17 + | +LL | let _ = B { .. }; + | - ^^ + | | + | this type has no fields + +error: `C` has no fields, `..` needs at least one default field in the struct definition + --> $DIR/empty-struct.rs:18:17 + | +LL | let _ = C { .. }; + | - ^^ + | | + | this type has no fields + +error: aborting due to 3 previous errors +