diff --git a/src/doc/unstable-book/src/language-features/non-exhaustive.md b/src/doc/unstable-book/src/language-features/non-exhaustive.md index f9840e1b83f2b..955bbcc36a12d 100644 --- a/src/doc/unstable-book/src/language-features/non-exhaustive.md +++ b/src/doc/unstable-book/src/language-features/non-exhaustive.md @@ -7,10 +7,11 @@ The tracking issue for this feature is: [#44109] ------------------------ The `non_exhaustive` gate allows you to use the `#[non_exhaustive]` attribute -on structs and enums. When applied within a crate, users of the crate will need -to use the `_` pattern when matching enums and use the `..` pattern when -matching structs. Structs marked as `non_exhaustive` will not be able to be -created normally outside of the defining crate. This is demonstrated below: +on structs, enums and variants. When applied within a crate, users of the crate +will need to use the `_` pattern when matching enums and use the `..` pattern +when matching structs or variants. Structs and variants marked as +`non_exhaustive` will not be able to be created normally outside of the +defining crate. This is demonstrated below: ```rust,ignore (pseudo-Rust) use std::error::Error as StdError; diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 5ace8397d9f82..43cb43adb9d2a 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2052,7 +2052,7 @@ where 'x: 'y E0701: r##" This error indicates that a `#[non_exhaustive]` attribute was incorrectly placed -on something other than a struct or enum. +on something other than a struct, enum or variant. Examples of erroneous code: diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index cb685f83aba1e..0876bfc1cb33d 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -349,7 +349,8 @@ impl_stable_hash_for!(struct ty::VariantDef { name, discr, fields, - ctor_kind + ctor_kind, + can_extend_field_list }); impl_stable_hash_for!(enum ty::VariantDiscr { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c2795bae01029..9be03889fdfb3 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1679,6 +1679,8 @@ pub struct VariantDef { pub discr: VariantDiscr, pub fields: Vec, pub ctor_kind: CtorKind, + /// Field list can be extended if this struct/variant is marked as non-exhaustive. + pub can_extend_field_list: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] @@ -1940,9 +1942,16 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.flags.intersects(AdtFlags::IS_ENUM) } + /// Variant list can be extended if this enum is marked as non-exhaustive (only `true` if adt + /// represents an enum). #[inline] - pub fn is_non_exhaustive(&self) -> bool { - self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE) + pub fn can_extend_variant_list(&self) -> bool { + // AdtDef represents structs and enums where structs have a single variant. + // We represent a non-exhaustive enum by setting the non-exhaustive flag on the + // AdtDef and non-exhaustive variants and structs by setting the non-exhaustive + // flag on the VariantDef. Therefore, we should double check here that the + // AdtDef represents an enum. + self.is_enum() && self.flags.intersects(AdtFlags::IS_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 ab566654c389c..98c9dc580008e 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -526,6 +526,7 @@ impl<'a, 'tcx> CrateMetadata { }).collect(), discr: data.discr, ctor_kind: data.ctor_kind, + can_extend_field_list: data.can_extend_field_list, } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 7ed991e0de3a1..689943aa9debc 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -595,7 +595,8 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { Some(self.lazy(&tcx.fn_sig(def_id))) } else { None - } + }, + can_extend_field_list: variant.can_extend_field_list, }; let enum_id = tcx.hir.as_local_node_id(enum_did).unwrap(); @@ -721,7 +722,8 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { Some(self.lazy(&tcx.fn_sig(def_id))) } else { None - } + }, + can_extend_field_list: variant.can_extend_field_list, }; let struct_id = tcx.hir.as_local_node_id(adt_def_id).unwrap(); @@ -735,7 +737,7 @@ 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 variant.can_extend_field_list && ctor_vis == ty::Visibility::Public { ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); } @@ -1089,6 +1091,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { discr: variant.discr, struct_ctor, ctor_sig: None, + can_extend_field_list: variant.can_extend_field_list, }), repr_options) } hir::ItemKind::Union(..) => { @@ -1100,6 +1103,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { discr: variant.discr, struct_ctor: None, ctor_sig: None, + can_extend_field_list: variant.can_extend_field_list, }), repr_options) } hir::ItemKind::Impl(_, polarity, defaultness, ..) => { diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index d7c54cbc81d94..172aaa53af008 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -455,13 +455,18 @@ pub struct VariantData<'tcx> { /// If this is a tuple struct or variant /// ctor, this is its "function" signature. pub ctor_sig: Option>>, + + /// Field list can be extended if this struct/variant + /// is marked as non-exhaustive. + pub can_extend_field_list: bool, } impl_stable_hash_for!(struct VariantData<'tcx> { ctor_kind, discr, struct_ctor, - ctor_sig + ctor_sig, + can_extend_field_list, }); #[derive(RustcEncodable, RustcDecodable)] diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 83361ea57c371..ae5869c786614 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -220,7 +220,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.sty { - ty::TyAdt(adt_def, ..) => adt_def.is_enum() && adt_def.is_non_exhaustive(), + ty::TyAdt(adt_def, ..) => adt_def.can_extend_variant_list(), _ => false, } } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index f27ca444672ab..6550c2e57ee79 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -51,14 +51,6 @@ impl<'a> AstValidator<'a> { } } - fn invalid_non_exhaustive_attribute(&self, variant: &Variant) { - let has_non_exhaustive = attr::contains_name(&variant.node.attrs, "non_exhaustive"); - if has_non_exhaustive { - self.err_handler().span_err(variant.span, - "#[non_exhaustive] is not yet supported on variants"); - } - } - fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) { if let VisibilityKind::Inherited = vis.node { return @@ -309,7 +301,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } ItemKind::Enum(ref def, _) => { for variant in &def.variants { - self.invalid_non_exhaustive_attribute(variant); for field in variant.node.data.fields() { self.invalid_visibility(&field.vis, None); } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 7b13c98b31ddf..c540b2c41120f 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -674,9 +674,11 @@ 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 { - ctor_vis = ty::Visibility::Restricted( - DefId::local(CRATE_DEF_INDEX)); + if let Some(variant) = adt_def.variants.first() { + if variant.can_extend_field_list && ctor_vis == ty::Visibility::Public { + ctor_vis = ty::Visibility::Restricted( + DefId::local(CRATE_DEF_INDEX)); + } } return ctor_vis; diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index c9b5fd525dd82..54e148a159492 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -13,6 +13,7 @@ use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc::infer; use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::session::Session; use rustc::traits::ObligationCauseCode; use rustc::ty::{self, Ty, TypeFoldable}; use check::{FnCtxt, Expectation, Diverges, Needs}; @@ -789,7 +790,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); // Resolve the path and check the definition for errors. let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(qpath, pat.id, pat.span); - let variant = match def { + let (variant, kind_name) = match def { Def::Err => { self.set_tainted_by_errors(); on_error(); @@ -799,10 +800,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); report_unexpected_def(def); return tcx.types.err; } - Def::VariantCtor(_, CtorKind::Fn) | - Def::StructCtor(_, CtorKind::Fn) => { - tcx.expect_variant_def(def) - } + Def::VariantCtor(_, CtorKind::Fn) => (tcx.expect_variant_def(def), "variant"), + Def::StructCtor(_, CtorKind::Fn) => (tcx.expect_variant_def(def), "struct"), _ => bug!("unexpected pattern definition: {:?}", def) }; @@ -814,6 +813,11 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); self.demand_eqtype(pat.span, expected, pat_ty); + // Require `..` if tuple struct/variant has non_exhaustive attribute. + if variant.can_extend_field_list && !variant.did.is_local() && ddpos.is_none() { + self.report_non_exhaustive_error(&tcx.sess, &pat.span, kind_name); + } + // Type check subpatterns. if subpats.len() == variant.fields.len() || subpats.len() < variant.fields.len() && ddpos.is_some() { @@ -948,10 +952,8 @@ 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 { - span_err!(tcx.sess, span, E0638, - "`..` required with {} marked as non-exhaustive", - kind_name); + if variant.can_extend_field_list && !variant.did.is_local() && !etc { + self.report_non_exhaustive_error(&tcx.sess, &span, kind_name); } // Report an error if incorrect number of the fields were specified. @@ -998,4 +1000,9 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); } no_field_errors } + + fn report_non_exhaustive_error(&self, sess: &Session, span: &Span, kind_name: &str) { + span_err!(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 2cfeb2513926b..c73cc018bb56e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3573,12 +3573,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // Prohibit struct expressions when non exhaustive flag is set. + let mut is_struct = false; if let ty::TyAdt(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()); - } + is_struct = adt.is_struct(); + } + + if !variant.did.is_local() && variant.can_extend_field_list { + span_err!(self.tcx.sess, expr.span, E0639, + "cannot create non-exhaustive {} using struct expression", + if is_struct { "struct" } else { "variant" }); } let error_happened = self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5193113d82c8a..d9e3885eda367 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -546,12 +546,14 @@ fn convert_struct_variant<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, vis: ty::Visibility::from_hir(&f.vis, node_id, tcx) } }).collect(); + ty::VariantDef { did, name, discr, fields, ctor_kind: CtorKind::from_hir(def), + can_extend_field_list: tcx.has_attr(did, "non_exhaustive"), } } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 4d957c9aa4520..70e8d346ea447 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4446,11 +4446,12 @@ foo.method(); // Ok! "##, E0638: r##" -This error indicates that the struct or enum must be matched non-exhaustively -as it has been marked as `non_exhaustive`. +This error indicates that the struct, enum or variant must be matched +non-exhaustively as it has been marked as `non_exhaustive`. When applied within a crate, downstream users of the crate will need to use the -`_` pattern when matching enums and use the `..` pattern when matching structs. +`_` pattern when matching enums and use the `..` pattern when matching structs +or variants. For example, in the below example, since the enum is marked as `non_exhaustive`, it is required that downstream crates match non-exhaustively @@ -4495,10 +4496,10 @@ Similarly, for structs, match with `..` to avoid this error. "##, E0639: r##" -This error indicates that the struct or enum cannot be instantiated from -outside of the defining crate as it has been marked as `non_exhaustive` and as -such more fields/variants may be added in future that could cause adverse side -effects for this code. +This error indicates that the struct, enum or variant cannot be instantiated +from outside of the defining crate as it has been marked as `non_exhaustive` +and as such more fields/variants may be added in future that could cause +adverse side effects for this code. It is recommended that you look for a `new` function or equivalent in the crate's documentation. diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs index d1b65ac1f3e52..ba181d7820d7d 100644 --- a/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs @@ -13,12 +13,6 @@ extern crate variants; use variants::NonExhaustiveVariants; -/* - * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for - * variants. See issue #44109 and PR 45394. - */ -// ignore-test - fn main() { let variant_struct = NonExhaustiveVariants::Struct { field: 640 }; //~^ ERROR cannot create non-exhaustive variant @@ -26,6 +20,12 @@ fn main() { let variant_tuple = NonExhaustiveVariants::Tuple { 0: 640 }; //~^ ERROR cannot create non-exhaustive variant + match variant_struct { + NonExhaustiveVariants::Tuple { 0: fe_tpl } => "", + //~^ ERROR `..` required with variant marked as non-exhaustive + _ => "", + }; + match variant_struct { NonExhaustiveVariants::Unit => "", NonExhaustiveVariants::Tuple(fe_tpl) => "", diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/variants_create.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/variants_create.rs deleted file mode 100644 index f4e4b1bb84b8b..0000000000000 --- a/src/test/compile-fail/rfc-2008-non-exhaustive/variants_create.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(non_exhaustive)] - -/* - * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for - * variants. See issue #44109 and PR 45394. - */ - -pub enum NonExhaustiveVariants { - #[non_exhaustive] Unit, - //~^ ERROR #[non_exhaustive] is not yet supported on variants - #[non_exhaustive] Tuple(u32), - //~^ ERROR #[non_exhaustive] is not yet supported on variants - #[non_exhaustive] Struct { field: u32 } - //~^ ERROR #[non_exhaustive] is not yet supported on variants -} - -fn main() { } diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs b/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs index 2658c59a69985..1b128cffa15f1 100644 --- a/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs +++ b/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs @@ -13,19 +13,15 @@ extern crate variants; use variants::NonExhaustiveVariants; -/* - * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for - * variants. See issue #44109 and PR 45394. - */ -// ignore-test +// We only test matching here as we cannot create non-exhaustive +// variants from another crate. ie. they'll never pass in run-pass tests. -fn main() { - let variant_tuple = NonExhaustiveVariants::Tuple { 0: 340 }; - let variant_struct = NonExhaustiveVariants::Struct { field: 340 }; - - match variant_struct { +fn match_variants(non_exhaustive_enum: NonExhaustiveVariants) { + match non_exhaustive_enum { NonExhaustiveVariants::Unit => "", NonExhaustiveVariants::Struct { field, .. } => "", NonExhaustiveVariants::Tuple(fe_tpl, ..) => "" }; } + +fn main() {} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs b/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs index a1c376c17985d..21eb013d2e9f9 100644 --- a/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs +++ b/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs @@ -10,12 +10,6 @@ #![feature(non_exhaustive)] -/* - * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for - * variants. See issue #44109 and PR 45394. - */ -// ignore-test - pub enum NonExhaustiveVariants { #[non_exhaustive] Unit, #[non_exhaustive] Tuple(u32),