Skip to content

Commit

Permalink
Auto merge of #35138 - petrochenkov:clarify, r=eddyb
Browse files Browse the repository at this point in the history
Implement RFC 1506 "Clarify the relationships between various kinds of structs and variants"

cc #35626
  • Loading branch information
bors authored Aug 13, 2016
2 parents 1deb02e + f662478 commit d3c3de8
Show file tree
Hide file tree
Showing 24 changed files with 330 additions and 218 deletions.
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

0 comments on commit d3c3de8

Please sign in to comment.