Skip to content

Commit 27f079a

Browse files
committed
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 ```
1 parent bcd0683 commit 27f079a

File tree

3 files changed

+72
-8
lines changed

3 files changed

+72
-8
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+25-8
Original file line numberDiff line numberDiff line change
@@ -1991,18 +1991,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19911991
adt_ty: Ty<'tcx>,
19921992
expected: Expectation<'tcx>,
19931993
expr: &hir::Expr<'_>,
1994-
span: Span,
1994+
path_span: Span,
19951995
variant: &'tcx ty::VariantDef,
19961996
hir_fields: &'tcx [hir::ExprField<'tcx>],
19971997
base_expr: &'tcx hir::StructTailExpr<'tcx>,
19981998
) {
19991999
let tcx = self.tcx;
20002000

2001-
let adt_ty = self.try_structurally_resolve_type(span, adt_ty);
2001+
let adt_ty = self.try_structurally_resolve_type(path_span, adt_ty);
20022002
let adt_ty_hint = expected.only_has_type(self).and_then(|expected| {
20032003
self.fudge_inference_if_ok(|| {
20042004
let ocx = ObligationCtxt::new(self);
2005-
ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?;
2005+
ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?;
20062006
if !ocx.select_where_possible().is_empty() {
20072007
return Err(TypeError::Mismatch);
20082008
}
@@ -2012,11 +2012,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20122012
});
20132013
if let Some(adt_ty_hint) = adt_ty_hint {
20142014
// re-link the variables that the fudging above can create.
2015-
self.demand_eqtype(span, adt_ty_hint, adt_ty);
2015+
self.demand_eqtype(path_span, adt_ty_hint, adt_ty);
20162016
}
20172017

20182018
let ty::Adt(adt, args) = adt_ty.kind() else {
2019-
span_bug!(span, "non-ADT passed to check_expr_struct_fields");
2019+
span_bug!(path_span, "non-ADT passed to check_expr_struct_fields");
20202020
};
20212021
let adt_kind = adt.adt_kind();
20222022

@@ -2107,7 +2107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21072107
if adt_kind == AdtKind::Union && hir_fields.len() != 1 {
21082108
struct_span_code_err!(
21092109
self.dcx(),
2110-
span,
2110+
path_span,
21112111
E0784,
21122112
"union expressions should have exactly one field",
21132113
)
@@ -2167,6 +2167,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21672167
});
21682168
return;
21692169
}
2170+
if variant.fields.is_empty() {
2171+
let mut err = self.dcx().struct_span_err(
2172+
span,
2173+
format!(
2174+
"`{adt_ty}` has no fields, `..` needs at least one default field in the \
2175+
struct definition",
2176+
),
2177+
);
2178+
err.span_label(path_span, "this type has no fields");
2179+
err.emit();
2180+
}
21702181
if !missing_mandatory_fields.is_empty() {
21712182
let s = pluralize!(missing_mandatory_fields.len());
21722183
let fields: Vec<_> =
@@ -2316,11 +2327,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23162327
.collect();
23172328

23182329
if !private_fields.is_empty() {
2319-
self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields);
2330+
self.report_private_fields(
2331+
adt_ty,
2332+
path_span,
2333+
expr.span,
2334+
private_fields,
2335+
hir_fields,
2336+
);
23202337
} else {
23212338
self.report_missing_fields(
23222339
adt_ty,
2323-
span,
2340+
path_span,
23242341
remaining_fields,
23252342
variant,
23262343
hir_fields,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![feature(default_field_values)]
2+
3+
// If an API wants users to always use `..` even if they specify all the fields, they should use a
4+
// sentinel field. As of now, that field can't be made private so it is only constructable with this
5+
// syntax, but this might change in the future.
6+
7+
struct A {}
8+
struct B();
9+
struct C;
10+
struct D {
11+
x: i32,
12+
}
13+
struct E(i32);
14+
15+
fn main() {
16+
let _ = A { .. }; //~ ERROR has no fields
17+
let _ = B { .. }; //~ ERROR has no fields
18+
let _ = C { .. }; //~ ERROR has no fields
19+
let _ = D { x: 0, .. };
20+
let _ = E { 0: 0, .. };
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: `A` has no fields, `..` needs at least one default field in the struct definition
2+
--> $DIR/empty-struct.rs:16:17
3+
|
4+
LL | let _ = A { .. };
5+
| - ^^
6+
| |
7+
| this type has no fields
8+
9+
error: `B` has no fields, `..` needs at least one default field in the struct definition
10+
--> $DIR/empty-struct.rs:17:17
11+
|
12+
LL | let _ = B { .. };
13+
| - ^^
14+
| |
15+
| this type has no fields
16+
17+
error: `C` has no fields, `..` needs at least one default field in the struct definition
18+
--> $DIR/empty-struct.rs:18:17
19+
|
20+
LL | let _ = C { .. };
21+
| - ^^
22+
| |
23+
| this type has no fields
24+
25+
error: aborting due to 3 previous errors
26+

0 commit comments

Comments
 (0)