Skip to content

Commit a7ae8b2

Browse files
authored
Rollup merge of rust-lang#59376 - davidtwco:finally-rfc-2008-variants, r=petrochenkov,QuietMisdreavus
RFC 2008: Enum Variants Part of rust-lang#44109. See [Zulip topic](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/rfc-2008/near/132663140) for previous discussion. r? @petrochenkov cc @nikomatsakis
2 parents e782d79 + 49a6da2 commit a7ae8b2

File tree

26 files changed

+219
-216
lines changed

26 files changed

+219
-216
lines changed

src/doc/unstable-book/src/language-features/non-exhaustive.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ The tracking issue for this feature is: [#44109]
77
------------------------
88

99
The `non_exhaustive` gate allows you to use the `#[non_exhaustive]` attribute
10-
on structs and enums. When applied within a crate, users of the crate will need
11-
to use the `_` pattern when matching enums and use the `..` pattern when
12-
matching structs. Structs marked as `non_exhaustive` will not be able to be
13-
created normally outside of the defining crate. This is demonstrated below:
10+
on structs, enums and enum variants. When applied within a crate, users of the
11+
crate will need to use the `_` pattern when matching enums and use the `..`
12+
pattern when matching structs. Enum variants cannot be matched against.
13+
Structs and enum variants marked as `non_exhaustive` will not be able to
14+
be created normally outside of the defining crate. This is demonstrated
15+
below:
1416

1517
```rust,ignore (pseudo-Rust)
1618
use std::error::Error as StdError;
@@ -72,4 +74,3 @@ let config = Config { window_width: 640, window_height: 480 };
7274
// when marked non_exhaustive.
7375
let &Config { window_width, window_height, .. } = config;
7476
```
75-

src/librustc/ty/mod.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,11 @@ impl<'a, 'gcx, 'tcx> VariantDef {
18691869
if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, "non_exhaustive") {
18701870
debug!("found non-exhaustive field list for {:?}", parent_did);
18711871
flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
1872+
} else if let Some(variant_did) = variant_did {
1873+
if tcx.has_attr(variant_did, "non_exhaustive") {
1874+
debug!("found non-exhaustive field list for {:?}", variant_did);
1875+
flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
1876+
}
18721877
}
18731878

18741879
VariantDef {
@@ -2146,6 +2151,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
21462151
debug!("found non-exhaustive variant list for {:?}", did);
21472152
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
21482153
}
2154+
21492155
flags |= match kind {
21502156
AdtKind::Enum => AdtFlags::IS_ENUM,
21512157
AdtKind::Union => AdtFlags::IS_UNION,
@@ -2299,21 +2305,25 @@ impl<'a, 'gcx, 'tcx> AdtDef {
22992305
self.variants.iter().all(|v| v.fields.is_empty())
23002306
}
23012307

2308+
/// Return a `VariantDef` given a variant id.
23022309
pub fn variant_with_id(&self, vid: DefId) -> &VariantDef {
23032310
self.variants.iter().find(|v| v.def_id == vid)
23042311
.expect("variant_with_id: unknown variant")
23052312
}
23062313

2314+
/// Return a `VariantDef` given a constructor id.
23072315
pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef {
23082316
self.variants.iter().find(|v| v.ctor_def_id == Some(cid))
23092317
.expect("variant_with_ctor_id: unknown variant")
23102318
}
23112319

2320+
/// Return the index of `VariantDef` given a variant id.
23122321
pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx {
23132322
self.variants.iter_enumerated().find(|(_, v)| v.def_id == vid)
23142323
.expect("variant_index_with_id: unknown variant").0
23152324
}
23162325

2326+
/// Return the index of `VariantDef` given a constructor id.
23172327
pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx {
23182328
self.variants.iter_enumerated().find(|(_, v)| v.ctor_def_id == Some(cid))
23192329
.expect("variant_index_with_ctor_id: unknown variant").0
@@ -2930,8 +2940,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
29302940
}
29312941
}
29322942

2933-
// Returns `ty::VariantDef` if `def` refers to a struct,
2934-
// or variant or their constructors, panics otherwise.
2943+
/// Returns `ty::VariantDef` if `def` refers to a struct,
2944+
/// or variant or their constructors, panics otherwise.
29352945
pub fn expect_variant_def(self, def: Def) -> &'tcx VariantDef {
29362946
match def {
29372947
Def::Variant(did) => {

src/librustc_metadata/encoder.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -643,13 +643,18 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
643643
}
644644
};
645645

646-
// Variant constructors have the same visibility as the parent enums.
646+
// Variant constructors have the same visibility as the parent enums, unless marked as
647+
// non-exhaustive, in which case they are lowered to `pub(crate)`.
647648
let enum_id = tcx.hir().as_local_hir_id(enum_did).unwrap();
648649
let enum_vis = &tcx.hir().expect_item_by_hir_id(enum_id).vis;
650+
let mut ctor_vis = ty::Visibility::from_hir(enum_vis, enum_id, tcx);
651+
if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public {
652+
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
653+
}
649654

