Auto merge of #45394 - davidtwco:rfc-2008, r=petrochenkov
RFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute This work-in-progress pull request contains my changes to implement [RFC 2008](https://github.com/rust-lang/rfcs/pull/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).
This commit is contained in:
commit
d762b1d6c6
@ -0,0 +1,75 @@
|
||||
# `non_exhaustive`
|
||||
|
||||
The tracking issue for this feature is: [#44109]
|
||||
|
||||
[#44109]: https://github.com/rust-lang/rust/issues/44109
|
||||
|
||||
------------------------
|
||||
|
||||
The `non_exhaustive` gate allows you to use the `#[non_exhaustive]` attribute
|
||||
on structs and enums. When applied within a crate, users of the crate will need
|
||||
to use the `_` pattern when matching enums and use the `..` pattern when
|
||||
matching structs. Structs marked as `non_exhaustive` will not be able to be
|
||||
created normally outside of the defining crate. This is demonstrated below:
|
||||
|
||||
```rust,ignore (pseudo-Rust)
|
||||
use std::error::Error as StdError;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
Message(String),
|
||||
Other,
|
||||
}
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
// This will not error, despite being marked as non_exhaustive, as this
|
||||
// enum is defined within the current crate, it can be matched
|
||||
// exhaustively.
|
||||
match *self {
|
||||
Message(ref s) => s,
|
||||
Other => "other or unknown error",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```rust,ignore (pseudo-Rust)
|
||||
use mycrate::Error;
|
||||
|
||||
// This will not error as the non_exhaustive Error enum has been matched with
|
||||
// a wildcard.
|
||||
match error {
|
||||
Message(ref s) => ...,
|
||||
Other => ...,
|
||||
_ => ...,
|
||||
}
|
||||
```
|
||||
|
||||
```rust,ignore (pseudo-Rust)
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub window_width: u16,
|
||||
pub window_height: u16,
|
||||
}
|
||||
|
||||
// We can create structs as normal within the defining crate when marked as
|
||||
// non_exhaustive.
|
||||
let config = Config { window_width: 640, window_height: 480 };
|
||||
|
||||
// We can match structs exhaustively when within the defining crate.
|
||||
if let Ok(Config { window_width, window_height }) = load_config() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
```rust,ignore (pseudo-Rust)
|
||||
use mycrate::Config;
|
||||
|
||||
// We cannot create a struct like normal if it has been marked as
|
||||
// non_exhaustive.
|
||||
let config = Config { window_width: 640, window_height: 480 };
|
||||
// By adding the `..` we can match the config as below outside of the crate
|
||||
// when marked non_exhaustive.
|
||||
let &Config { window_width, window_height, .. } = config;
|
||||
```
|
||||
|
@ -1326,6 +1326,12 @@ bitflags! {
|
||||
const IS_FUNDAMENTAL = 1 << 2;
|
||||
const IS_UNION = 1 << 3;
|
||||
const IS_BOX = 1 << 4;
|
||||
/// Indicates whether this abstract data type will be expanded on in future (new
|
||||
/// fields/variants) and as such, whether downstream crates must match exhaustively on the
|
||||
/// fields/variants of this data type.
|
||||
///
|
||||
/// See RFC 2008 (https://github.com/rust-lang/rfcs/pull/2008).
|
||||
const IS_NON_EXHAUSTIVE = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1526,6 +1532,9 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||
if Some(did) == tcx.lang_items().owned_box() {
|
||||
flags = flags | AdtFlags::IS_BOX;
|
||||
}
|
||||
if tcx.has_attr(did, "non_exhaustive") {
|
||||
flags = flags | AdtFlags::IS_NON_EXHAUSTIVE;
|
||||
}
|
||||
match kind {
|
||||
AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM,
|
||||
AdtKind::Union => flags = flags | AdtFlags::IS_UNION,
|
||||
@ -1554,6 +1563,11 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||
self.flags.intersects(AdtFlags::IS_ENUM)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_non_exhaustive(&self) -> bool {
|
||||
self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE)
|
||||
}
|
||||
|
||||
/// Returns the kind of the ADT - Struct or Enum.
|
||||
#[inline]
|
||||
pub fn adt_kind(&self) -> AdtKind {
|
||||
|
@ -208,6 +208,20 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyAdt(adt_def, ..) => adt_def.is_enum() && adt_def.is_non_exhaustive(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_local(&self, ty: Ty<'tcx>) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyAdt(adt_def, ..) => adt_def.did.is_local(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_variant_uninhabited(&self,
|
||||
variant: &'tcx ty::VariantDef,
|
||||
substs: &'tcx ty::subst::Substs<'tcx>)
|
||||
@ -628,9 +642,16 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
|
||||
let is_privately_empty =
|
||||
all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
|
||||
debug!("missing_ctors={:?} is_privately_empty={:?}", missing_ctors,
|
||||
is_privately_empty);
|
||||
if missing_ctors.is_empty() && !is_privately_empty {
|
||||
let is_declared_nonexhaustive =
|
||||
cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
|
||||
debug!("missing_ctors={:?} is_privately_empty={:?} is_declared_nonexhaustive={:?}",
|
||||
missing_ctors, is_privately_empty, is_declared_nonexhaustive);
|
||||
|
||||
// For privately empty and non-exhaustive enums, we work as if there were an "extra"
|
||||
// `_` constructor for the type, so we can never match over all constructors.
|
||||
let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive;
|
||||
|
||||
if missing_ctors.is_empty() && !is_non_exhaustive {
|
||||
all_ctors.into_iter().map(|c| {
|
||||
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
|
||||
}).find(|result| result.is_useful()).unwrap_or(NotUseful)
|
||||
@ -645,7 +666,51 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
match is_useful(cx, &matrix, &v[1..], witness) {
|
||||
UsefulWithWitness(pats) => {
|
||||
let cx = &*cx;
|
||||
let new_witnesses = if used_ctors.is_empty() {
|
||||
// In this case, there's at least one "free"
|
||||
// constructor that is only matched against by
|
||||
// wildcard patterns.
|
||||
//
|
||||
// There are 2 ways we can report a witness here.
|
||||
// Commonly, we can report all the "free"
|
||||
// constructors as witnesses, e.g. if we have:
|
||||
//
|
||||
// ```
|
||||
// enum Direction { N, S, E, W }
|
||||
// let Direction::N = ...;
|
||||
// ```
|
||||
//
|
||||
// we can report 3 witnesses: `S`, `E`, and `W`.
|
||||
//
|
||||
// However, there are 2 cases where we don't want
|
||||
// to do this and instead report a single `_` witness:
|
||||
//
|
||||
// 1) If the user is matching against a non-exhaustive
|
||||
// enum, there is no point in enumerating all possible
|
||||
// variants, because the user can't actually match
|
||||
// against them himself, e.g. in an example like:
|
||||
// ```
|
||||
// let err: io::ErrorKind = ...;
|
||||
// match err {
|
||||
// io::ErrorKind::NotFound => {},
|
||||
// }
|
||||
// ```
|
||||
// we don't want to show every possible IO error,
|
||||
// but instead have `_` as the witness (this is
|
||||
// actually *required* if the user specified *all*
|
||||
// IO errors, but is probably what we want in every
|
||||
// case).
|
||||
//
|
||||
// 2) If the user didn't actually specify a constructor
|
||||
// in this arm, e.g. in
|
||||
// ```
|
||||
// let x: (Direction, Direction, bool) = ...;
|
||||
// let (_, _, false) = x;
|
||||
// ```
|
||||
// we don't want to show all 16 possible witnesses
|
||||
// `(<direction-1>, <direction-2>, true)` - we are
|
||||
// satisfied with `(_, _, true)`. In this case,
|
||||
// `used_ctors` is empty.
|
||||
let new_witnesses = if is_non_exhaustive || used_ctors.is_empty() {
|
||||
// All constructors are unused. Add wild patterns
|
||||
// rather than each individual constructor
|
||||
pats.into_iter().map(|mut witness| {
|
||||
|
@ -584,7 +584,8 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
|
||||
fn encode_struct_ctor(&mut self, (adt_def_id, def_id): (DefId, DefId)) -> Entry<'tcx> {
|
||||
debug!("IsolatedEncoder::encode_struct_ctor({:?})", def_id);
|
||||
let tcx = self.tcx;
|
||||
let variant = tcx.adt_def(adt_def_id).struct_variant();
|
||||
let adt_def = tcx.adt_def(adt_def_id);
|
||||
let variant = adt_def.struct_variant();
|
||||
|
||||
let data = VariantData {
|
||||
ctor_kind: variant.ctor_kind,
|
||||
@ -606,6 +607,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// If the structure is marked as non_exhaustive then lower the visibility
|
||||
// to within the crate.
|
||||
if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
|
||||
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
|
||||
}
|
||||
|
||||
let repr_options = get_repr_options(&tcx, adt_def_id);
|
||||
|
||||
Entry {
|
||||
|
@ -42,6 +42,15 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_non_exhaustive_attribute(&self, variant: &Variant) {
|
||||
let has_non_exhaustive = variant.node.attrs.iter()
|
||||
.any(|attr| attr.check_name("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, span: Span, note: Option<&str>) {
|
||||
if vis != &Visibility::Inherited {
|
||||
let mut err = struct_span_err!(self.session,
|
||||
@ -224,6 +233,7 @@ 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, field.span, None);
|
||||
}
|
||||
|
@ -627,6 +627,16 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
|
||||
ctor_vis = field_vis;
|
||||
}
|
||||
}
|
||||
|
||||
// If the structure is marked as non_exhaustive then lower the
|
||||
// visibility to within the crate.
|
||||
let struct_def_id = self.tcx.hir.get_parent_did(node_id);
|
||||
let adt_def = self.tcx.adt_def(struct_def_id);
|
||||
if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
|
||||
ctor_vis = ty::Visibility::Restricted(
|
||||
DefId::local(CRATE_DEF_INDEX));
|
||||
}
|
||||
|
||||
return ctor_vis;
|
||||
}
|
||||
node => bug!("unexpected node kind: {:?}", node)
|
||||
|
@ -338,11 +338,22 @@ impl<'a> Resolver<'a> {
|
||||
// These items live in both the type and value namespaces.
|
||||
ItemKind::Struct(ref struct_def, _) => {
|
||||
// Define a name in the type namespace.
|
||||
let def = Def::Struct(self.definitions.local_def_id(item.id));
|
||||
let def_id = self.definitions.local_def_id(item.id);
|
||||
let def = Def::Struct(def_id);
|
||||
self.define(parent, ident, TypeNS, (def, vis, sp, expansion));
|
||||
|
||||
// Record field names for error reporting.
|
||||
let mut ctor_vis = vis;
|
||||
|
||||
let has_non_exhaustive = item.attrs.iter()
|
||||
.any(|item| item.check_name("non_exhaustive"));
|
||||
|
||||
// If the structure is marked as non_exhaustive then lower the visibility
|
||||
// to within the crate.
|
||||
if has_non_exhaustive && vis == ty::Visibility::Public {
|
||||
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
|
||||
}
|
||||
|
||||
// Record field names for error reporting.
|
||||
let field_names = struct_def.fields().iter().filter_map(|field| {
|
||||
let field_vis = self.resolve_visibility(&field.vis);
|
||||
if ctor_vis.is_at_least(field_vis, &*self) {
|
||||
@ -414,6 +425,7 @@ impl<'a> Resolver<'a> {
|
||||
// value namespace, they are reserved for possible future use.
|
||||
let ctor_kind = CtorKind::from_ast(&variant.node.data);
|
||||
let ctor_def = Def::VariantCtor(def_id, ctor_kind);
|
||||
|
||||
self.define(parent, ident, ValueNS, (ctor_def, vis, variant.span, expansion));
|
||||
}
|
||||
|
||||
|
@ -825,10 +825,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
def_bm: ty::BindingMode) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let (substs, kind_name) = match adt_ty.sty {
|
||||
ty::TyAdt(adt, substs) => (substs, adt.variant_descr()),
|
||||
let (substs, adt) = match adt_ty.sty {
|
||||
ty::TyAdt(adt, substs) => (substs, adt),
|
||||
_ => span_bug!(span, "struct pattern is not an ADT")
|
||||
};
|
||||
let kind_name = adt.variant_descr();
|
||||
|
||||
// Index the struct fields' types.
|
||||
let field_map = variant.fields
|
||||
@ -882,6 +883,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
self.check_pat_walk(&field.pat, field_ty, def_bm, true);
|
||||
}
|
||||
|
||||
// Require `..` if struct has non_exhaustive attribute.
|
||||
if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc {
|
||||
span_err!(tcx.sess, span, E0638,
|
||||
"`..` required with {} marked as non-exhaustive",
|
||||
kind_name);
|
||||
}
|
||||
|
||||
// Report an error if incorrect number of the fields were specified.
|
||||
if kind_name == "union" {
|
||||
if fields.len() != 1 {
|
||||
|
@ -3448,6 +3448,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
hir::QPath::TypeRelative(ref qself, _) => qself.span
|
||||
};
|
||||
|
||||
// Prohibit struct expressions when non exhaustive flag is set.
|
||||
if let ty::TyAdt(adt, _) = struct_ty.sty {
|
||||
if !adt.did.is_local() && adt.is_non_exhaustive() {
|
||||
span_err!(self.tcx.sess, expr.span, E0639,
|
||||
"cannot create non-exhaustive {} using struct expression",
|
||||
adt.variant_descr());
|
||||
}
|
||||
}
|
||||
|
||||
self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, variant, fields,
|
||||
base_expr.is_none());
|
||||
if let &Some(ref base_expr) = base_expr {
|
||||
|
@ -4606,6 +4606,65 @@ foo.method(); // Ok!
|
||||
```
|
||||
"##,
|
||||
|
||||
E0638: r##"
|
||||
This error indicates that the struct or enum must be matched non-exhaustively
|
||||
as it has been marked as `non_exhaustive`.
|
||||
|
||||
When applied within a crate, downstream users of the crate will need to use the
|
||||
`_` pattern when matching enums and use the `..` pattern when matching structs.
|
||||
|
||||
For example, in the below example, since the enum is marked as
|
||||
`non_exhaustive`, it is required that downstream crates match non-exhaustively
|
||||
on it.
|
||||
|
||||
```rust,ignore (pseudo-Rust)
|
||||
use std::error::Error as StdError;
|
||||
|
||||
#[non_exhaustive] pub enum Error {
|
||||
Message(String),
|
||||
Other,
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
// This will not error, despite being marked as non_exhaustive, as this
|
||||
// enum is defined within the current crate, it can be matched
|
||||
// exhaustively.
|
||||
match *self {
|
||||
Message(ref s) => s,
|
||||
Other => "other or unknown error",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An example of matching non-exhaustively on the above enum is provided below:
|
||||
|
||||
```rust,ignore (pseudo-Rust)
|
||||
use mycrate::Error;
|
||||
|
||||
// This will not error as the non_exhaustive Error enum has been matched with a
|
||||
// wildcard.
|
||||
match error {
|
||||
Message(ref s) => ...,
|
||||
Other => ...,
|
||||
_ => ...,
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, for structs, match with `..` to avoid this error.
|
||||
"##,
|
||||
|
||||
E0639: r##"
|
||||
This error indicates that the struct or enum cannot be instantiated from
|
||||
outside of the defining crate as it has been marked as `non_exhaustive` and as
|
||||
such more fields/variants may be added in future that could cause adverse side
|
||||
effects for this code.
|
||||
|
||||
It is recommended that you look for a `new` function or equivalent in the
|
||||
crate's documentation.
|
||||
"##,
|
||||
|
||||
}
|
||||
|
||||
register_diagnostics! {
|
||||
|
@ -386,6 +386,9 @@ declare_features! (
|
||||
// allow '|' at beginning of match arms (RFC 1925)
|
||||
(active, match_beginning_vert, "1.21.0", Some(44101)),
|
||||
|
||||
// Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008)
|
||||
(active, non_exhaustive, "1.22.0", Some(44109)),
|
||||
|
||||
// Copy/Clone closures (RFC 2132)
|
||||
(active, clone_closures, "1.22.0", Some(44490)),
|
||||
(active, copy_closures, "1.22.0", Some(44490)),
|
||||
@ -614,6 +617,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||
not yet settled",
|
||||
cfg_fn!(structural_match))),
|
||||
|
||||
// RFC #2008
|
||||
("non_exhaustive", Whitelisted, Gated(Stability::Unstable,
|
||||
"non_exhaustive",
|
||||
"non exhaustive is an experimental feature",
|
||||
cfg_fn!(non_exhaustive))),
|
||||
|
||||
("plugin", CrateLevel, Gated(Stability::Unstable,
|
||||
"plugin",
|
||||
"compiler plugins are experimental \
|
||||
|
20
src/test/compile-fail/feature-gate-non_exhaustive.rs
Normal file
20
src/test/compile-fail/feature-gate-non_exhaustive.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive] //~ERROR non exhaustive is an experimental feature (see issue #44109)
|
||||
pub enum NonExhaustiveEnum {
|
||||
Unit,
|
||||
Tuple(u32),
|
||||
Struct { field: u32 }
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive]
|
||||
pub enum NonExhaustiveEnum {
|
||||
Unit,
|
||||
Tuple(u32),
|
||||
Struct { field: u32 }
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct NormalStruct {
|
||||
pub first_field: u16,
|
||||
pub second_field: u16,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct UnitStruct;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct TupleStruct(pub u16, pub u16);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct FunctionalRecord {
|
||||
pub first_field: u16,
|
||||
pub second_field: u16,
|
||||
pub third_field: bool
|
||||
}
|
||||
|
||||
impl Default for FunctionalRecord {
|
||||
fn default() -> FunctionalRecord {
|
||||
FunctionalRecord { first_field: 640, second_field: 480, third_field: false }
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
pub enum NonExhaustiveVariants {
|
||||
#[non_exhaustive] Unit,
|
||||
#[non_exhaustive] Tuple(u32),
|
||||
#[non_exhaustive] Struct { field: u32 }
|
||||
}
|
25
src/test/compile-fail/rfc-2008-non-exhaustive/enum.rs
Normal file
25
src/test/compile-fail/rfc-2008-non-exhaustive/enum.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// aux-build:enums.rs
|
||||
extern crate enums;
|
||||
|
||||
use enums::NonExhaustiveEnum;
|
||||
|
||||
fn main() {
|
||||
let enum_unit = NonExhaustiveEnum::Unit;
|
||||
|
||||
match enum_unit {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered [E0004]
|
||||
NonExhaustiveEnum::Unit => "first",
|
||||
NonExhaustiveEnum::Tuple(_) => "second",
|
||||
NonExhaustiveEnum::Struct { .. } => "third"
|
||||
};
|
||||
}
|
47
src/test/compile-fail/rfc-2008-non-exhaustive/structs.rs
Normal file
47
src/test/compile-fail/rfc-2008-non-exhaustive/structs.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// aux-build:structs.rs
|
||||
extern crate structs;
|
||||
|
||||
use structs::{NormalStruct, UnitStruct, TupleStruct, FunctionalRecord};
|
||||
|
||||
fn main() {
|
||||
let fr = FunctionalRecord {
|
||||
//~^ ERROR cannot create non-exhaustive struct
|
||||
first_field: 1920,
|
||||
second_field: 1080,
|
||||
..FunctionalRecord::default()
|
||||
};
|
||||
|
||||
let ns = NormalStruct { first_field: 640, second_field: 480 };
|
||||
//~^ ERROR cannot create non-exhaustive struct
|
||||
|
||||
let NormalStruct { first_field, second_field } = ns;
|
||||
//~^ ERROR `..` required with struct marked as non-exhaustive
|
||||
|
||||
let ts = TupleStruct(640, 480);
|
||||
//~^ ERROR expected function, found struct `TupleStruct` [E0423]
|
||||
|
||||
let ts_explicit = structs::TupleStruct(640, 480);
|
||||
//~^ ERROR tuple struct `TupleStruct` is private [E0603]
|
||||
|
||||
let TupleStruct { 0: first_field, 1: second_field } = ts;
|
||||
//~^ ERROR `..` required with struct marked as non-exhaustive
|
||||
|
||||
let us = UnitStruct;
|
||||
//~^ ERROR expected value, found struct `UnitStruct` [E0423]
|
||||
|
||||
let us_explicit = structs::UnitStruct;
|
||||
//~^ ERROR unit struct `UnitStruct` is private [E0603]
|
||||
|
||||
let UnitStruct { } = us;
|
||||
//~^ ERROR `..` required with struct marked as non-exhaustive
|
||||
}
|
36
src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs
Normal file
36
src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// aux-build:variants.rs
|
||||
extern crate variants;
|
||||
|
||||
use variants::NonExhaustiveVariants;
|
||||
|
||||
/*
|
||||
* The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for
|
||||
* variants. See issue #44109 and PR 45394.
|
||||
*/
|
||||
// ignore-test
|
||||
|
||||
fn main() {
|
||||
let variant_struct = NonExhaustiveVariants::Struct { field: 640 };
|
||||
//~^ ERROR cannot create non-exhaustive variant
|
||||
|
||||
let variant_tuple = NonExhaustiveVariants::Tuple { 0: 640 };
|
||||
//~^ ERROR cannot create non-exhaustive variant
|
||||
|
||||
match variant_struct {
|
||||
NonExhaustiveVariants::Unit => "",
|
||||
NonExhaustiveVariants::Tuple(fe_tpl) => "",
|
||||
//~^ ERROR `..` required with variant marked as non-exhaustive
|
||||
NonExhaustiveVariants::Struct { field } => ""
|
||||
//~^ ERROR `..` required with variant marked as non-exhaustive
|
||||
};
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
/*
|
||||
* The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for
|
||||
* variants. See issue #44109 and PR 45394.
|
||||
*/
|
||||
|
||||
pub enum NonExhaustiveVariants {
|
||||
#[non_exhaustive] Unit,
|
||||
//~^ ERROR #[non_exhaustive] is not yet supported on variants
|
||||
#[non_exhaustive] Tuple(u32),
|
||||
//~^ ERROR #[non_exhaustive] is not yet supported on variants
|
||||
#[non_exhaustive] Struct { field: u32 }
|
||||
//~^ ERROR #[non_exhaustive] is not yet supported on variants
|
||||
}
|
||||
|
||||
fn main() { }
|
19
src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/enums.rs
Normal file
19
src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/enums.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive]
|
||||
pub enum NonExhaustiveEnum {
|
||||
Unit,
|
||||
Tuple(u32),
|
||||
Struct { field: u32 }
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct NormalStruct {
|
||||
pub first_field: u16,
|
||||
pub second_field: u16,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct UnitStruct;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct TupleStruct (pub u16, pub u16);
|
@ -0,0 +1,18 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
pub enum NonExhaustiveVariants {
|
||||
#[non_exhaustive] Unit,
|
||||
#[non_exhaustive] Tuple(u32),
|
||||
#[non_exhaustive] Struct { field: u32 }
|
||||
}
|
33
src/test/run-pass/rfc-2008-non-exhaustive/enums.rs
Normal file
33
src/test/run-pass/rfc-2008-non-exhaustive/enums.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// aux-build:enums.rs
|
||||
extern crate enums;
|
||||
|
||||
// ignore-pretty issue #37199
|
||||
|
||||
use enums::NonExhaustiveEnum;
|
||||
|
||||
fn main() {
|
||||
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"
|
||||
};
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive]
|
||||
pub enum NonExhaustiveEnum {
|
||||
Unit,
|
||||
Tuple(u32),
|
||||
Struct { field: u32 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let enum_unit = NonExhaustiveEnum::Unit;
|
||||
|
||||
match enum_unit {
|
||||
NonExhaustiveEnum::Unit => "first",
|
||||
NonExhaustiveEnum::Tuple(_) => "second",
|
||||
NonExhaustiveEnum::Struct { .. } => "third",
|
||||
};
|
||||
}
|
27
src/test/run-pass/rfc-2008-non-exhaustive/structs.rs
Normal file
27
src/test/run-pass/rfc-2008-non-exhaustive/structs.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// aux-build:structs.rs
|
||||
extern crate structs;
|
||||
|
||||
use structs::{NormalStruct, UnitStruct, TupleStruct};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,40 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct NormalStruct {
|
||||
pub first_field: u16,
|
||||
pub second_field: u16,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct UnitStruct;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct TupleStruct (pub u16, pub u16);
|
||||
|
||||
fn main() {
|
||||
let ns = NormalStruct { first_field: 640, second_field: 480 };
|
||||
|
||||
let NormalStruct { first_field, second_field } = ns;
|
||||
|
||||
let ts = TupleStruct { 0: 340, 1: 480 };
|
||||
let ts_constructor = TupleStruct(340, 480);
|
||||
|
||||
let TupleStruct { 0: first, 1: second } = ts;
|
||||
let TupleStruct(first, second) = ts_constructor;
|
||||
|
||||
let us = UnitStruct {};
|
||||
let us_constructor = UnitStruct;
|
||||
|
||||
let UnitStruct { } = us;
|
||||
}
|
31
src/test/run-pass/rfc-2008-non-exhaustive/variants.rs
Normal file
31
src/test/run-pass/rfc-2008-non-exhaustive/variants.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// aux-build:variants.rs
|
||||
extern crate variants;
|
||||
|
||||
use variants::NonExhaustiveVariants;
|
||||
|
||||
/*
|
||||
* The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for
|
||||
* variants. See issue #44109 and PR 45394.
|
||||
*/
|
||||
// ignore-test
|
||||
|
||||
fn main() {
|
||||
let variant_tuple = NonExhaustiveVariants::Tuple { 0: 340 };
|
||||
let variant_struct = NonExhaustiveVariants::Struct { field: 340 };
|
||||
|
||||
match variant_struct {
|
||||
NonExhaustiveVariants::Unit => "",
|
||||
NonExhaustiveVariants::Struct { field, .. } => "",
|
||||
NonExhaustiveVariants::Tuple(fe_tpl, ..) => ""
|
||||
};
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(non_exhaustive)]
|
||||
|
||||
/*
|
||||
* The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for
|
||||
* variants. See issue #44109 and PR 45394.
|
||||
*/
|
||||
// ignore-test
|
||||
|
||||
pub enum NonExhaustiveVariants {
|
||||
#[non_exhaustive] Unit,
|
||||
#[non_exhaustive] Tuple(u32),
|
||||
#[non_exhaustive] Struct { field: u32 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let variant_tuple = NonExhaustiveVariants::Tuple(340);
|
||||
let variant_struct = NonExhaustiveVariants::Struct { field: 340 };
|
||||
|
||||
match variant_tuple {
|
||||
NonExhaustiveVariants::Unit => "",
|
||||
NonExhaustiveVariants::Tuple(fe_tpl) => "",
|
||||
NonExhaustiveVariants::Struct { field } => ""
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user