Skip to content

Implement RFC 1506 "Clarify the relationships between various kinds of structs and variants" #35138

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

Merged
merged 2 commits into from
Aug 13, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
@@ -197,27 +197,6 @@ impl<'a> Visitor for AstValidator<'a> {
visit::walk_foreign_item(self, fi)
}

fn visit_variant_data(&mut self,
vdata: &VariantData,
_: Ident,
_: &Generics,
_: NodeId,
span: Span) {
if vdata.fields().is_empty() {
if vdata.is_tuple() {
self.err_handler()
.struct_span_err(span,
"empty tuple structs and enum variants are not allowed, use \
unit structs and enum variants instead")
.span_help(span,
"remove trailing `()` to make a unit struct or unit enum variant")
.emit();
}
}

visit::walk_struct_def(self, vdata)
}

fn visit_vis(&mut self, vis: &Visibility) {
match *vis {
Visibility::Restricted { ref path, .. } => {
27 changes: 19 additions & 8 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
@@ -116,6 +116,7 @@ use syntax::ast;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::{self, Spanned};
use syntax::feature_gate::{GateIssue, emit_feature_err};
use syntax::parse::token::{self, InternedString, keywords};
use syntax::ptr::P;
use syntax::util::lev_distance::find_best_match_for_name;
@@ -1700,7 +1701,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
node_id: ast::NodeId)
-> Ty<'tcx> {
debug!("instantiate_type_path(did={:?}, path={:?})", did, path);
let type_scheme = self.tcx.lookup_item_type(did);
let mut type_scheme = self.tcx.lookup_item_type(did);
if type_scheme.ty.is_fn() {
// Tuple variants have fn type even in type namespace, extract true variant type from it
let fn_ret = self.tcx.no_late_bound_regions(&type_scheme.ty.fn_ret()).unwrap().unwrap();
type_scheme = ty::TypeScheme { ty: fn_ret, generics: type_scheme.generics }
}
let type_predicates = self.tcx.lookup_predicates(did);
let substs = AstConv::ast_path_substs_for_ty(self, self,
path.span,
@@ -3244,19 +3250,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
_ => None
};
if variant.is_none() || variant.unwrap().kind == ty::VariantKind::Tuple {
// Reject tuple structs for now, braced and unit structs are allowed.

if let Some(variant) = variant {
if variant.kind == ty::VariantKind::Tuple &&
!self.tcx.sess.features.borrow().relaxed_adts {
emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic,
"relaxed_adts", span, GateIssue::Language,
"tuple structs and variants in struct patterns are unstable");
}
let ty = self.instantiate_type_path(def.def_id(), path, node_id);
Some((variant, ty))
} else {
struct_span_err!(self.tcx.sess, path.span, E0071,
"`{}` does not name a struct or a struct variant",
pprust::path_to_string(path))
.span_label(path.span, &format!("not a struct"))
.emit();

return None;
None
}

let ty = self.instantiate_type_path(def.def_id(), path, node_id);
Some((variant.unwrap(), ty))
}

fn check_expr_struct(&self,
24 changes: 20 additions & 4 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -280,7 +280,11 @@ declare_features! (
(active, dotdot_in_tuple_patterns, "1.10.0", Some(33627)),

// Allows `impl Trait` in function return types.
(active, conservative_impl_trait, "1.12.0", Some(34511))
(active, conservative_impl_trait, "1.12.0", Some(34511)),

// Allows tuple structs and variants in more contexts,
// Permits numeric fields in struct expressions and patterns.
(active, relaxed_adts, "1.12.0", Some(35626))
);

declare_features! (
@@ -1022,9 +1026,8 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
}
PatKind::TupleStruct(_, ref fields, ddpos)
if ddpos.is_none() && fields.is_empty() => {
self.context.span_handler.struct_span_err(pattern.span,
"nullary enum variants are written with \
no trailing `( )`").emit();
gate_feature_post!(&self, relaxed_adts, pattern.span,
"empty tuple structs patterns are unstable");
}
_ => {}
}
@@ -1107,6 +1110,19 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
visit::walk_impl_item(self, ii);
}

fn visit_variant_data(&mut self, vdata: &ast::VariantData, _: ast::Ident,
_: &ast::Generics, _: NodeId, span: Span) {
if vdata.fields().is_empty() {
if vdata.is_tuple() {
gate_feature_post!(&self, relaxed_adts, span,
"empty tuple structs and enum variants are unstable, \
use unit structs and enum variants instead");
}
}

visit::walk_struct_def(self, vdata)
}

fn visit_vis(&mut self, vis: &ast::Visibility) {
let span = match *vis {
ast::Visibility::Crate(span) => span,
13 changes: 11 additions & 2 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
@@ -2009,10 +2009,19 @@ impl<'a> Parser<'a> {
}
}

pub fn parse_field_name(&mut self) -> PResult<'a, Ident> {
if let token::Literal(token::Integer(name), None) = self.token {
self.bump();
Ok(Ident::with_empty_ctxt(name))
} else {
self.parse_ident()
}
}

