diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 6250e12f43043..bb8498cb94d67 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -344,13 +344,13 @@ impl<'a> HashStable> for ty::AdtFlags { } } -impl_stable_hash_for!(struct ty::VariantDef { - did, - name, - discr, - fields, - ctor_kind -}); +impl<'a> HashStable> for ty::VariantFlags { + fn hash_stable(&self, + _: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + std_hash::Hash::hash(self, hasher); + } +} impl_stable_hash_for!(enum ty::VariantDiscr { Explicit(def_id), diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 63308ac46d10f..5e2093d03566a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1692,12 +1692,17 @@ bitflags! { const IS_FUNDAMENTAL = 1 << 2; const IS_UNION = 1 << 3; const IS_BOX = 1 << 4; - /// Indicates whether this abstract data type will be expanded on in future (new - /// fields/variants) and as such, whether downstream crates must match exhaustively on the - /// fields/variants of this data type. - /// - /// See RFC 2008 (). - const IS_NON_EXHAUSTIVE = 1 << 5; + /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. + /// (i.e., this flag is never set unless this ADT is an enum). + const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 5; + } +} + +bitflags! { + pub struct VariantFlags: u32 { + const NO_VARIANT_FLAGS = 0; + /// Indicates whether the field list of this variant is `#[non_exhaustive]`. + const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0; } } @@ -1710,8 +1715,56 @@ pub struct VariantDef { pub discr: VariantDiscr, pub fields: Vec, pub ctor_kind: CtorKind, + flags: VariantFlags, } +impl<'a, 'gcx, 'tcx> VariantDef { + /// Create a new `VariantDef`. + /// + /// - `did` is the DefId used for the variant - for tuple-structs, it is the constructor DefId, + /// and for everything else, it is the variant DefId. + /// - `attribute_def_id` is the DefId that has the variant's attributes. + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, + did: DefId, + name: Name, + discr: VariantDiscr, + fields: Vec, + adt_kind: AdtKind, + ctor_kind: CtorKind) + -> Self + { + debug!("VariantDef::new({:?}, {:?}, {:?}, {:?}, {:?}, {:?})", did, name, discr, fields, + adt_kind, ctor_kind); + let mut flags = VariantFlags::NO_VARIANT_FLAGS; + if adt_kind == AdtKind::Struct && tcx.has_attr(did, "non_exhaustive") { + debug!("found non-exhaustive field list for {:?}", did); + flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; + } + VariantDef { + did, + name, + discr, + fields, + ctor_kind, + flags + } + } + + #[inline] + pub fn is_field_list_non_exhaustive(&self) -> bool { + self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) + } +} + +impl_stable_hash_for!(struct VariantDef { + did, + name, + discr, + fields, + ctor_kind, + flags +}); + #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum VariantDiscr { /// Explicit value for this variant, i.e. `X = 123`. @@ -1850,7 +1903,7 @@ impl_stable_hash_for!(struct ReprFlags { /// Represents the repr options provided by the user, -#[derive(Copy, Clone, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)] pub struct ReprOptions { pub int: Option, pub align: u32, @@ -1939,6 +1992,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { kind: AdtKind, variants: Vec, repr: ReprOptions) -> Self { + debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); let mut flags = AdtFlags::NO_ADT_FLAGS; let attrs = tcx.get_attrs(did); if attr::contains_name(&attrs, "fundamental") { @@ -1950,8 +2004,9 @@ impl<'a, 'gcx, 'tcx> AdtDef { if Some(did) == tcx.lang_items().owned_box() { flags = flags | AdtFlags::IS_BOX; } - if tcx.has_attr(did, "non_exhaustive") { - flags = flags | AdtFlags::IS_NON_EXHAUSTIVE; + if kind == AdtKind::Enum && tcx.has_attr(did, "non_exhaustive") { + debug!("found non-exhaustive variant list for {:?}", did); + flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } match kind { AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM, @@ -1982,8 +2037,8 @@ impl<'a, 'gcx, 'tcx> AdtDef { } #[inline] - pub fn is_non_exhaustive(&self) -> bool { - self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE) + pub fn is_variant_list_non_exhaustive(&self) -> bool { + self.flags.intersects(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) } /// Returns the kind of the ADT - Struct or Enum. diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index f4456b96027ae..9907df7ed0240 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -542,7 +542,13 @@ impl<'a, 'tcx> CrateMetadata { self.def_path_table.def_path_hash(item_id)) } - fn get_variant(&self, item: &Entry, index: DefIndex) -> ty::VariantDef { + fn get_variant(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + item: &Entry, + index: DefIndex, + adt_kind: ty::AdtKind) + -> ty::VariantDef + { let data = match item.kind { EntryKind::Variant(data) | EntryKind::Struct(data, _) | @@ -550,10 +556,12 @@ impl<'a, 'tcx> CrateMetadata { _ => bug!(), }; - ty::VariantDef { - did: self.local_def_id(data.struct_ctor.unwrap_or(index)), - name: self.item_name(index).as_symbol(), - fields: item.children.decode(self).map(|index| { + ty::VariantDef::new( + tcx, + self.local_def_id(data.struct_ctor.unwrap_or(index)), + self.item_name(index).as_symbol(), + data.discr, + item.children.decode(self).map(|index| { let f = self.entry(index); ty::FieldDef { did: self.local_def_id(index), @@ -561,9 +569,9 @@ impl<'a, 'tcx> CrateMetadata { vis: f.visibility.decode(self) } }).collect(), - discr: data.discr, - ctor_kind: data.ctor_kind, - } + adt_kind, + data.ctor_kind + ) } pub fn get_adt_def(&self, @@ -584,11 +592,11 @@ impl<'a, 'tcx> CrateMetadata { item.children .decode(self) .map(|index| { - self.get_variant(&self.entry(index), index) + self.get_variant(tcx, &self.entry(index), index, kind) }) .collect() } else { - vec![self.get_variant(&item, item_id)] + vec![self.get_variant(tcx, &item, item_id, kind)] }; tcx.alloc_adt_def(did, kind, variants, repr) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 8219ec3df248a..0fd43c592c853 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -740,7 +740,9 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { // If the structure is marked as non_exhaustive then lower the visibility // to within the crate. - if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public { + if adt_def.non_enum_variant().is_field_list_non_exhaustive() && + ctor_vis == ty::Visibility::Public + { ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); } diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index e9d65caf08743..d7fbbc88cc16d 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -381,7 +381,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.sty { - ty::Adt(adt_def, ..) => adt_def.is_enum() && adt_def.is_non_exhaustive(), + ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(), _ => false, } } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 5166f69ba0330..4245136ef2131 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -686,7 +686,9 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { // visibility to within the crate. let struct_def_id = self.tcx.hir.get_parent_did(node_id); let adt_def = self.tcx.adt_def(struct_def_id); - if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public { + if adt_def.non_enum_variant().is_field_list_non_exhaustive() + && ctor_vis == ty::Visibility::Public + { ctor_vis = ty::Visibility::Restricted( DefId::local(CRATE_DEF_INDEX)); } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 6b5abbfa4a6f7..2a8ee4bd8df0e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -950,7 +950,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); } // Require `..` if struct has non_exhaustive attribute. - if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc { + if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc { span_err!(tcx.sess, span, E0638, "`..` required with {} marked as non-exhaustive", kind_name); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5cc87e12ed069..a45e5c8ec2a45 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3471,7 +3471,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // re-link the regions that EIfEO can erase. self.demand_eqtype(span, adt_ty_hint, adt_ty); - let (substs, adt_kind, kind_name) = match &adt_ty.sty{ + let (substs, adt_kind, kind_name) = match &adt_ty.sty { &ty::Adt(adt, substs) => { (substs, adt.adt_kind(), adt.variant_descr()) } @@ -3645,13 +3645,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { base_expr: &'gcx Option>) -> Ty<'tcx> { // Find the relevant variant - let (variant, struct_ty) = - if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) { - variant_ty - } else { - self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.types.err; - }; + let (variant, adt_ty) = + if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) { + variant_ty + } else { + self.check_struct_fields_on_error(fields, base_expr); + return self.tcx.types.err; + }; let path_span = match *qpath { hir::QPath::Resolved(_, ref path) => path.span, @@ -3659,23 +3659,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // Prohibit struct expressions when non exhaustive flag is set. - if let ty::Adt(adt, _) = struct_ty.sty { - if !adt.did.is_local() && adt.is_non_exhaustive() { - span_err!(self.tcx.sess, expr.span, E0639, - "cannot create non-exhaustive {} using struct expression", - adt.variant_descr()); - } + let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); + if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { + span_err!(self.tcx.sess, expr.span, E0639, + "cannot create non-exhaustive {} using struct expression", + adt.variant_descr()); } - let error_happened = self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, + let error_happened = self.check_expr_struct_fields(adt_ty, expected, expr.id, path_span, variant, fields, base_expr.is_none()); if let &Some(ref base_expr) = base_expr { // If check_expr_struct_fields hit an error, do not attempt to populate // 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 { - self.check_expr_has_type_or_error(base_expr, struct_ty); - match struct_ty.sty { + self.check_expr_has_type_or_error(base_expr, adt_ty); + match adt_ty.sty { ty::Adt(adt, substs) if adt.is_struct() => { let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| { self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs)) @@ -3693,8 +3692,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } } - self.require_type_is_sized(struct_ty, expr.span, traits::StructInitializerSized); - struct_ty + self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); + adt_ty } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b956c72b3a2da..decd24e87f365 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -549,12 +549,13 @@ fn convert_enum_variant_types<'a, 'tcx>( } } -fn convert_struct_variant<'a, 'tcx>( +fn convert_variant<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, did: DefId, name: ast::Name, discr: ty::VariantDiscr, def: &hir::VariantData, + adt_kind: ty::AdtKind ) -> ty::VariantDef { let mut seen_fields: FxHashMap = FxHashMap(); let node_id = tcx.hir.as_local_node_id(did).unwrap(); @@ -585,13 +586,13 @@ fn convert_struct_variant<'a, 'tcx>( } }) .collect(); - ty::VariantDef { + ty::VariantDef::new(tcx, did, name, discr, fields, - ctor_kind: CtorKind::from_hir(def), - } + adt_kind, + CtorKind::from_hir(def)) } 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 }; distance_from_explicit += 1; - convert_struct_variant(tcx, did, v.node.name, discr, &v.node.data) + convert_variant(tcx, did, v.node.name, discr, &v.node.data, AdtKind::Enum) }) .collect(), ) @@ -635,23 +636,25 @@ fn adt_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::Ad }; ( AdtKind::Struct, - vec![convert_struct_variant( + vec![convert_variant( tcx, ctor_id.unwrap_or(def_id), item.name, ty::VariantDiscr::Relative(0), def, + AdtKind::Struct )], ) } ItemKind::Union(ref def, _) => ( AdtKind::Union, - vec![convert_struct_variant( + vec![convert_variant( tcx, def_id, item.name, ty::VariantDiscr::Relative(0), def, + AdtKind::Union )], ), _ => bug!(), diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs b/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs index 9d41eca8fe5d2..83fb24cda088c 100644 --- a/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs +++ b/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs @@ -30,4 +30,33 @@ fn main() { match enum_unit { _ => "no error with only wildcard" }; + + + // issue #53549 - check that variant constructors can still be called normally. + + match NonExhaustiveEnum::Unit { + NonExhaustiveEnum::Unit => {}, + _ => {} + }; + + match NonExhaustiveEnum::Tuple(2) { + NonExhaustiveEnum::Tuple(2) => {}, + _ => {} + }; + + match (NonExhaustiveEnum::Unit {}) { + NonExhaustiveEnum::Unit {} => {}, + _ => {} + }; + + match (NonExhaustiveEnum::Tuple { 0: 2 }) { + NonExhaustiveEnum::Tuple { 0: 2 } => {}, + _ => {} + }; + + match (NonExhaustiveEnum::Struct { field: 2 }) { + NonExhaustiveEnum::Struct { field: 2 } => {}, + _ => {} + }; + }