Skip to content
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

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

Merged
merged 2 commits into from
Aug 13, 2016
Merged
Show file tree
Hide file tree
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
Expand Up @@ -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, .. } => {
Expand Down
27 changes: 19 additions & 8 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
24 changes: 20 additions & 4 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! (
Expand Down Expand Up @@ -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");
}
_ => {}
}
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 11 additions & 2 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/test/compile-fail/E0071.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
4 changes: 4 additions & 0 deletions src/test/compile-fail/auxiliary/empty-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -12,6 +12,8 @@

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

Expand All @@ -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`
}
Expand Down
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
Expand Up @@ -12,6 +12,8 @@

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

Expand All @@ -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
}
Expand Down
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
Expand Up @@ -12,6 +12,8 @@

// aux-build:empty-struct.rs

#![feature(relaxed_adts)]

extern crate empty_struct;
use empty_struct::*;

Expand All @@ -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
Expand All @@ -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
Expand Down
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