-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute #45394
Conversation
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. |
src/librustc/ty/mod.rs
Outdated
@@ -1371,6 +1371,7 @@ pub struct AdtDef { | |||
pub variants: Vec<VariantDef>, | |||
flags: AdtFlags, | |||
pub repr: ReprOptions, | |||
pub non_exhaustive: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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... =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just pushed up a comment here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: create
-> crate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
fn match_enum(ne: &NormalEnum) -> &str { | ||
match *ne { | ||
//~^ ERROR `..` required with variant marked as non-exhaustive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message is wrong, the enum
is marked as non-exhaustive, not the variant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed this part of the test.
src/librustc_typeck/check/_match.rs
Outdated
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( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this error should have its own error number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
src/librustc_typeck/check/mod.rs
Outdated
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 {}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, but a different new error code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See other comment w/r/t error codes.
tidy failed:
|
@oli-obk Thanks for the comments, I'll get on these ASAP. Update: pushed up changes that fixed most everything mentioned including the tidy. |
src/librustc/diagnostics.rs
Outdated
@@ -1939,6 +1939,65 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { | |||
``` | |||
"##, | |||
|
|||
E0638: r##" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to add this to the diagnostics file of librustc_typeck
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
src/librustc/ty/mod.rs
Outdated
/// fields/variants of this data type. | ||
/// | ||
/// See RFC 2008 (https://github.com/rust-lang/rfcs/pull/2008). | ||
pub non_exhaustive: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is better merged into the flags: AdtFlags
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/librustc_metadata/encoder.rs
Outdated
@@ -648,6 +649,14 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { | |||
} | |||
} | |||
|
|||
if tcx.sess.features.borrow().non_exhaustive { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition seems unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/librustc_privacy/lib.rs
Outdated
@@ -625,6 +625,18 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { | |||
ctor_vis = field_vis; | |||
} | |||
} | |||
|
|||
if self.tcx.sess.features.borrow().non_exhaustive { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary condition again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary condition again, you don't have to check this all the time, one feature check in feature_gate.rs
is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Wasn't aware that I didn't need it everywhere - I misinterpreted the instructions on the Rust forge.
// 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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this doesn't look correct - constructor's visibility below (ctor_vis
) should be lowered to pub(crate)
, not visibility of the struct itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted this.
src/librustc_typeck/check/_match.rs
Outdated
@@ -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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary feature check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After doing this, I noticed a regression in creating the structs - wasn't able to work out what was causing it.
src/librustc_typeck/check/mod.rs
Outdated
@@ -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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary feature check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/librustc_typeck/check/_match.rs
Outdated
@@ -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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted this comment.
src/librustc_typeck/check/mod.rs
Outdated
if self.tcx.sess.features.borrow().non_exhaustive { | ||
let is_local = variant.did.is_local(); | ||
|
||
if !is_local { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the check for non_exhaustive
is forgotten here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added check.
|
||
pub enum FieldEnum { | ||
WithoutFields, | ||
#[non_exhaustive] WithAnonStruct { field: u32 }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since non_exhaustive
on variants is not implemented, could you make it an error? (Currently it's silently ignored.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not gotten around to doing this yet.
pub enum FieldEnum { | ||
WithoutFields, | ||
#[non_exhaustive] WithAnonStruct { field: u32 }, | ||
#[non_exhaustive] WithTuple(u32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 },
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now testing with these enums.
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
// FIXME issue #44109, PR #45394 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This can probably be omitted (also FIXME seems out-of-place).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with other tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// except according to those terms. | ||
|
||
// FIXME issue #44109, PR #45394 | ||
// gate-test-non_exhaustive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with other tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a proper feature gate test.
|
||
// FIXME issue #44109, PR #45394 | ||
// gate-test-non_exhaustive | ||
#![feature(non_exhaustive)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary feature, feature(non_exhaustive)
is required only in crated defining something non_exhaustive
, not using it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with other tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the unnecessary feature usages.
src/librustc_typeck/check/mod.rs
Outdated
|
||
// Prohibit struct expressions when non exhaustive flag is set. | ||
span_err!(self.tcx.sess, expr.span, E0639, | ||
"cannot create non-exhaustive {}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "cannot create non-exhaustive {} using struct expression"
?
I mean you can obtain non-exhaustive structs using other means, e.g. function calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
fn match_struct(us: &UnitStruct) { | ||
let &UnitStruct { } = us; | ||
//~^ ERROR `..` required with struct marked as non-exhaustive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed this. It's a lot easier to keep track of now.
@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. |
src/librustc_const_eval/_match.rs
Outdated
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed this, thanks.
Almost ready, r=me after addressing the last round of comments and squashing commits. |
@petrochenkov That's all your comments resolved. @bors r=petrochenkov |
@davidtwco: 🔑 Insufficient privileges: Not in reviewers |
@bors r+ |
📋 Looks like this PR is still in progress, ignoring approval |
@bors r+ |
📌 Commit d3babe5 has been approved by |
⌛ Testing commit d3babe5 with merge 5fe8cd7aa66c92d1936d8286e6205e24e566aa7c... |
💔 Test failed - status-travis |
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 is a pretty-printer bug (#37199). |
@bors r+ |
📌 Commit 86c62d0 has been approved by |
RFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute This work-in-progress pull request contains my changes to implement [RFC 2008](rust-lang/rfcs#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](https://gitter.im/rust-impl-period/WG-compiler-middle?at=59e90e6297cedeb0482ade3e).
☀️ Test successful - status-appveyor, status-travis |
Just saw that this landed, nice work @davidtwco ! |
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.