Skip to content

Commit 20ca025

Browse files
committed
Auto merge of #53721 - arielb1:exhaustively-unpun, r=nikomatsakis
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. r? @eddyb
2 parents 27e5457 + ae2ad30 commit 20ca025

File tree

10 files changed

+156
-58
lines changed

10 files changed

+156
-58
lines changed

src/librustc/ich/impls_ty.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,13 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::AdtFlags {
344344
}
345345
}
346346

347-
impl_stable_hash_for!(struct ty::VariantDef {
348-
did,
349-
name,
350-
discr,
351-
fields,
352-
ctor_kind
353-
});
347+
impl<'a> HashStable<StableHashingContext<'a>> for ty::VariantFlags {
348+
fn hash_stable<W: StableHasherResult>(&self,
349+
_: &mut StableHashingContext<'a>,
350+
hasher: &mut StableHasher<W>) {
351+
std_hash::Hash::hash(self, hasher);
352+
}
353+
}
354354

355355
impl_stable_hash_for!(enum ty::VariantDiscr {
356356
Explicit(def_id),

src/librustc/ty/mod.rs

+66-11
Original file line numberDiff line numberDiff line change
@@ -1692,12 +1692,17 @@ bitflags! {
16921692
const IS_FUNDAMENTAL = 1 << 2;
16931693
const IS_UNION = 1 << 3;
16941694
const IS_BOX = 1 << 4;
1695-
/// Indicates whether this abstract data type will be expanded on in future (new
1696-
/// fields/variants) and as such, whether downstream crates must match exhaustively on the
1697-
/// fields/variants of this data type.
1698-
///
1699-
/// See RFC 2008 (<https://github.com/rust-lang/rfcs/pull/2008>).
1700-
const IS_NON_EXHAUSTIVE = 1 << 5;
1695+
/// Indicates whether the variant list of this ADT is `#[non_exhaustive]`.
1696+
/// (i.e., this flag is never set unless this ADT is an enum).
1697+
const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 5;
1698+
}
1699+
}
1700+
1701+
bitflags! {
1702+
pub struct VariantFlags: u32 {
1703+
const NO_VARIANT_FLAGS = 0;
1704+
/// Indicates whether the field list of this variant is `#[non_exhaustive]`.
1705+
const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0;
17011706
}
17021707
}
17031708

@@ -1710,8 +1715,56 @@ pub struct VariantDef {
17101715
pub discr: VariantDiscr,
17111716
pub fields: Vec<FieldDef>,
17121717
pub ctor_kind: CtorKind,
1718+
flags: VariantFlags,
17131719
}
17141720

1721+
impl<'a, 'gcx, 'tcx> VariantDef {
1722+
/// Create a new `VariantDef`.
1723+
///
1724+
/// - `did` is the DefId used for the variant - for tuple-structs, it is the constructor DefId,
1725+
/// and for everything else, it is the variant DefId.
1726+
/// - `attribute_def_id` is the DefId that has the variant's attributes.
1727+
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
1728+
did: DefId,
1729+
name: Name,
1730+
discr: VariantDiscr,
1731+
fields: Vec<FieldDef>,
1732+
adt_kind: AdtKind,
1733+
ctor_kind: CtorKind)
1734+
-> Self
1735+
{
1736+
debug!("VariantDef::new({:?}, {:?}, {:?}, {:?}, {:?}, {:?})", did, name, discr, fields,
1737+
adt_kind, ctor_kind);
1738+
let mut flags = VariantFlags::NO_VARIANT_FLAGS;
1739+
if adt_kind == AdtKind::Struct && tcx.has_attr(did, "non_exhaustive") {
1740+
debug!("found non-exhaustive field list for {:?}", did);
1741+
flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
1742+
}
1743+
VariantDef {
1744+
did,
1745+
name,
1746+
discr,
1747+
fields,
1748+
ctor_kind,
1749+
flags
1750+
}
1751+
}
1752+
1753+
#[inline]
1754+
pub fn is_field_list_non_exhaustive(&self) -> bool {
1755+
self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE)
1756+
}
1757+
}
1758+
1759+
impl_stable_hash_for!(struct VariantDef {
1760+
did,
1761+
name,
1762+
discr,
1763+
fields,
1764+
ctor_kind,
1765+
flags
1766+
});
1767+
17151768
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
17161769
pub enum VariantDiscr {
17171770
/// Explicit value for this variant, i.e. `X = 123`.
@@ -1850,7 +1903,7 @@ impl_stable_hash_for!(struct ReprFlags {
18501903

18511904

18521905
/// Represents the repr options provided by the user,
1853-
#[derive(Copy, Clone, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)]
1906+
#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)]
18541907
pub struct ReprOptions {
18551908
pub int: Option<attr::IntType>,
18561909
pub align: u32,
@@ -1939,6 +1992,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
19391992
kind: AdtKind,
19401993
variants: Vec<VariantDef>,
19411994
repr: ReprOptions) -> Self {
1995+
debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr);
19421996
let mut flags = AdtFlags::NO_ADT_FLAGS;
19431997
let attrs = tcx.get_attrs(did);
19441998
if attr::contains_name(&attrs, "fundamental") {
@@ -1950,8 +2004,9 @@ impl<'a, 'gcx, 'tcx> AdtDef {
19502004
if Some(did) == tcx.lang_items().owned_box() {
19512005
flags = flags | AdtFlags::IS_BOX;
19522006
}
1953-
if tcx.has_attr(did, "non_exhaustive") {
1954-
flags = flags | AdtFlags::IS_NON_EXHAUSTIVE;
2007+
if kind == AdtKind::Enum && tcx.has_attr(did, "non_exhaustive") {
2008+
debug!("found non-exhaustive variant list for {:?}", did);
2009+
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
19552010
}
19562011
match kind {
19572012
AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM,
@@ -1982,8 +2037,8 @@ impl<'a, 'gcx, 'tcx> AdtDef {
19822037
}
19832038

19842039
#[inline]
1985-
pub fn is_non_exhaustive(&self) -> bool {
1986-
self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE)
2040+
pub fn is_variant_list_non_exhaustive(&self) -> bool {
2041+
self.flags.intersects(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE)
19872042
}
19882043

19892044
/// Returns the kind of the ADT - Struct or Enum.

src/librustc_metadata/decoder.rs

+18-10
Original file line numberDiff line numberDiff line change
@@ -542,28 +542,36 @@ impl<'a, 'tcx> CrateMetadata {
542542
self.def_path_table.def_path_hash(item_id))
543543
}
544544

545-
fn get_variant(&self, item: &Entry, index: DefIndex) -> ty::VariantDef {
545+
fn get_variant(&self,
546+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
547+
item: &Entry,
548+
index: DefIndex,
549+
adt_kind: ty::AdtKind)
550+
-> ty::VariantDef
551+
{
546552
let data = match item.kind {
547553
EntryKind::Variant(data) |
548554
EntryKind::Struct(data, _) |
549555
EntryKind::Union(data, _) => data.decode(self),
550556
_ => bug!(),
551557
};
552558

553-
ty::VariantDef {
554-
did: self.local_def_id(data.struct_ctor.unwrap_or(index)),
555-
name: self.item_name(index).as_symbol(),
556-
fields: item.children.decode(self).map(|index| {
559+
ty::VariantDef::new(
560+
tcx,
561+
self.local_def_id(data.struct_ctor.unwrap_or(index)),
562+
self.item_name(index).as_symbol(),
563+
data.discr,
564+
item.children.decode(self).map(|index| {
557565
let f = self.entry(index);
558566
ty::FieldDef {
559567
did: self.local_def_id(index),
560568
ident: Ident::from_interned_str(self.item_name(index)),
561569
vis: f.visibility.decode(self)
562570
}
563571
}).collect(),
564-
discr: data.discr,
565-
ctor_kind: data.ctor_kind,
566-
}
572+
adt_kind,
573+
data.ctor_kind
574+
)
567575
}
568576

569577
pub fn get_adt_def(&self,
@@ -584,11 +592,11 @@ impl<'a, 'tcx> CrateMetadata {
584592
item.children
585593
.decode(self)
586594
.map(|index| {
587-
self.get_variant(&self.entry(index), index)
595+
self.get_variant(tcx, &self.entry(index), index, kind)
588596
})
589597
.collect()
590598
} else {
591-
vec![self.get_variant(&item, item_id)]
599+
vec![self.get_variant(tcx, &item, item_id, kind)]
592600
};
593601

594602
tcx.alloc_adt_def(did, kind, variants, repr)

src/librustc_metadata/encoder.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,9 @@ 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.non_enum_variant().is_field_list_non_exhaustive() &&
744+
ctor_vis == ty::Visibility::Public
745+
{
744746
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
745747
}
746748

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_variant_list_non_exhaustive(),
385385
_ => false,
386386
}
387387
}

src/librustc_privacy/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,9 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
686686
// visibility to within the crate.
687687
let struct_def_id = self.tcx.hir.get_parent_did(node_id);
688688
let adt_def = self.tcx.adt_def(struct_def_id);
689-
if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
689+
if adt_def.non_enum_variant().is_field_list_non_exhaustive()
690+
&& ctor_vis == ty::Visibility::Public
691+
{
690692
ctor_vis = ty::Visibility::Restricted(
691693
DefId::local(CRATE_DEF_INDEX));
692694
}

src/librustc_typeck/check/_match.rs

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

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

src/librustc_typeck/check/mod.rs

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

3474-
let (substs, adt_kind, kind_name) = match &adt_ty.sty{
3474+
let (substs, adt_kind, kind_name) = match &adt_ty.sty {
34753475
&ty::Adt(adt, substs) => {
34763476
(substs, adt.adt_kind(), adt.variant_descr())
34773477
}
@@ -3645,37 +3645,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36453645
base_expr: &'gcx Option<P<hir::Expr>>) -> Ty<'tcx>
36463646
{
36473647
// Find the relevant variant
3648-
let (variant, struct_ty) =
3649-
if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) {
3650-
variant_ty
3651-
} else {
3652-
self.check_struct_fields_on_error(fields, base_expr);
3653-
return self.tcx.types.err;
3654-
};
3648+
let (variant, adt_ty) =
3649+
if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) {
3650+
variant_ty
3651+
} else {
3652+
self.check_struct_fields_on_error(fields, base_expr);
3653+
return self.tcx.types.err;
3654+
};
36553655

36563656
let path_span = match *qpath {
36573657
hir::QPath::Resolved(_, ref path) => path.span,
36583658
hir::QPath::TypeRelative(ref qself, _) => qself.span
36593659
};
36603660

36613661
// Prohibit struct expressions when non exhaustive flag is set.
3662-
if let ty::Adt(adt, _) = struct_ty.sty {
3663-
if !adt.did.is_local() && adt.is_non_exhaustive() {
3664-
span_err!(self.tcx.sess, expr.span, E0639,
3665-
"cannot create non-exhaustive {} using struct expression",
3666-
adt.variant_descr());
3667-
}
3662+
let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
3663+
if !adt.did.is_local() && variant.is_field_list_non_exhaustive() {
3664+
span_err!(self.tcx.sess, expr.span, E0639,
3665+
"cannot create non-exhaustive {} using struct expression",
3666+
adt.variant_descr());
36683667
}
36693668

3670-
let error_happened = self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span,
3669+
let error_happened = self.check_expr_struct_fields(adt_ty, expected, expr.id, path_span,
36713670
variant, fields, base_expr.is_none());
36723671
if let &Some(ref base_expr) = base_expr {
36733672
// If check_expr_struct_fields hit an error, do not attempt to populate
36743673
// the fields with the base_expr. This could cause us to hit errors later
36753674
// when certain fields are assumed to exist that in fact do not.
36763675
if !error_happened {
3677-
self.check_expr_has_type_or_error(base_expr, struct_ty);
3678-
match struct_ty.sty {
3676+
self.check_expr_has_type_or_error(base_expr, adt_ty);
3677+
match adt_ty.sty {
36793678
ty::Adt(adt, substs) if adt.is_struct() => {
36803679
let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {
36813680
self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs))
@@ -3693,8 +3692,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36933692
}
36943693
}
36953694
}
3696-
self.require_type_is_sized(struct_ty, expr.span, traits::StructInitializerSized);
3697-
struct_ty
3695+
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
3696+
adt_ty
36983697
}
36993698

37003699

src/librustc_typeck/collect.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -549,12 +549,13 @@ fn convert_enum_variant_types<'a, 'tcx>(
549549
}
550550
}
551551

552-
fn convert_struct_variant<'a, 'tcx>(
552+
fn convert_variant<'a, 'tcx>(
553553
tcx: TyCtxt<'a, 'tcx, 'tcx>,
554554
did: DefId,
555555
name: ast::Name,
556556
discr: ty::VariantDiscr,
557557
def: &hir::VariantData,
558+
adt_kind: ty::AdtKind
558559
) -> ty::VariantDef {
559560
let mut seen_fields: FxHashMap<ast::Ident, Span> = FxHashMap();
560561
let node_id = tcx.hir.as_local_node_id(did).unwrap();
@@ -585,13 +586,13 @@ fn convert_struct_variant<'a, 'tcx>(
585586
}
586587
})
587588
.collect();
588-
ty::VariantDef {
589+
ty::VariantDef::new(tcx,
589590
did,
590591
name,
591592
discr,
592593
fields,
593-
ctor_kind: CtorKind::from_hir(def),
594-
}
594+
adt_kind,
595+
CtorKind::from_hir(def))
595596
}
596597

597598
fn adt_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::AdtDef {
@@ -621,7 +622,7 @@ fn adt_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::Ad
621622
};
622623
distance_from_explicit += 1;
623624

624-
convert_struct_variant(tcx, did, v.node.name, discr, &v.node.data)
625+
convert_variant(tcx, did, v.node.name, discr, &v.node.data, AdtKind::Enum)
625626
})
626627
.collect(),
627628
)
@@ -635,23 +636,25 @@ fn adt_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::Ad
635636
};
636637
(
637638
AdtKind::Struct,
638-
vec![convert_struct_variant(
639+
vec![convert_variant(
639640
tcx,
640641
ctor_id.unwrap_or(def_id),
641642
item.name,
642643
ty::VariantDiscr::Relative(0),
643644
def,
645+
AdtKind::Struct
644646
)],
645647
)
646648
}
647649
ItemKind::Union(ref def, _) => (
648650
AdtKind::Union,
649-
vec![convert_struct_variant(
651+
vec![convert_variant(
650652
tcx,
651653
def_id,
652654
item.name,
653655
ty::VariantDiscr::Relative(0),
654656
def,
657+
AdtKind::Union
655658
)],
656659
),
657660
_ => bug!(),

0 commit comments

Comments
 (0)