Skip to content

Commit 3a88cd7

Browse files
committed
Implement #[non_exhaustive] on variants.
This commit removes the check that disallows the `#[non_exhaustive]` attribute from being placed on enum variants and removes the associated tests. Further, this commit lowers the visibility of enum variant constructors when the variant is marked as non-exhaustive.
1 parent 4187560 commit 3a88cd7

File tree

21 files changed

+179
-204
lines changed

21 files changed

+179
-204
lines changed

src/librustc/ty/mod.rs

+7-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 {
@@ -2935,8 +2940,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
29352940
}
29362941
}
29372942

2938-
// Returns `ty::VariantDef` if `def` refers to a struct,
2939-
// 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.
29402945
pub fn expect_variant_def(self, def: Def) -> &'tcx VariantDef {
29412946
match def {
29422947
Def::Variant(did) => {

src/librustc_metadata/encoder.rs

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

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

649654
Entry {
650655
kind: EntryKind::Variant(self.lazy(&data)),
651-
visibility: self.lazy(&ty::Visibility::from_hir(enum_vis, enum_id, tcx)),
656+
visibility: self.lazy(&ctor_vis),
652657
span: self.lazy(&tcx.def_span(def_id)),
653658
attributes: LazySeq::empty(),
654659
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/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.

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

-22
This file was deleted.

src/test/ui/rfc-2008-non-exhaustive/enum.rs

+44
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,48 @@ fn main() {
1212
NonExhaustiveEnum::Tuple(_) => "second",
1313
NonExhaustiveEnum::Struct { .. } => "third"
1414
};
15+
16+
// Everything below this is expected to compile successfully.
17+
18+
let enum_unit = NonExhaustiveEnum::Unit;
19+
20+
match enum_unit {
21+
NonExhaustiveEnum::Unit => 1,
22+
NonExhaustiveEnum::Tuple(_) => 2,
23+
// This particular arm tests that a enum marked as non-exhaustive
24+
// will not error if its variants are matched exhaustively.
25+
NonExhaustiveEnum::Struct { field } => field,
26+
_ => 0 // no error with wildcard
27+
};
28+
29+
match enum_unit {
30+
_ => "no error with only wildcard"
31+
};
32+
33+
// #53549: Check that variant constructors can still be called normally.
34+
match NonExhaustiveEnum::Unit {
35+
NonExhaustiveEnum::Unit => {},
36+
_ => {}
37+
};
38+
39+
match NonExhaustiveEnum::Tuple(2) {
40+
NonExhaustiveEnum::Tuple(2) => {},
41+
_ => {}
42+
};
43+
44+
match (NonExhaustiveEnum::Unit {}) {
45+
NonExhaustiveEnum::Unit {} => {},
46+
_ => {}
47+
};
48+
49+
match (NonExhaustiveEnum::Tuple { 0: 2 }) {
50+
NonExhaustiveEnum::Tuple { 0: 2 } => {},
51+
_ => {}
52+
};
53+
54+
match (NonExhaustiveEnum::Struct { field: 2 }) {
55+
NonExhaustiveEnum::Struct { field: 2 } => {},
56+
_ => {}
57+
};
58+
1559
}

src/test/run-pass/rfcs/rfc-2008-non-exhaustive/enums_same_crate.rs renamed to src/test/ui/rfc-2008-non-exhaustive/enum_same_crate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// run-pass
2+
23
#![feature(non_exhaustive)]
34

45
#[non_exhaustive]

src/test/ui/rfc-2008-non-exhaustive/structs.rs renamed to src/test/ui/rfc-2008-non-exhaustive/struct.rs

+12
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,15 @@ fn main() {
3535
let UnitStruct { } = us;
3636
//~^ ERROR `..` required with struct marked as non-exhaustive
3737
}
38+
39+
// Everything below this is expected to compile successfully.
40+
41+
// We only test matching here as we cannot create non-exhaustive
42+
// structs from another crate. ie. they'll never pass in run-pass tests.
43+
fn match_structs(ns: NormalStruct, ts: TupleStruct, us: UnitStruct) {
44+
let NormalStruct { first_field, second_field, .. } = ns;
45+
46+
let TupleStruct { 0: first, 1: second, .. } = ts;
47+
48+
let UnitStruct { .. } = us;
49+
}

src/test/ui/rfc-2008-non-exhaustive/structs.stderr renamed to src/test/ui/rfc-2008-non-exhaustive/struct.stderr

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
error[E0423]: expected function, found struct `TupleStruct`
2-
--> $DIR/structs.rs:20:14
2+
--> $DIR/struct.rs:20:14
33
|
44
LL | let ts = TupleStruct(640, 480);
55
| ^^^^^^^^^^^ constructor is not visible here due to private fields
66

77
error[E0423]: expected value, found struct `UnitStruct`
8-
--> $DIR/structs.rs:29:14
8+
--> $DIR/struct.rs:29:14
99
|
1010
LL | let us = UnitStruct;
1111
| ^^^^^^^^^^ constructor is not visible here due to private fields
1212

1313
error[E0603]: tuple struct `TupleStruct` is private
14-
--> $DIR/structs.rs:23:32
14+
--> $DIR/struct.rs:23:32
1515
|
1616
LL | let ts_explicit = structs::TupleStruct(640, 480);
1717
| ^^^^^^^^^^^
1818

1919
error[E0603]: unit struct `UnitStruct` is private
20-
--> $DIR/structs.rs:32:32
20+
--> $DIR/struct.rs:32:32
2121
|
2222
LL | let us_explicit = structs::UnitStruct;
2323
| ^^^^^^^^^^
2424

2525
error[E0639]: cannot create non-exhaustive struct using struct expression
26-
--> $DIR/structs.rs:7:14
26+
--> $DIR/struct.rs:7:14
2727
|
2828
LL | let fr = FunctionalRecord {
2929
| ______________^
@@ -35,25 +35,25 @@ LL | | };
3535
| |_____^
3636

3737
error[E0639]: cannot create non-exhaustive struct using struct expression
38-
--> $DIR/structs.rs:14:14
38+
--> $DIR/struct.rs:14:14
3939
|
4040
LL | let ns = NormalStruct { first_field: 640, second_field: 480 };
4141
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4242

4343
error[E0638]: `..` required with struct marked as non-exhaustive
44-
--> $DIR/structs.rs:17:9
44+
--> $DIR/struct.rs:17:9
4545
|
4646
LL | let NormalStruct { first_field, second_field } = ns;
4747
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4848

4949
error[E0638]: `..` required with struct marked as non-exhaustive
50-
--> $DIR/structs.rs:26:9
50+
--> $DIR/struct.rs:26:9
5151
|
5252
LL | let TupleStruct { 0: first_field, 1: second_field } = ts;
5353
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5454

5555
error[E0638]: `..` required with struct marked as non-exhaustive
56-
--> $DIR/structs.rs:35:9
56+
--> $DIR/struct.rs:35:9
5757
|
5858
LL | let UnitStruct { } = us;
5959
| ^^^^^^^^^^^^^^

src/test/run-pass/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs renamed to src/test/ui/rfc-2008-non-exhaustive/structs_same_crate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// run-pass
2+
23
#![allow(unused_variables)]
34
#![feature(non_exhaustive)]
45

0 commit comments

Comments
 (0)