Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute #45394
Conversation
rust-highfive
assigned
steveklabnik
Oct 19, 2017
This comment has been minimized.
This comment has been minimized.
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @steveklabnik (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
This comment has been minimized.
This comment has been minimized.
rust-highfive
assigned
nikomatsakis
and unassigned
steveklabnik
Oct 19, 2017
petrochenkov
self-assigned this
Oct 19, 2017
nikomatsakis
reviewed
Oct 20, 2017
| @@ -1371,6 +1371,7 @@ pub struct AdtDef { | |||
| pub variants: Vec<VariantDef>, | |||
| flags: AdtFlags, | |||
| pub repr: ReprOptions, | |||
| pub non_exhaustive: bool, | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Oct 20, 2017
Contributor
Can you add a comment briefly explaining what this is, perhaps linking to the RFC? I know none of the other fields have comments, but that's no reason not to do better... =)
This comment has been minimized.
This comment has been minimized.
davidtwco
Oct 20, 2017
Author
Member
I definitely will, I plan on doing another pass to make sure everything is well commented, just wanted to get that pushed up before I called it a night. Will do this ASAP.
This comment has been minimized.
This comment has been minimized.
oli-obk
requested changes
Oct 20, 2017
|
All the error-cases have a trivial solution. I think it would be cool if we also produced structured suggestions showing how to fix it |
| use enums::FieldEnum; | ||
|
|
||
| // This errors as we cannot create a enum variant marked non_exhaustive | ||
| // from an external crate as if the create added new fields it would be |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
|
||
| fn match_enum(ne: &NormalEnum) -> &str { | ||
| match *ne { | ||
| //~^ ERROR `..` required with variant marked as non-exhaustive |
This comment has been minimized.
This comment has been minimized.
oli-obk
Oct 20, 2017
Contributor
The error message is wrong, the enum is marked as non-exhaustive, not the variant
This comment has been minimized.
This comment has been minimized.
| if tcx.sess.features.borrow().non_exhaustive { | ||
| // Require `..` if structure or enum has non_exhaustive attribute. | ||
| if adt.non_exhaustive && !adt.did.is_local() && !etc { | ||
| tcx.sess.span_err( |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
davidtwco
Oct 20, 2017
Author
Member
I've added error numbers in a WIP commit that I couldn't get compiling locally - messages regarding the diagnostic code not being registered despite my not being able to spot a difference in how they are being added. Would appreciate any thoughts here.
| let descr = self.tcx.adt_def(variant.did).variant_descr(); | ||
|
|
||
| // Prohibit struct expressions when non exhaustive flag is set. | ||
| self.tcx.sess.span_err(expr.span, &format!("cannot create non-exhaustive {}", |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
tidy failed:
|
This comment has been minimized.
This comment has been minimized.
|
@oli-obk Thanks for the comments, I'll get on these ASAP. Update: pushed up changes that fixed most everything mentioned including the tidy. |
kennytm
added
the
S-waiting-on-author
label
Oct 20, 2017
oli-obk
reviewed
Oct 21, 2017
| @@ -1939,6 +1939,65 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { | |||
| ``` | |||
| "##, | |||
|
|
|||
| E0638: r##" | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
davidtwco
Oct 21, 2017
Author
Member
Thanks, I've pushed up a fix for this. I wasn't aware that there was a diagnostics file in the other crates, my bad.
petrochenkov
reviewed
Oct 22, 2017
| /// fields/variants of this data type. | ||
| /// | ||
| /// See RFC 2008 (https://github.com/rust-lang/rfcs/pull/2008). | ||
| pub non_exhaustive: bool, |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| @@ -648,6 +649,14 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { | |||
| } | |||
| } | |||
|
|
|||
| if tcx.sess.features.borrow().non_exhaustive { | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| @@ -625,6 +625,18 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { | |||
| ctor_vis = field_vis; | |||
| } | |||
| } | |||
|
|
|||
| if self.tcx.sess.features.borrow().non_exhaustive { | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| @@ -337,8 +337,20 @@ impl<'a> Resolver<'a> { | |||
|
|
|||
| // These items live in both the type and value namespaces. | |||
| ItemKind::Struct(ref struct_def, _) => { | |||
| if self.session.features.borrow().non_exhaustive { | |||
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Unnecessary condition again, you don't have to check this all the time, one feature check in feature_gate.rs is enough.
This comment has been minimized.
This comment has been minimized.
davidtwco
Oct 22, 2017
Author
Member
Fixed. Wasn't aware that I didn't need it everywhere - I misinterpreted the instructions on the Rust forge.
petrochenkov
reviewed
Oct 22, 2017
| // If the structure is marked as non_exhaustive then lower the visibility | ||
| // to within the crate. | ||
| if has_non_exhaustive && vis == ty::Visibility::Public { | ||
| vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Hmm, this doesn't look correct - constructor's visibility below (ctor_vis) should be lowered to pub(crate), not visibility of the struct itself.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| @@ -867,6 +868,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { | |||
| self.check_pat_walk(&field.pat, field_ty, def_bm, true); | |||
| } | |||
|
|
|||
| if tcx.sess.features.borrow().non_exhaustive { | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
davidtwco
Oct 22, 2017
Author
Member
After doing this, I noticed a regression in creating the structs - wasn't able to work out what was causing it.
petrochenkov
reviewed
Oct 22, 2017
| @@ -3408,6 +3408,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { | |||
| return self.tcx.types.err; | |||
| }; | |||
|
|
|||
| if self.tcx.sess.features.borrow().non_exhaustive { | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| @@ -867,6 +868,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { | |||
| self.check_pat_walk(&field.pat, field_ty, def_bm, true); | |||
| } | |||
|
|
|||
| if tcx.sess.features.borrow().non_exhaustive { | |||
| // Require `..` if structure or enum has non_exhaustive attribute. | |||
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Require .. if structure or variant has the non_exhaustive attribute.
It looks like this patch doesn't implement non_exhaustive for variants yet, but non_exhaustive on enum should not restrict use of its variants.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| if self.tcx.sess.features.borrow().non_exhaustive { | ||
| let is_local = variant.did.is_local(); | ||
|
|
||
| if !is_local { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Also see https://github.com/rust-lang/rust/pull/45394/files#r146130454, non_exhaustive on enum should not prohibit struct expressions using that enum's variants.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
|
|
||
| pub enum FieldEnum { | ||
| WithoutFields, | ||
| #[non_exhaustive] WithAnonStruct { field: u32 }, |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Since non_exhaustive on variants is not implemented, could you make it an error? (Currently it's silently ignored.)
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| pub enum FieldEnum { | ||
| WithoutFields, | ||
| #[non_exhaustive] WithAnonStruct { field: u32 }, | ||
| #[non_exhaustive] WithTuple(u32) |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
I'd like to see all of these configurations tested:
#[non_exhaustive]
enum NonExhaustiveEnum {
Unit,
Tuple(u8),
Struct { field: u8 },
}
enum NonExhaustiveVariants {
#[non_exhaustive] Unit,
#[non_exhaustive] Tuple(u8),
#[non_exhaustive] Struct { field: u8 },
}
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| // option. This file may not be copied, modified, or distributed | ||
| // except according to those terms. | ||
|
|
||
| // FIXME issue #44109, PR #45394 |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Nit: This can probably be omitted (also FIXME seems out-of-place).
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
| // except according to those terms. | ||
|
|
||
| // FIXME issue #44109, PR #45394 | ||
| // gate-test-non_exhaustive |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
This marker is used for feature gate tests, but this test is not a feature gate test.
Also, could you actually add a feature gate test? (Making sure a feature error is reported in #[non_exhaustive] is used without feature(non_exhaustive)).
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
|
|
||
| // FIXME issue #44109, PR #45394 | ||
| // gate-test-non_exhaustive | ||
| #![feature(non_exhaustive)] |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Unnecessary feature, feature(non_exhaustive) is required only in crated defining something non_exhaustive, not using it.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
|
|
||
| // Prohibit struct expressions when non exhaustive flag is set. | ||
| span_err!(self.tcx.sess, expr.span, E0639, | ||
| "cannot create non-exhaustive {}", |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
Nit: "cannot create non-exhaustive {} using struct expression"?
I mean you can obtain non-exhaustive structs using other means, e.g. function calls.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Oct 22, 2017
|
|
||
| fn match_struct(us: &UnitStruct) { | ||
| let &UnitStruct { } = us; | ||
| //~^ ERROR `..` required with struct marked as non-exhaustive |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Oct 22, 2017
Contributor
You can merge several test files testing similar things into one file with several //~ ERROR markers, it's usually more convenient that way (both for writing better more exhaustive tests, and for reviewing them).
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@petrochenkov Thanks a bunch for the comments. Just fixed a bunch of them and I think there's one that I'm still working to resolve. |
arielb1
reviewed
Oct 26, 2017
| all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); | ||
| debug!("missing_ctors={:?} is_privately_empty={:?}", missing_ctors, | ||
| is_privately_empty); | ||
|
|
||
| if cx.is_non_exhaustive(pcx.ty) && !cx.is_local(pcx.ty) { |
This comment has been minimized.
This comment has been minimized.
arielb1
Oct 26, 2017
•
Contributor
It's confusing to mutate this variable, especially after the debug print
Maybe have
let is_privately_empty =
all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
let is_declared_nonexhaustive =
cx.is_non_exhaustive(pcx.ty) && !cx.is_local(pcx.ty);
// For privately empty and nonexhaustive enums, we work as
// if there were an "extra" `_` constructor for the type, so we
// can never match over all constructors.
let is_nonexhaustive =
is_privately_empty || is_declared_nonexhaustive;
if missing_ctors.is_empty() && !is_nonexhaustive {
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Nov 3, 2017
| ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); | ||
| } | ||
|
|
||
| self.define(parent, ident, ValueNS, (ctor_def, ctor_vis, variant.span, expansion)); |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 3, 2017
Contributor
Since we don't support variants yet, this change can be removed.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@davidtwco |
This comment has been minimized.
This comment has been minimized.
|
Almost ready, r=me after addressing the last round of comments and squashing commits. |
This comment has been minimized.
This comment has been minimized.
|
@petrochenkov That's all your comments resolved. @bors r=petrochenkov |
This comment has been minimized.
This comment has been minimized.
|
@davidtwco: |
davidtwco
added some commits
Nov 3, 2017
This comment has been minimized.
This comment has been minimized.
|
@bors r+ |
This comment has been minimized.
This comment has been minimized.
|
|
petrochenkov
changed the title
[WIP] RFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute
RFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute
Nov 3, 2017
This comment has been minimized.
This comment has been minimized.
|
@bors r+ |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
bors
added a commit
that referenced
this pull request
Nov 4, 2017
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
Diff (a single empty line --- expected
+++ actual
@@ -20,6 +20,7 @@
[01:16:34] NonExhaustiveEnum::Unit => 1,
[01:16:34] NonExhaustiveEnum::Tuple(_) => 2,
[01:16:34]
+[01:16:34]
[01:16:34] // This particular arm tests that a enum marked as non-exhaustive
[01:16:34] // will not error if its variants are matched exhaustively.
[01:16:34] NonExhaustiveEnum::Struct { field } => field, |
This comment has been minimized.
This comment has been minimized.
|
This is a pretty-printer bug (#37199). |
This comment has been minimized.
This comment has been minimized.
|
@bors r+ |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
bors
added a commit
that referenced
this pull request
Nov 4, 2017
This comment has been minimized.
This comment has been minimized.
|
|
bors
merged commit 86c62d0
into
rust-lang:master
Nov 4, 2017
davidtwco
deleted the
davidtwco:rfc-2008
branch
Nov 4, 2017
This comment has been minimized.
This comment has been minimized.
|
Just saw that this landed, nice work @davidtwco ! |
davidtwco commentedOct 19, 2017
•
edited
This work-in-progress pull request contains my changes to implement RFC 2008. The related tracking issue is #44109.
As of writing, enum-related functionality is not included and there are some issues related to tuple/unit structs. Enum related tests are currently ignored.
WIP PR requested by @nikomatsakis in Gitter.