Rollup merge of #59376 - davidtwco:finally-rfc-2008-variants, r=petrochenkov,QuietMisdreavus
RFC 2008: Enum Variants
Part of #44109. See [Zulip topic](132663140
) for previous discussion.
r? @petrochenkov
cc @nikomatsakis
This commit is contained in:
commit
d050a157a8
26 changed files with 219 additions and 216 deletions
|
@ -7,10 +7,12 @@ The tracking issue for this feature is: [#44109]
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
The `non_exhaustive` gate allows you to use the `#[non_exhaustive]` attribute
|
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
|
on structs, enums and enum variants. When applied within a crate, users of the
|
||||||
to use the `_` pattern when matching enums and use the `..` pattern when
|
crate will need to use the `_` pattern when matching enums and use the `..`
|
||||||
matching structs. Structs marked as `non_exhaustive` will not be able to be
|
pattern when matching structs. Enum variants cannot be matched against.
|
||||||
created normally outside of the defining crate. This is demonstrated below:
|
Structs and enum 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)
|
```rust,ignore (pseudo-Rust)
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
@ -72,4 +74,3 @@ let config = Config { window_width: 640, window_height: 480 };
|
||||||
// when marked non_exhaustive.
|
// when marked non_exhaustive.
|
||||||
let &Config { window_width, window_height, .. } = config;
|
let &Config { window_width, window_height, .. } = config;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1869,6 +1869,11 @@ impl<'a, 'gcx, 'tcx> VariantDef {
|
||||||
if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, "non_exhaustive") {
|
if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, "non_exhaustive") {
|
||||||
debug!("found non-exhaustive field list for {:?}", parent_did);
|
debug!("found non-exhaustive field list for {:?}", parent_did);
|
||||||
flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
|
flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
|
||||||
|
} else if let Some(variant_did) = variant_did {
|
||||||
|
if tcx.has_attr(variant_did, "non_exhaustive") {
|
||||||
|
debug!("found non-exhaustive field list for {:?}", variant_did);
|
||||||
|
flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantDef {
|
VariantDef {
|
||||||
|
@ -2146,6 +2151,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
debug!("found non-exhaustive variant list for {:?}", did);
|
debug!("found non-exhaustive variant list for {:?}", did);
|
||||||
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
|
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
flags |= match kind {
|
flags |= match kind {
|
||||||
AdtKind::Enum => AdtFlags::IS_ENUM,
|
AdtKind::Enum => AdtFlags::IS_ENUM,
|
||||||
AdtKind::Union => AdtFlags::IS_UNION,
|
AdtKind::Union => AdtFlags::IS_UNION,
|
||||||
|
@ -2299,21 +2305,25 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
self.variants.iter().all(|v| v.fields.is_empty())
|
self.variants.iter().all(|v| v.fields.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a `VariantDef` given a variant id.
|
||||||
pub fn variant_with_id(&self, vid: DefId) -> &VariantDef {
|
pub fn variant_with_id(&self, vid: DefId) -> &VariantDef {
|
||||||
self.variants.iter().find(|v| v.def_id == vid)
|
self.variants.iter().find(|v| v.def_id == vid)
|
||||||
.expect("variant_with_id: unknown variant")
|
.expect("variant_with_id: unknown variant")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a `VariantDef` given a constructor id.
|
||||||
pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef {
|
pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef {
|
||||||
self.variants.iter().find(|v| v.ctor_def_id == Some(cid))
|
self.variants.iter().find(|v| v.ctor_def_id == Some(cid))
|
||||||
.expect("variant_with_ctor_id: unknown variant")
|
.expect("variant_with_ctor_id: unknown variant")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the index of `VariantDef` given a variant id.
|
||||||
pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx {
|
pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx {
|
||||||
self.variants.iter_enumerated().find(|(_, v)| v.def_id == vid)
|
self.variants.iter_enumerated().find(|(_, v)| v.def_id == vid)
|
||||||
.expect("variant_index_with_id: unknown variant").0
|
.expect("variant_index_with_id: unknown variant").0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the index of `VariantDef` given a constructor id.
|
||||||
pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx {
|
pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx {
|
||||||
self.variants.iter_enumerated().find(|(_, v)| v.ctor_def_id == Some(cid))
|
self.variants.iter_enumerated().find(|(_, v)| v.ctor_def_id == Some(cid))
|
||||||
.expect("variant_index_with_ctor_id: unknown variant").0
|
.expect("variant_index_with_ctor_id: unknown variant").0
|
||||||
|
@ -2930,8 +2940,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns `ty::VariantDef` if `def` refers to a struct,
|
/// Returns `ty::VariantDef` if `def` refers to a struct,
|
||||||
// or variant or their constructors, panics otherwise.
|
/// or variant or their constructors, panics otherwise.
|
||||||
pub fn expect_variant_def(self, def: Def) -> &'tcx VariantDef {
|
pub fn expect_variant_def(self, def: Def) -> &'tcx VariantDef {
|
||||||
match def {
|
match def {
|
||||||
Def::Variant(did) => {
|
Def::Variant(did) => {
|
||||||
|
|
|
@ -643,13 +643,18 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Variant constructors have the same visibility as the parent enums.
|
// Variant constructors have the same visibility as the parent enums, unless marked as
|
||||||
|
// non-exhaustive, in which case they are lowered to `pub(crate)`.
|
||||||
let enum_id = tcx.hir().as_local_hir_id(enum_did).unwrap();
|
let enum_id = tcx.hir().as_local_hir_id(enum_did).unwrap();
|
||||||
let enum_vis = &tcx.hir().expect_item_by_hir_id(enum_id).vis;
|
let enum_vis = &tcx.hir().expect_item_by_hir_id(enum_id).vis;
|
||||||
|
let mut ctor_vis = ty::Visibility::from_hir(enum_vis, enum_id, tcx);
|
||||||
|
if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public {
|
||||||
|
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
|
||||||
|
}
|
||||||
|
|
||||||
Entry {
|
Entry {
|
||||||
kind: EntryKind::Variant(self.lazy(&data)),
|
kind: EntryKind::Variant(self.lazy(&data)),
|
||||||
visibility: self.lazy(&ty::Visibility::from_hir(enum_vis, enum_id, tcx)),
|
visibility: self.lazy(&ctor_vis),
|
||||||
span: self.lazy(&tcx.def_span(def_id)),
|
span: self.lazy(&tcx.def_span(def_id)),
|
||||||
attributes: LazySeq::empty(),
|
attributes: LazySeq::empty(),
|
||||||
children: LazySeq::empty(),
|
children: LazySeq::empty(),
|
||||||
|
|
|
@ -192,14 +192,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>) {
|
fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
|
||||||
if let VisibilityKind::Inherited = vis.node {
|
if let VisibilityKind::Inherited = vis.node {
|
||||||
return
|
return
|
||||||
|
@ -608,7 +600,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
}
|
}
|
||||||
ItemKind::Enum(ref def, _) => {
|
ItemKind::Enum(ref def, _) => {
|
||||||
for variant in &def.variants {
|
for variant in &def.variants {
|
||||||
self.invalid_non_exhaustive_attribute(variant);
|
|
||||||
for field in variant.node.data.fields() {
|
for field in variant.node.data.fields() {
|
||||||
self.invalid_visibility(&field.vis, None);
|
self.invalid_visibility(&field.vis, None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,26 @@ fn def_id_visibility<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
|
||||||
match tcx.hir().get_by_hir_id(parent_hir_id) {
|
match tcx.hir().get_by_hir_id(parent_hir_id) {
|
||||||
Node::Variant(..) => {
|
Node::Variant(..) => {
|
||||||
let parent_did = tcx.hir().local_def_id_from_hir_id(parent_hir_id);
|
let parent_did = tcx.hir().local_def_id_from_hir_id(parent_hir_id);
|
||||||
return def_id_visibility(tcx, parent_did);
|
let (mut ctor_vis, mut span, mut descr) = def_id_visibility(
|
||||||
|
tcx, parent_did,
|
||||||
|
);
|
||||||
|
|
||||||
|
let adt_def = tcx.adt_def(tcx.hir().get_parent_did_by_hir_id(hir_id));
|
||||||
|
let ctor_did = tcx.hir().local_def_id_from_hir_id(
|
||||||
|
vdata.ctor_hir_id().unwrap());
|
||||||
|
let variant = adt_def.variant_with_ctor_id(ctor_did);
|
||||||
|
|
||||||
|
if variant.is_field_list_non_exhaustive() &&
|
||||||
|
ctor_vis == ty::Visibility::Public
|
||||||
|
{
|
||||||
|
ctor_vis = ty::Visibility::Restricted(
|
||||||
|
DefId::local(CRATE_DEF_INDEX));
|
||||||
|
let attrs = tcx.get_attrs(variant.def_id);
|
||||||
|
span = attr::find_by_name(&attrs, "non_exhaustive").unwrap().span;
|
||||||
|
descr = "crate-visible";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ctor_vis, span, descr);
|
||||||
}
|
}
|
||||||
Node::Item(..) => {
|
Node::Item(..) => {
|
||||||
let item = match tcx.hir().get_by_hir_id(parent_hir_id) {
|
let item = match tcx.hir().get_by_hir_id(parent_hir_id) {
|
||||||
|
|
|
@ -588,6 +588,14 @@ impl<'a> Resolver<'a> {
|
||||||
let def = Def::Variant(def_id);
|
let def = Def::Variant(def_id);
|
||||||
self.define(parent, ident, TypeNS, (def, vis, variant.span, expansion));
|
self.define(parent, ident, TypeNS, (def, vis, variant.span, expansion));
|
||||||
|
|
||||||
|
// If the variant is marked as non_exhaustive then lower the visibility to within the
|
||||||
|
// crate.
|
||||||
|
let mut ctor_vis = vis;
|
||||||
|
let has_non_exhaustive = attr::contains_name(&variant.node.attrs, "non_exhaustive");
|
||||||
|
if has_non_exhaustive && vis == ty::Visibility::Public {
|
||||||
|
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
|
||||||
|
}
|
||||||
|
|
||||||
// Define a constructor name in the value namespace.
|
// Define a constructor name in the value namespace.
|
||||||
// Braced variants, unlike structs, generate unusable names in
|
// Braced variants, unlike structs, generate unusable names in
|
||||||
// value namespace, they are reserved for possible future use.
|
// value namespace, they are reserved for possible future use.
|
||||||
|
@ -597,7 +605,7 @@ impl<'a> Resolver<'a> {
|
||||||
let ctor_def_id = self.definitions.local_def_id(ctor_node_id);
|
let ctor_def_id = self.definitions.local_def_id(ctor_node_id);
|
||||||
let ctor_kind = CtorKind::from_ast(&variant.node.data);
|
let ctor_kind = CtorKind::from_ast(&variant.node.data);
|
||||||
let ctor_def = Def::Ctor(ctor_def_id, CtorOf::Variant, ctor_kind);
|
let ctor_def = Def::Ctor(ctor_def_id, CtorOf::Variant, ctor_kind);
|
||||||
self.define(parent, ident, ValueNS, (ctor_def, vis, variant.span, expansion));
|
self.define(parent, ident, ValueNS, (ctor_def, ctor_vis, variant.span, expansion));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs the reduced graph for one foreign item.
|
/// Constructs the reduced graph for one foreign item.
|
||||||
|
|
|
@ -4341,11 +4341,12 @@ foo.method(); // Ok!
|
||||||
"##,
|
"##,
|
||||||
|
|
||||||
E0638: r##"
|
E0638: r##"
|
||||||
This error indicates that the struct or enum must be matched non-exhaustively
|
This error indicates that the struct, enum or enum variant must be matched
|
||||||
as it has been marked as `non_exhaustive`.
|
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
|
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.
|
||||||
|
Downstream crates cannot match against non-exhaustive enum variants.
|
||||||
|
|
||||||
For example, in the below example, since the enum is marked as
|
For example, in the below example, since the enum is marked as
|
||||||
`non_exhaustive`, it is required that downstream crates match non-exhaustively
|
`non_exhaustive`, it is required that downstream crates match non-exhaustively
|
||||||
|
@ -4390,10 +4391,10 @@ Similarly, for structs, match with `..` to avoid this error.
|
||||||
"##,
|
"##,
|
||||||
|
|
||||||
E0639: r##"
|
E0639: r##"
|
||||||
This error indicates that the struct or enum cannot be instantiated from
|
This error indicates that the struct, enum or enum variant cannot be
|
||||||
outside of the defining crate as it has been marked as `non_exhaustive` and as
|
instantiated from outside of the defining crate as it has been marked
|
||||||
such more fields/variants may be added in future that could cause adverse side
|
as `non_exhaustive` and as such more fields/variants may be added in
|
||||||
effects for this code.
|
future that could cause adverse side effects for this code.
|
||||||
|
|
||||||
It is recommended that you look for a `new` function or equivalent in the
|
It is recommended that you look for a `new` function or equivalent in the
|
||||||
crate's documentation.
|
crate's documentation.
|
||||||
|
|
|
@ -421,6 +421,9 @@ impl Item {
|
||||||
pub fn is_enum(&self) -> bool {
|
pub fn is_enum(&self) -> bool {
|
||||||
self.type_() == ItemType::Enum
|
self.type_() == ItemType::Enum
|
||||||
}
|
}
|
||||||
|
pub fn is_variant(&self) -> bool {
|
||||||
|
self.type_() == ItemType::Variant
|
||||||
|
}
|
||||||
pub fn is_associated_type(&self) -> bool {
|
pub fn is_associated_type(&self) -> bool {
|
||||||
self.type_() == ItemType::AssociatedType
|
self.type_() == ItemType::AssociatedType
|
||||||
}
|
}
|
||||||
|
|
|
@ -2604,7 +2604,15 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
|
||||||
fn document_non_exhaustive(w: &mut fmt::Formatter<'_>, item: &clean::Item) -> fmt::Result {
|
fn document_non_exhaustive(w: &mut fmt::Formatter<'_>, item: &clean::Item) -> fmt::Result {
|
||||||
if item.is_non_exhaustive() {
|
if item.is_non_exhaustive() {
|
||||||
write!(w, "<div class='docblock non-exhaustive non-exhaustive-{}'>", {
|
write!(w, "<div class='docblock non-exhaustive non-exhaustive-{}'>", {
|
||||||
if item.is_struct() { "struct" } else if item.is_enum() { "enum" } else { "type" }
|
if item.is_struct() {
|
||||||
|
"struct"
|
||||||
|
} else if item.is_enum() {
|
||||||
|
"enum"
|
||||||
|
} else if item.is_variant() {
|
||||||
|
"variant"
|
||||||
|
} else {
|
||||||
|
"type"
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if item.is_struct() {
|
if item.is_struct() {
|
||||||
|
@ -2617,6 +2625,10 @@ fn document_non_exhaustive(w: &mut fmt::Formatter<'_>, item: &clean::Item) -> fm
|
||||||
write!(w, "Non-exhaustive enums could have additional variants added in future. \
|
write!(w, "Non-exhaustive enums could have additional variants added in future. \
|
||||||
Therefore, when matching against variants of non-exhaustive enums, an \
|
Therefore, when matching against variants of non-exhaustive enums, an \
|
||||||
extra wildcard arm must be added to account for any future variants.")?;
|
extra wildcard arm must be added to account for any future variants.")?;
|
||||||
|
} else if item.is_variant() {
|
||||||
|
write!(w, "Non-exhaustive enum variants could have additional fields added in future. \
|
||||||
|
Therefore, non-exhaustive enum variants cannot be constructed in external \
|
||||||
|
crates and cannot be matched against.")?;
|
||||||
} else {
|
} else {
|
||||||
write!(w, "This type will require a wildcard arm in any match statements or \
|
write!(w, "This type will require a wildcard arm in any match statements or \
|
||||||
constructors.")?;
|
constructors.")?;
|
||||||
|
@ -3679,6 +3691,7 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
|
||||||
}
|
}
|
||||||
write!(w, "</code></span>")?;
|
write!(w, "</code></span>")?;
|
||||||
document(w, cx, variant)?;
|
document(w, cx, variant)?;
|
||||||
|
document_non_exhaustive(w, variant)?;
|
||||||
|
|
||||||
use crate::clean::{Variant, VariantKind};
|
use crate::clean::{Variant, VariantKind};
|
||||||
if let clean::VariantItem(Variant {
|
if let clean::VariantItem(Variant {
|
||||||
|
|
|
@ -2263,6 +2263,8 @@ if (!DOMTokenList.prototype.remove) {
|
||||||
otherMessage += "struct";
|
otherMessage += "struct";
|
||||||
} else if (hasClass(e, "non-exhaustive-enum")) {
|
} else if (hasClass(e, "non-exhaustive-enum")) {
|
||||||
otherMessage += "enum";
|
otherMessage += "enum";
|
||||||
|
} else if (hasClass(e, "non-exhaustive-variant")) {
|
||||||
|
otherMessage += "enum variant";
|
||||||
} else if (hasClass(e, "non-exhaustive-type")) {
|
} else if (hasClass(e, "non-exhaustive-type")) {
|
||||||
otherMessage += "type";
|
otherMessage += "type";
|
||||||
}
|
}
|
||||||
|
@ -2280,6 +2282,9 @@ if (!DOMTokenList.prototype.remove) {
|
||||||
if (hasClass(e, "type-decl") === true && showItemDeclarations === true) {
|
if (hasClass(e, "type-decl") === true && showItemDeclarations === true) {
|
||||||
collapseDocs(e.previousSibling.childNodes[0], "toggle");
|
collapseDocs(e.previousSibling.childNodes[0], "toggle");
|
||||||
}
|
}
|
||||||
|
if (hasClass(e, "non-exhaustive") === true) {
|
||||||
|
collapseDocs(e.previousSibling.childNodes[0], "toggle");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
// run-pass
|
|
||||||
#![crate_type = "rlib"]
|
|
||||||
#![feature(non_exhaustive)]
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum NonExhaustiveEnum {
|
|
||||||
Unit,
|
|
||||||
Tuple(u32),
|
|
||||||
Struct { field: u32 }
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// run-pass
|
|
||||||
#![feature(non_exhaustive)]
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct NormalStruct {
|
|
||||||
pub first_field: u16,
|
|
||||||
pub second_field: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct UnitStruct;
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct TupleStruct (pub u16, pub u16);
|
|
|
@ -1,9 +0,0 @@
|
||||||
// run-pass
|
|
||||||
#![crate_type = "rlib"]
|
|
||||||
#![feature(non_exhaustive)]
|
|
||||||
|
|
||||||
pub enum NonExhaustiveVariants {
|
|
||||||
#[non_exhaustive] Unit,
|
|
||||||
#[non_exhaustive] Tuple(u32),
|
|
||||||
#[non_exhaustive] Struct { field: u32 }
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// run-pass
|
|
||||||
// aux-build:enums.rs
|
|
||||||
extern crate enums;
|
|
||||||
|
|
||||||
// ignore-pretty issue #37199
|
|
||||||
|
|
||||||
use enums::NonExhaustiveEnum;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let enum_unit = NonExhaustiveEnum::Unit;
|
|
||||||
|
|
||||||
match enum_unit {
|
|
||||||
NonExhaustiveEnum::Unit => 1,
|
|
||||||
NonExhaustiveEnum::Tuple(_) => 2,
|
|
||||||
// This particular arm tests that a enum marked as non-exhaustive
|
|
||||||
// will not error if its variants are matched exhaustively.
|
|
||||||
NonExhaustiveEnum::Struct { field } => field,
|
|
||||||
_ => 0 // no error with wildcard
|
|
||||||
};
|
|
||||||
|
|
||||||
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 } => {},
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// run-pass
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
// aux-build:structs.rs
|
|
||||||
extern crate structs;
|
|
||||||
|
|
||||||
use structs::{NormalStruct, UnitStruct, TupleStruct};
|
|
||||||
|
|
||||||
// We only test matching here as we cannot create non-exhaustive
|
|
||||||
// structs from another crate. ie. they'll never pass in run-pass tests.
|
|
||||||
|
|
||||||
fn match_structs(ns: NormalStruct, ts: TupleStruct, us: UnitStruct) {
|
|
||||||
let NormalStruct { first_field, second_field, .. } = ns;
|
|
||||||
|
|
||||||
let TupleStruct { 0: first, 1: second, .. } = ts;
|
|
||||||
|
|
||||||
let UnitStruct { .. } = us;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() { }
|
|
|
@ -1,22 +0,0 @@
|
||||||
// run-pass
|
|
||||||
// aux-build:variants.rs
|
|
||||||
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_tuple = NonExhaustiveVariants::Tuple { 0: 340 };
|
|
||||||
let variant_struct = NonExhaustiveVariants::Struct { field: 340 };
|
|
||||||
|
|
||||||
match variant_struct {
|
|
||||||
NonExhaustiveVariants::Unit => "",
|
|
||||||
NonExhaustiveVariants::Struct { field, .. } => "",
|
|
||||||
NonExhaustiveVariants::Tuple(fe_tpl, ..) => ""
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -12,4 +12,48 @@ fn main() {
|
||||||
NonExhaustiveEnum::Tuple(_) => "second",
|
NonExhaustiveEnum::Tuple(_) => "second",
|
||||||
NonExhaustiveEnum::Struct { .. } => "third"
|
NonExhaustiveEnum::Struct { .. } => "third"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Everything below this is expected to compile successfully.
|
||||||
|
|
||||||
|
let enum_unit = NonExhaustiveEnum::Unit;
|
||||||
|
|
||||||
|
match enum_unit {
|
||||||
|
NonExhaustiveEnum::Unit => 1,
|
||||||
|
NonExhaustiveEnum::Tuple(_) => 2,
|
||||||
|
// This particular arm tests that a enum marked as non-exhaustive
|
||||||
|
// will not error if its variants are matched exhaustively.
|
||||||
|
NonExhaustiveEnum::Struct { field } => field,
|
||||||
|
_ => 0 // no error with wildcard
|
||||||
|
};
|
||||||
|
|
||||||
|
match enum_unit {
|
||||||
|
_ => "no error with only wildcard"
|
||||||
|
};
|
||||||
|
|
||||||
|
// #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 } => {},
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(non_exhaustive)]
|
#![feature(non_exhaustive)]
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
|
@ -35,3 +35,15 @@ fn main() {
|
||||||
let UnitStruct { } = us;
|
let UnitStruct { } = us;
|
||||||
//~^ ERROR `..` required with struct marked as non-exhaustive
|
//~^ ERROR `..` required with struct marked as non-exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Everything below this is expected to compile successfully.
|
||||||
|
|
||||||
|
// We only test matching here as we cannot create non-exhaustive
|
||||||
|
// structs from another crate. ie. they'll never pass in run-pass tests.
|
||||||
|
fn match_structs(ns: NormalStruct, ts: TupleStruct, us: UnitStruct) {
|
||||||
|
let NormalStruct { first_field, second_field, .. } = ns;
|
||||||
|
|
||||||
|
let TupleStruct { 0: first, 1: second, .. } = ts;
|
||||||
|
|
||||||
|
let UnitStruct { .. } = us;
|
||||||
|
}
|
|
@ -1,29 +1,29 @@
|
||||||
error[E0423]: expected function, found struct `TupleStruct`
|
error[E0423]: expected function, found struct `TupleStruct`
|
||||||
--> $DIR/structs.rs:20:14
|
--> $DIR/struct.rs:20:14
|
||||||
|
|
|
|
||||||
LL | let ts = TupleStruct(640, 480);
|
LL | let ts = TupleStruct(640, 480);
|
||||||
| ^^^^^^^^^^^ constructor is not visible here due to private fields
|
| ^^^^^^^^^^^ constructor is not visible here due to private fields
|
||||||
|
|
||||||
error[E0423]: expected value, found struct `UnitStruct`
|
error[E0423]: expected value, found struct `UnitStruct`
|
||||||
--> $DIR/structs.rs:29:14
|
--> $DIR/struct.rs:29:14
|
||||||
|
|
|
|
||||||
LL | let us = UnitStruct;
|
LL | let us = UnitStruct;
|
||||||
| ^^^^^^^^^^ constructor is not visible here due to private fields
|
| ^^^^^^^^^^ constructor is not visible here due to private fields
|
||||||
|
|
||||||
error[E0603]: tuple struct `TupleStruct` is private
|
error[E0603]: tuple struct `TupleStruct` is private
|
||||||
--> $DIR/structs.rs:23:32
|
--> $DIR/struct.rs:23:32
|
||||||
|
|
|
|
||||||
LL | let ts_explicit = structs::TupleStruct(640, 480);
|
LL | let ts_explicit = structs::TupleStruct(640, 480);
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0603]: unit struct `UnitStruct` is private
|
error[E0603]: unit struct `UnitStruct` is private
|
||||||
--> $DIR/structs.rs:32:32
|
--> $DIR/struct.rs:32:32
|
||||||
|
|
|
|
||||||
LL | let us_explicit = structs::UnitStruct;
|
LL | let us_explicit = structs::UnitStruct;
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
error[E0639]: cannot create non-exhaustive struct using struct expression
|
error[E0639]: cannot create non-exhaustive struct using struct expression
|
||||||
--> $DIR/structs.rs:7:14
|
--> $DIR/struct.rs:7:14
|
||||||
|
|
|
|
||||||
LL | let fr = FunctionalRecord {
|
LL | let fr = FunctionalRecord {
|
||||||
| ______________^
|
| ______________^
|
||||||
|
@ -35,25 +35,25 @@ LL | | };
|
||||||
| |_____^
|
| |_____^
|
||||||
|
|
||||||
error[E0639]: cannot create non-exhaustive struct using struct expression
|
error[E0639]: cannot create non-exhaustive struct using struct expression
|
||||||
--> $DIR/structs.rs:14:14
|
--> $DIR/struct.rs:14:14
|
||||||
|
|
|
|
||||||
LL | let ns = NormalStruct { first_field: 640, second_field: 480 };
|
LL | let ns = NormalStruct { first_field: 640, second_field: 480 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0638]: `..` required with struct marked as non-exhaustive
|
error[E0638]: `..` required with struct marked as non-exhaustive
|
||||||
--> $DIR/structs.rs:17:9
|
--> $DIR/struct.rs:17:9
|
||||||
|
|
|
|
||||||
LL | let NormalStruct { first_field, second_field } = ns;
|
LL | let NormalStruct { first_field, second_field } = ns;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0638]: `..` required with struct marked as non-exhaustive
|
error[E0638]: `..` required with struct marked as non-exhaustive
|
||||||
--> $DIR/structs.rs:26:9
|
--> $DIR/struct.rs:26:9
|
||||||
|
|
|
|
||||||
LL | let TupleStruct { 0: first_field, 1: second_field } = ts;
|
LL | let TupleStruct { 0: first_field, 1: second_field } = ts;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0638]: `..` required with struct marked as non-exhaustive
|
error[E0638]: `..` required with struct marked as non-exhaustive
|
||||||
--> $DIR/structs.rs:35:9
|
--> $DIR/struct.rs:35:9
|
||||||
|
|
|
|
||||||
LL | let UnitStruct { } = us;
|
LL | let UnitStruct { } = us;
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
|
@ -1,4 +1,5 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![feature(non_exhaustive)]
|
#![feature(non_exhaustive)]
|
||||||
|
|
|
@ -1,26 +1,33 @@
|
||||||
// aux-build:variants.rs
|
// aux-build:variants.rs
|
||||||
|
|
||||||
extern crate variants;
|
extern crate variants;
|
||||||
|
|
||||||
use variants::NonExhaustiveVariants;
|
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() {
|
fn main() {
|
||||||
let variant_struct = NonExhaustiveVariants::Struct { field: 640 };
|
let variant_struct = NonExhaustiveVariants::Struct { field: 640 };
|
||||||
//~^ ERROR cannot create non-exhaustive variant
|
//~^ ERROR cannot create non-exhaustive variant
|
||||||
|
|
||||||
let variant_tuple = NonExhaustiveVariants::Tuple { 0: 640 };
|
let variant_tuple = NonExhaustiveVariants::Tuple(640);
|
||||||
//~^ ERROR cannot create non-exhaustive variant
|
//~^ ERROR tuple variant `Tuple` is private [E0603]
|
||||||
|
|
||||||
|
let variant_unit = NonExhaustiveVariants::Unit;
|
||||||
|
//~^ ERROR unit variant `Unit` is private [E0603]
|
||||||
|
|
||||||
match variant_struct {
|
match variant_struct {
|
||||||
NonExhaustiveVariants::Unit => "",
|
NonExhaustiveVariants::Unit => "",
|
||||||
|
//~^ ERROR unit variant `Unit` is private [E0603]
|
||||||
NonExhaustiveVariants::Tuple(fe_tpl) => "",
|
NonExhaustiveVariants::Tuple(fe_tpl) => "",
|
||||||
//~^ ERROR `..` required with variant marked as non-exhaustive
|
//~^ ERROR tuple variant `Tuple` is private [E0603]
|
||||||
NonExhaustiveVariants::Struct { field } => ""
|
NonExhaustiveVariants::Struct { field } => ""
|
||||||
//~^ ERROR `..` required with variant marked as non-exhaustive
|
//~^ ERROR `..` required with variant marked as non-exhaustive
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let NonExhaustiveVariants::Tuple(fe_tpl) = variant_struct {
|
||||||
|
//~^ ERROR tuple variant `Tuple` is private [E0603]
|
||||||
|
}
|
||||||
|
|
||||||
|
if let NonExhaustiveVariants::Struct { field } = variant_struct {
|
||||||
|
//~^ ERROR `..` required with variant marked as non-exhaustive
|
||||||
|
}
|
||||||
}
|
}
|
52
src/test/ui/rfc-2008-non-exhaustive/variant.stderr
Normal file
52
src/test/ui/rfc-2008-non-exhaustive/variant.stderr
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
error[E0603]: tuple variant `Tuple` is private
|
||||||
|
--> $DIR/variant.rs:11:48
|
||||||
|
|
|
||||||
|
LL | let variant_tuple = NonExhaustiveVariants::Tuple(640);
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0603]: unit variant `Unit` is private
|
||||||
|
--> $DIR/variant.rs:14:47
|
||||||
|
|
|
||||||
|
LL | let variant_unit = NonExhaustiveVariants::Unit;
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error[E0603]: unit variant `Unit` is private
|
||||||
|
--> $DIR/variant.rs:18:32
|
||||||
|
|
|
||||||
|
LL | NonExhaustiveVariants::Unit => "",
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error[E0603]: tuple variant `Tuple` is private
|
||||||
|
--> $DIR/variant.rs:20:32
|
||||||
|
|
|
||||||
|
LL | NonExhaustiveVariants::Tuple(fe_tpl) => "",
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0603]: tuple variant `Tuple` is private
|
||||||
|
--> $DIR/variant.rs:26:35
|
||||||
|
|
|
||||||
|
LL | if let NonExhaustiveVariants::Tuple(fe_tpl) = variant_struct {
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0639]: cannot create non-exhaustive variant using struct expression
|
||||||
|
--> $DIR/variant.rs:8:26
|
||||||
|
|
|
||||||
|
LL | let variant_struct = NonExhaustiveVariants::Struct { field: 640 };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0638]: `..` required with variant marked as non-exhaustive
|
||||||
|
--> $DIR/variant.rs:22:9
|
||||||
|
|
|
||||||
|
LL | NonExhaustiveVariants::Struct { field } => ""
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0638]: `..` required with variant marked as non-exhaustive
|
||||||
|
--> $DIR/variant.rs:30:12
|
||||||
|
|
|
||||||
|
LL | if let NonExhaustiveVariants::Struct { field } = variant_struct {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
|
Some errors occurred: E0603, E0638, E0639.
|
||||||
|
For more information about an error, try `rustc --explain E0603`.
|
|
@ -1,17 +0,0 @@
|
||||||
#![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() { }
|
|
|
@ -1,20 +0,0 @@
|
||||||
error: #[non_exhaustive] is not yet supported on variants
|
|
||||||
--> $DIR/variants_create.rs:9:23
|
|
||||||
|
|
|
||||||
LL | #[non_exhaustive] Unit,
|
|
||||||
| ^^^^
|
|
||||||
|
|
||||||
error: #[non_exhaustive] is not yet supported on variants
|
|
||||||
--> $DIR/variants_create.rs:11:23
|
|
||||||
|
|
|
||||||
LL | #[non_exhaustive] Tuple(u32),
|
|
||||||
| ^^^^^^^^^^
|
|
||||||
|
|
||||||
error: #[non_exhaustive] is not yet supported on variants
|
|
||||||
--> $DIR/variants_create.rs:13:23
|
|
||||||
|
|
|
||||||
LL | #[non_exhaustive] Struct { field: u32 }
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
#![feature(non_exhaustive)]
|
|
||||||
|
|
||||||
/*
|
#![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 {
|
pub enum NonExhaustiveVariants {
|
||||||
#[non_exhaustive] Unit,
|
#[non_exhaustive] Unit,
|
Loading…
Add table
Add a link
Reference in a new issue