Skip to content

Commit 993e7e2

Browse files
committed
fix is_non_exhaustive confusion between structs and enums
Structs and enums can both be non-exhaustive, with a very different meaning. This PR splits `is_non_exhaustive` to 2 separate functions - 1 for structs, and another for enums, and fixes the places that got the usage confused. Fixes #53549.
1 parent caed80b commit 993e7e2

File tree

7 files changed

+87
-24
lines changed

7 files changed

+87
-24
lines changed

src/librustc/ty/mod.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -1981,10 +1981,30 @@ impl<'a, 'gcx, 'tcx> AdtDef {
19811981
}
19821982

19831983
#[inline]
1984-
pub fn is_non_exhaustive(&self) -> bool {
1984+
fn is_non_exhaustive(&self) -> bool {
19851985
self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE)
19861986
}
19871987

1988+
#[inline]
1989+
pub fn is_enum_non_exhaustive(&self) -> bool {
1990+
match self.adt_kind() {
1991+
AdtKind::Enum => self.is_non_exhaustive(),
1992+
AdtKind::Struct | AdtKind::Union => {
1993+
bug!("is_non_exhaustive_enum called on non-enum `{:?}`", self);
1994+
}
1995+
}
1996+
}
1997+
1998+
#[inline]
1999+
pub fn is_univariant_non_exhaustive(&self) -> bool {
2000+
match self.adt_kind() {
2001+
AdtKind::Struct | AdtKind::Union => self.is_non_exhaustive(),
2002+
AdtKind::Enum => {
2003+
bug!("is_non_exhaustive_enum called on non-enum `{:?}`", self);
2004+
}
2005+
}
2006+
}
2007+
19882008
/// Returns the kind of the ADT - Struct or Enum.
19892009
#[inline]
19902010
pub fn adt_kind(&self) -> AdtKind {
@@ -1997,6 +2017,19 @@ impl<'a, 'gcx, 'tcx> AdtDef {
19972017
}
19982018
}
19992019

2020+
/// Return whether `variant` is non-exhaustive as a *struct* (i.e., whether
2021+
/// it can have additional fields).
2022+
pub fn is_variant_non_exhaustive(&self, _variant: &ty::VariantDef) -> bool {
2023+
match self.adt_kind() {
2024+
// A struct is non-exhaustive if it has a `#[non_exhaustive]` attribute.
2025+
AdtKind::Struct => self.is_non_exhaustive(),
2026+
// At this moment, all enum variants are exhaustive.
2027+
AdtKind::Enum => false,
2028+
// All unions are "exhaustive", as far as that makes sense.
2029+
AdtKind::Union => false,
2030+
}
2031+
}
2032+
20002033
pub fn descr(&self) -> &'static str {
20012034
match self.adt_kind() {
20022035
AdtKind::Struct => "struct",

src/librustc_metadata/encoder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
740740

741741
// If the structure is marked as non_exhaustive then lower the visibility
742742
// to within the crate.
743-
if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
743+
if adt_def.is_univariant_non_exhaustive() && ctor_vis == ty::Visibility::Public {
744744
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
745745
}
746746

src/librustc_mir/hair/pattern/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
381381

382382
fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
383383
match ty.sty {
384-
ty::Adt(adt_def, ..) => adt_def.is_enum() && adt_def.is_non_exhaustive(),
384+
ty::Adt(adt_def, ..) => adt_def.is_enum() && adt_def.is_enum_non_exhaustive(),
385385
_ => false,
386386
}
387387
}

src/librustc_privacy/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,9 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
684684
// visibility to within the crate.
685685
let struct_def_id = self.tcx.hir.get_parent_did(node_id);
686686
let adt_def = self.tcx.adt_def(struct_def_id);
687-
if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
687+
if adt_def.is_univariant_non_exhaustive()
688+
&& ctor_vis == ty::Visibility::Public
689+
{
688690
ctor_vis = ty::Visibility::Restricted(
689691
DefId::local(CRATE_DEF_INDEX));
690692
}

src/librustc_typeck/check/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
948948
}
949949