/// Parse ident COLON expr
pub fn parse_field(&mut self) -> PResult<'a, Field> {
let lo = self.span.lo;
let i = self.parse_ident()?;
let i = self.parse_field_name()?;
let hi = self.last_span.hi;
self.expect(&token::Colon)?;
let e = self.parse_expr()?;
@@ -3508,7 +3517,7 @@ impl<'a> Parser<'a> {
// Check if a colon exists one ahead. This means we're parsing a fieldname.
let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
// Parsing a pattern of the form "fieldname: pat"
let fieldname = self.parse_ident()?;
let fieldname = self.parse_field_name()?;
self.bump();
let pat = self.parse_pat()?;
hi = pat.span.hi;
6 changes: 3 additions & 3 deletions src/test/compile-fail/E0071.rs
Original file line number Diff line number Diff line change
@@ -8,11 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

enum Foo { FirstValue(i32) }
enum Foo {}

fn main() {
let u = Foo::FirstValue { value: 0 };
//~^ ERROR `Foo::FirstValue` does not name a struct or a struct variant [E0071]
let u = Foo { value: 0 };
//~^ ERROR `Foo` does not name a struct or a struct variant [E0071]
//~| NOTE not a struct

let t = u32 { value: 4 };
4 changes: 4 additions & 0 deletions src/test/compile-fail/auxiliary/empty-struct.rs
Original file line number Diff line number Diff line change
@@ -8,10 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(relaxed_adts)]

pub struct XEmpty1 {}
pub struct XEmpty2;
pub struct XEmpty6();

pub enum XE {
XEmpty3 {},
XEmpty4,
XEmpty5(),
}
15 changes: 8 additions & 7 deletions src/test/compile-fail/empty-struct-braces-pat-2.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

@@ -21,13 +23,12 @@ fn main() {
let e1 = Empty1 {};
let xe1 = XEmpty1 {};

// Rejected by parser as yet
// match e1 {
// Empty1() => () // ERROR unresolved enum variant, struct or const `Empty1`
// }
// match xe1 {
// XEmpty1() => () // ERROR unresolved enum variant, struct or const `XEmpty1`
// }
match e1 {
Empty1() => () //~ ERROR unresolved variant or struct `Empty1`
}
match xe1 {
XEmpty1() => () //~ ERROR unresolved variant or struct `XEmpty1`
}
match e1 {
Empty1(..) => () //~ ERROR unresolved variant or struct `Empty1`
}
15 changes: 8 additions & 7 deletions src/test/compile-fail/empty-struct-braces-pat-3.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

@@ -23,13 +25,12 @@ fn main() {
let e3 = E::Empty3 {};
let xe3 = XE::XEmpty3 {};

// Rejected by parser as yet
// match e3 {
// E::Empty3() => () // ERROR `E::Empty3` does not name a tuple variant or a tuple struct
// }
// match xe3 {
// E::Empty3() => () // ERROR `XE::XEmpty3` does not name a tuple variant or a tuple struct
// }
match e3 {
E::Empty3() => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct
}
match xe3 {
XE::XEmpty3() => () //~ ERROR `XE::XEmpty3` does not name a tuple variant or a tuple struct
}
match e3 {
E::Empty3(..) => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct
}
47 changes: 47 additions & 0 deletions src/test/compile-fail/empty-struct-tuple-pat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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.

// Can't use unit struct as enum pattern

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

struct Empty2();

enum E {
Empty4()
}

// remove attribute after warning cycle and promoting warnings to errors
fn main() {
let e2 = Empty2();
let e4 = E::Empty4();
let xe6 = XEmpty6();
let xe5 = XE::XEmpty5();

match e2 {
Empty2 => () //~ ERROR `Empty2` does not name a unit variant, unit struct or a constant
}
match xe6 {
XEmpty6 => () //~ ERROR `XEmpty6` does not name a unit variant, unit struct or a constant
}

match e4 {
E::Empty4 => () //~ ERROR `E::Empty4` does not name a unit variant, unit struct or a
}
match xe5 {
XE::XEmpty5 => (), //~ ERROR `XE::XEmpty5` does not name a unit variant, unit struct or a
_ => {},
}
}
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

@@ -28,13 +30,6 @@ fn main() {
let xe2 = XEmpty2;
let xe4 = XE::XEmpty4;

// Rejected by parser as yet
// match e2 {
// Empty2() => () // ERROR `Empty2` does not name a tuple variant or a tuple struct
// }
// match xe2 {
// XEmpty2() => () // ERROR `XEmpty2` does not name a tuple variant or a tuple struct
// }
match e2 {
Empty2(..) => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct
//~^ WARNING hard error
@@ -43,14 +38,7 @@ fn main() {
XEmpty2(..) => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct
//~^ WARNING hard error
}
// Rejected by parser as yet
// match e4 {
// E::Empty4() => () // ERROR `E::Empty4` does not name a tuple variant or a tuple struct
// }
// match xe4 {
// XE::XEmpty4() => (), // ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
// _ => {},
// }

match e4 {
E::Empty4(..) => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct
//~^ WARNING hard error
47 changes: 47 additions & 0 deletions src/test/compile-fail/empty-struct-unit-pat-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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.

// Can't use unit struct as enum pattern

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

struct Empty2;

enum E {
Empty4
}

// remove attribute after warning cycle and promoting warnings to errors
fn main() {
let e2 = Empty2;
let e4 = E::Empty4;
let xe2 = XEmpty2;
let xe4 = XE::XEmpty4;

match e2 {
Empty2() => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct
}
match xe2 {
XEmpty2() => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct
}

match e4 {
E::Empty4() => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct
}
match xe4 {
XE::XEmpty4() => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
_ => {},
}
}
Loading