Skip to content

Commit

Permalink
Implement #[non_exhaustive] on variants.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davidtwco committed Mar 28, 2019
1 parent 4187560 commit 3a88cd7
Show file tree
Hide file tree
Showing 21 changed files with 179 additions and 204 deletions.
9 changes: 7 additions & 2 deletions src/librustc/ty/mod.rs
Expand Up @@ -1869,6 +1869,11 @@ impl<'a, 'gcx, 'tcx> VariantDef {
if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, "non_exhaustive") {
debug!("found non-exhaustive field list for {:?}", parent_did);
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 {
Expand Down Expand Up @@ -2935,8 +2940,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
}

// Returns `ty::VariantDef` if `def` refers to a struct,
// or variant or their constructors, panics otherwise.
/// Returns `ty::VariantDef` if `def` refers to a struct,
/// or variant or their constructors, panics otherwise.
pub fn expect_variant_def(self, def: Def) -> &'tcx VariantDef {
match def {
Def::Variant(did) => {
Expand Down
9 changes: 7 additions & 2 deletions src/librustc_metadata/encoder.rs
Expand Up @@ -642,13 +642,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_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 {
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)),
attributes: LazySeq::empty(),
children: LazySeq::empty(),
Expand Down
9 changes: 0 additions & 9 deletions src/librustc_passes/ast_validation.rs
Expand Up @@ -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>) {
if let VisibilityKind::Inherited = vis.node {
return
Expand Down Expand Up @@ -608,7 +600,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);
}
Expand Down
21 changes: 20 additions & 1 deletion src/librustc_privacy/lib.rs
Expand Up @@ -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) {
Node::Variant(..) => {
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(..) => {
let item = match tcx.hir().get_by_hir_id(parent_hir_id) {
Expand Down
10 changes: 9 additions & 1 deletion src/librustc_resolve/build_reduced_graph.rs
Expand Up @@ -588,6 +588,14 @@ impl<'a> Resolver<'a> {
let def = Def::Variant(def_id);
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.
// Braced variants, unlike structs, generate unusable names in
// value namespace, they are reserved for possible future use.
Expand All @@ -597,7 +605,7 @@ impl<'a> Resolver<'a> {
let ctor_def_id = self.definitions.local_def_id(ctor_node_id);
let ctor_kind = CtorKind::from_ast(&variant.node.data);
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.
Expand Down
10 changes: 0 additions & 10 deletions src/test/run-pass/rfcs/rfc-2008-non-exhaustive/auxiliary/enums.rs

This file was deleted.

This file was deleted.

This file was deleted.

53 changes: 0 additions & 53 deletions src/test/run-pass/rfcs/rfc-2008-non-exhaustive/enums.rs

This file was deleted.

20 changes: 0 additions & 20 deletions src/test/run-pass/rfcs/rfc-2008-non-exhaustive/structs.rs

This file was deleted.

22 changes: 0 additions & 22 deletions src/test/run-pass/rfcs/rfc-2008-non-exhaustive/variants.rs

This file was deleted.

44 changes: 44 additions & 0 deletions src/test/ui/rfc-2008-non-exhaustive/enum.rs
Expand Up @@ -12,4 +12,48 @@ fn main() {
NonExhaustiveEnum::Tuple(_) => "second",
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

#![feature(non_exhaustive)]

#[non_exhaustive]
Expand Down
Expand Up @@ -35,3 +35,15 @@ fn main() {
let UnitStruct { } = us;
//~^ 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`
--> $DIR/structs.rs:20:14
--> $DIR/struct.rs:20:14
|
LL | let ts = TupleStruct(640, 480);
| ^^^^^^^^^^^ constructor is not visible here due to private fields

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

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);
| ^^^^^^^^^^^

error[E0603]: unit struct `UnitStruct` is private
--> $DIR/structs.rs:32:32
--> $DIR/struct.rs:32:32
|
LL | let us_explicit = structs::UnitStruct;
| ^^^^^^^^^^

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

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 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0638]: `..` required with struct marked as non-exhaustive
--> $DIR/structs.rs:35:9
--> $DIR/struct.rs:35:9
|
LL | let UnitStruct { } = us;
| ^^^^^^^^^^^^^^
Expand Down
@@ -1,4 +1,5 @@
// run-pass

#![allow(unused_variables)]
#![feature(non_exhaustive)]

Expand Down

0 comments on commit 3a88cd7

Please sign in to comment.