950950
// Require `..` if struct has non_exhaustive attribute.
951-
if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc {
951+
if adt.is_variant_non_exhaustive(variant) && !adt.did.is_local() && !etc {
952952
span_err!(tcx.sess, span, E0638,
953953
"`..` required with {} marked as non-exhaustive",
954954
kind_name);

src/librustc_typeck/check/mod.rs

+18-19
Original file line numberDiff line numberDiff line change
@@ -3465,7 +3465,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
34653465
// re-link the regions that EIfEO can erase.
34663466
self.demand_eqtype(span, adt_ty_hint, adt_ty);
34673467

3468-
let (substs, adt_kind, kind_name) = match &adt_ty.sty{
3468+
let (substs, adt_kind, kind_name) = match &adt_ty.sty {
34693469
&ty::Adt(adt, substs) => {
34703470
(substs, adt.adt_kind(), adt.variant_descr())
34713471
}
@@ -3639,37 +3639,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36393639
base_expr: &'gcx Option<P<hir::Expr>>) -> Ty<'tcx>
36403640
{
36413641
// Find the relevant variant
3642-
let (variant, struct_ty) =
3643-
if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) {
3644-
variant_ty
3645-
} else {
3646-
self.check_struct_fields_on_error(fields, base_expr);
3647-
return self.tcx.types.err;
3648-
};
3642+
let (variant, adt_ty) =
3643+
if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) {
3644+
variant_ty
3645+
} else {
3646+
self.check_struct_fields_on_error(fields, base_expr);
3647+
return self.tcx.types.err;
3648+
};
36493649

36503650
let path_span = match *qpath {
36513651
hir::QPath::Resolved(_, ref path) => path.span,
36523652
hir::QPath::TypeRelative(ref qself, _) => qself.span
36533653
};
36543654

36553655
// Prohibit struct expressions when non exhaustive flag is set.
3656-
if let ty::Adt(adt, _) = struct_ty.sty {
3657-
if !adt.did.is_local() && adt.is_non_exhaustive() {
3658-
span_err!(self.tcx.sess, expr.span, E0639,
3659-
"cannot create non-exhaustive {} using struct expression",
3660-
adt.variant_descr());
3661-
}
3656+
let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
3657+
if !adt.did.is_local() && adt.is_variant_non_exhaustive(variant) {
3658+
span_err!(self.tcx.sess, expr.span, E0639,
3659+
"cannot create non-exhaustive {} using struct expression",
3660+
adt.variant_descr());
36623661
}
36633662

3664-
let error_happened = self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span,
3663+
let error_happened = self.check_expr_struct_fields(adt_ty, expected, expr.id, path_span,
36653664
variant, fields, base_expr.is_none());
36663665
if let &Some(ref base_expr) = base_expr {
36673666
// If check_expr_struct_fields hit an error, do not attempt to populate
36683667
// the fields with the base_expr. This could cause us to hit errors later
36693668
// when certain fields are assumed to exist that in fact do not.
36703669
if !error_happened {
3671-
self.check_expr_has_type_or_error(base_expr, struct_ty);
3672-
match struct_ty.sty {
3670+
self.check_expr_has_type_or_error(base_expr, adt_ty);
3671+
match adt_ty.sty {
36733672
ty::Adt(adt, substs) if adt.is_struct() => {
36743673
let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {
36753674
self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs))
@@ -3687,8 +3686,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36873686
}
36883687
}
36893688
}
3690-
self.require_type_is_sized(struct_ty, expr.span, traits::StructInitializerSized);
3691-
struct_ty
3689+
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
3690+
adt_ty
36923691
}
36933692

36943693

src/test/run-pass/rfc-2008-non-exhaustive/enums.rs

+29
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,33 @@ fn main() {
3030
match enum_unit {
3131
_ => "no error with only wildcard"
3232
};
33+
34+
35+
// issue #53549 - check that variant constructors can still be called normally.
36+
37+
match NonExhaustiveEnum::Unit {
38+
NonExhaustiveEnum::Unit => {},
39+
_ => {}
40+
};
41+
42+
match NonExhaustiveEnum::Tuple(2) {
43+
NonExhaustiveEnum::Tuple(2) => {},
44+
_ => {}
45+
};
46+
47+
match (NonExhaustiveEnum::Unit {}) {
48+
NonExhaustiveEnum::Unit {} => {},
49+
_ => {}
50+
};
51+
52+
match (NonExhaustiveEnum::Tuple { 0: 2 }) {
53+
NonExhaustiveEnum::Tuple { 0: 2 } => {},
54+
_ => {}
55+
};
56+
57+
match (NonExhaustiveEnum::Struct { field: 2 }) {
58+
NonExhaustiveEnum::Struct { field: 2 } => {},
59+
_ => {}
60+
};
61+
3362
}

0 commit comments

Comments
 (0)