650655
Entry {
651656
kind: EntryKind::Variant(self.lazy(&data)),
652-
visibility: self.lazy(&ty::Visibility::from_hir(enum_vis, enum_id, tcx)),
657+
visibility: self.lazy(&ctor_vis),
653658
span: self.lazy(&tcx.def_span(def_id)),
654659
attributes: LazySeq::empty(),
655660
children: LazySeq::empty(),

src/librustc_passes/ast_validation.rs

-9
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,6 @@ impl<'a> AstValidator<'a> {
192192
}
193193
}
194194

195-
fn invalid_non_exhaustive_attribute(&self, variant: &Variant) {
196-
let has_non_exhaustive = attr::contains_name(&variant.node.attrs, "non_exhaustive");
197-
if has_non_exhaustive {
198-
self.err_handler().span_err(variant.span,
199-
"#[non_exhaustive] is not yet supported on variants");
200-
}
201-
}
202-
203195
fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
204196
if let VisibilityKind::Inherited = vis.node {
205197
return
@@ -608,7 +600,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
608600
}
609601
ItemKind::Enum(ref def, _) => {
610602
for variant in &def.variants {
611-
self.invalid_non_exhaustive_attribute(variant);
612603
for field in variant.node.data.fields() {
613604
self.invalid_visibility(&field.vis, None);
614605
}

src/librustc_privacy/lib.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,26 @@ fn def_id_visibility<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
244244
match tcx.hir().get_by_hir_id(parent_hir_id) {
245245
Node::Variant(..) => {
246246
let parent_did = tcx.hir().local_def_id_from_hir_id(parent_hir_id);
247-
return def_id_visibility(tcx, parent_did);
247+
let (mut ctor_vis, mut span, mut descr) = def_id_visibility(
248+
tcx, parent_did,
249+
);
250+
251+
let adt_def = tcx.adt_def(tcx.hir().get_parent_did_by_hir_id(hir_id));
252+
let ctor_did = tcx.hir().local_def_id_from_hir_id(
253+
vdata.ctor_hir_id().unwrap());
254+
let variant = adt_def.variant_with_ctor_id(ctor_did);
255+
256+
if variant.is_field_list_non_exhaustive() &&
257+
ctor_vis == ty::Visibility::Public
258+
{
259+
ctor_vis = ty::Visibility::Restricted(
260+
DefId::local(CRATE_DEF_INDEX));
261+
let attrs = tcx.get_attrs(variant.def_id);
262+
span = attr::find_by_name(&attrs, "non_exhaustive").unwrap().span;
263+
descr = "crate-visible";
264+
}
265+
266+
return (ctor_vis, span, descr);
248267
}
249268
Node::Item(..) => {
250269
let item = match tcx.hir().get_by_hir_id(parent_hir_id) {

src/librustc_resolve/build_reduced_graph.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,14 @@ impl<'a> Resolver<'a> {
588588
let def = Def::Variant(def_id);
589589
self.define(parent, ident, TypeNS, (def, vis, variant.span, expansion));
590590

591+
// If the variant is marked as non_exhaustive then lower the visibility to within the
592+
// crate.
593+
let mut ctor_vis = vis;
594+
let has_non_exhaustive = attr::contains_name(&variant.node.attrs, "non_exhaustive");
595+
if has_non_exhaustive && vis == ty::Visibility::Public {
596+
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
597+
}
598+
591599
// Define a constructor name in the value namespace.
592600
// Braced variants, unlike structs, generate unusable names in
593601
// value namespace, they are reserved for possible future use.
@@ -597,7 +605,7 @@ impl<'a> Resolver<'a> {
597605
let ctor_def_id = self.definitions.local_def_id(ctor_node_id);
598606
let ctor_kind = CtorKind::from_ast(&variant.node.data);
599607
let ctor_def = Def::Ctor(ctor_def_id, CtorOf::Variant, ctor_kind);
600-
self.define(parent, ident, ValueNS, (ctor_def, vis, variant.span, expansion));
608+
self.define(parent, ident, ValueNS, (ctor_def, ctor_vis, variant.span, expansion));
601609
}
602610

603611
/// Constructs the reduced graph for one foreign item.

src/librustc_typeck/diagnostics.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -4341,11 +4341,12 @@ foo.method(); // Ok!
43414341
"##,
43424342

43434343
E0638: r##"
4344-
This error indicates that the struct or enum must be matched non-exhaustively
4345-
as it has been marked as `non_exhaustive`.
4344+
This error indicates that the struct, enum or enum variant must be matched
4345+
non-exhaustively as it has been marked as `non_exhaustive`.
43464346
43474347
When applied within a crate, downstream users of the crate will need to use the
43484348
`_` pattern when matching enums and use the `..` pattern when matching structs.
4349+
Downstream crates cannot match against non-exhaustive enum variants.
43494350
43504351
For example, in the below example, since the enum is marked as
43514352
`non_exhaustive`, it is required that downstream crates match non-exhaustively
@@ -4390,10 +4391,10 @@ Similarly, for structs, match with `..` to avoid this error.
43904391
"##,
43914392

43924393
E0639: r##"
4393-
This error indicates that the struct or enum cannot be instantiated from
4394-
outside of the defining crate as it has been marked as `non_exhaustive` and as
4395-
such more fields/variants may be added in future that could cause adverse side
4396-
effects for this code.
4394+
This error indicates that the struct, enum or enum variant cannot be
4395+
instantiated from outside of the defining crate as it has been marked
4396+
as `non_exhaustive` and as such more fields/variants may be added in
4397+
future that could cause adverse side effects for this code.
43974398
43984399
It is recommended that you look for a `new` function or equivalent in the
43994400
crate's documentation.

src/librustdoc/clean/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,9 @@ impl Item {
421421
pub fn is_enum(&self) -> bool {
422422
self.type_() == ItemType::Enum
423423
}
424+
pub fn is_variant(&self) -> bool {
425+
self.type_() == ItemType::Variant
426+
}
424427
pub fn is_associated_type(&self) -> bool {
425428
self.type_() == ItemType::AssociatedType
426429
}

src/librustdoc/html/render.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -2604,7 +2604,15 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
26042604
fn document_non_exhaustive(w: &mut fmt::Formatter<'_>, item: &clean::Item) -> fmt::Result {
26052605
if item.is_non_exhaustive() {
26062606
write!(w, "<div class='docblock non-exhaustive non-exhaustive-{}'>", {
2607-
if item.is_struct() { "struct" } else if item.is_enum() { "enum" } else { "type" }
2607+
if item.is_struct() {
2608+
"struct"
2609+
} else if item.is_enum() {
2610+
"enum"
2611+
} else if item.is_variant() {
2612+
"variant"
2613+
} else {
2614+
"type"
2615+
}
26082616
})?;
26092617

26102618
if item.is_struct() {
@@ -2617,6 +2625,10 @@ fn document_non_exhaustive(w: &mut fmt::Formatter<'_>, item: &clean::Item) -> fm
26172625
write!(w, "Non-exhaustive enums could have additional variants added in future. \
26182626
Therefore, when matching against variants of non-exhaustive enums, an \
26192627
extra wildcard arm must be added to account for any future variants.")?;
2628+
} else if item.is_variant() {
2629+
write!(w, "Non-exhaustive enum variants could have additional fields added in future. \
2630+
Therefore, non-exhaustive enum variants cannot be constructed in external \
2631+
crates and cannot be matched against.")?;
26202632
} else {
26212633
write!(w, "This type will require a wildcard arm in any match statements or \
26222634
constructors.")?;
@@ -3679,6 +3691,7 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
36793691
}
36803692
write!(w, "</code></span>")?;
36813693
document(w, cx, variant)?;
3694+
document_non_exhaustive(w, variant)?;
36823695

36833696
use crate::clean::{Variant, VariantKind};
36843697
if let clean::VariantItem(Variant {

src/librustdoc/html/static/main.js

+5
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,8 @@ if (!DOMTokenList.prototype.remove) {
22632263
otherMessage += "struct";
22642264
} else if (hasClass(e, "non-exhaustive-enum")) {
22652265
otherMessage += "enum";
2266+
} else if (hasClass(e, "non-exhaustive-variant")) {
2267+
otherMessage += "enum variant";
22662268
} else if (hasClass(e, "non-exhaustive-type")) {
22672269
otherMessage += "type";
22682270
}
@@ -2280,6 +2282,9 @@ if (!DOMTokenList.prototype.remove) {
22802282
if (hasClass(e, "type-decl") === true && showItemDeclarations === true) {
22812283
collapseDocs(e.previousSibling.childNodes[0], "toggle");
22822284
}
2285+
if (hasClass(e, "non-exhaustive") === true) {
2286+
collapseDocs(e.previousSibling.childNodes[0], "toggle");
2287+
}
22832288
}
22842289
}
22852290

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

-10
This file was deleted.

src/test/run-pass/rfcs/rfc-2008-non-exhaustive/auxiliary/structs.rs

-14
This file was deleted.

src/test/run-pass/rfcs/rfc-2008-non-exhaustive/auxiliary/variants.rs

-9
This file was deleted.

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

-53
This file was deleted.

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

-20
This file was deleted.

0 commit comments

Comments
 (0)