Skip to content

Commit 8a12c19

Browse files
committed
Test and gate empty structures and variants better
1 parent beda1f8 commit 8a12c19

16 files changed

+291
-69
lines changed

src/doc/reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2357,7 +2357,7 @@ The currently implemented features of the reference compiler are:
23572357
terms of encapsulation).
23582358
* - `default_type_parameter_fallback` - Allows type parameter defaults to
23592359
influence type inference.
2360-
* - `braced_empty_structs` - Allows use of empty structs with braces.
2360+
* - `braced_empty_structs` - Allows use of empty structs and enum variants with braces.
23612361

23622362
If a feature is promoted to a language feature, then all existing programs will
23632363
start to receive compilation warnings about `#![feature]` directives which enabled

src/librustc_typeck/check/_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx hir::Pat,
530530
let tcx = pcx.fcx.ccx.tcx;
531531

532532
let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
533-
let variant = match fcx.def_struct_variant(def) {
533+
let variant = match fcx.def_struct_variant(def, path.span) {
534534
Some((_, variant)) => variant,
535535
None => {
536536
let name = pprust::path_to_string(path);

src/librustc_typeck/check/mod.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14641464

14651465
/// Return the dict-like variant corresponding to a given `Def`.
14661466
pub fn def_struct_variant(&self,
1467-
def: def::Def)
1467+
def: def::Def,
1468+
span: Span)
14681469
-> Option<(ty::AdtDef<'tcx>, ty::VariantDef<'tcx>)>
14691470
{
14701471
let (adt, variant) = match def {
@@ -1484,11 +1485,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14841485
};
14851486

14861487
let var_kind = variant.kind();
1487-
if var_kind == ty::VariantKind::Dict || var_kind == ty::VariantKind::Unit {
1488+
if var_kind == ty::VariantKind::Dict {
14881489
Some((adt, variant))
1489-
} else {
1490-
None
1491-
}
1490+
} else if var_kind == ty::VariantKind::Unit {
1491+
if !self.tcx().sess.features.borrow().braced_empty_structs {
1492+
self.tcx().sess.span_err(span, "empty structs and enum variants \
1493+
with braces are unstable");
1494+
fileline_help!(self.tcx().sess, span, "add #![feature(braced_empty_structs)] to \
1495+
the crate features to enable");
1496+
}
1497+
1498+
Some((adt, variant))
1499+
} else {
1500+
None
1501+
}
14921502
}
14931503

14941504
pub fn write_nil(&self, node_id: ast::NodeId) {
@@ -3177,7 +3187,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
31773187

31783188
// Find the relevant variant
31793189
let def = lookup_full_def(tcx, path.span, expr.id);
3180-
let (adt, variant) = match fcx.def_struct_variant(def) {
3190+
let (adt, variant) = match fcx.def_struct_variant(def, path.span) {
31813191
Some((adt, variant)) => (adt, variant),
31823192
None => {
31833193
span_err!(fcx.tcx().sess, path.span, E0071,

src/libsyntax/feature_gate.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
196196
// allow `#[unwind]`
197197
("unwind_attributes", "1.4.0", None, Active),
198198

199-
// allow empty structs/enum variants with braces
199+
// allow empty structs and enum variants with braces
200200
("braced_empty_structs", "1.5.0", None, Active),
201201

202202
// allow overloading augmented assignment operations like `a += b`
@@ -486,6 +486,7 @@ pub struct Features {
486486
pub cfg_target_feature: bool,
487487
pub cfg_target_vendor: bool,
488488
pub augmented_assignments: bool,
489+
pub braced_empty_structs: bool,
489490
}
490491

491492
impl Features {
@@ -516,6 +517,7 @@ impl Features {
516517
cfg_target_feature: false,
517518
cfg_target_vendor: false,
518519
augmented_assignments: false,
520+
braced_empty_structs: false,
519521
}
520522
}
521523
}
@@ -809,7 +811,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
809811
}
810812
}
811813

812-
ast::ItemStruct(ref def, _) => {
814+
ast::ItemStruct(..) => {
813815
if attr::contains_name(&i.attrs[..], "simd") {
814816
self.gate_feature("simd", i.span,
815817
"SIMD types are experimental and possibly buggy");
@@ -828,10 +830,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
828830
}
829831
}
830832
}
831-
if def.fields.is_empty() && def.kind == ast::VariantKind::Dict {
832-
self.gate_feature("braced_empty_structs", i.span,
833-
"empty structs with braces are unstable");
834-
}
835833
}
836834

837835
ast::ItemDefaultImpl(..) => {
@@ -859,6 +857,21 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
859857
visit::walk_item(self, i);
860858
}
861859

860+
fn visit_struct_def(&mut self, s: &'v ast::StructDef, _: ast::Ident,
861+
_: &'v ast::Generics, _: ast::NodeId, span: Span) {
862+
if s.fields.is_empty() {
863+
if s.kind == ast::VariantKind::Dict {
864+
self.gate_feature("braced_empty_structs", span,
865+
"empty structs and enum variants with braces are unstable");
866+
} else if s.kind == ast::VariantKind::Tuple {
867+
self.context.span_handler.span_err(span, "empty tuple structs and enum variants \
868+
are not allowed, use unit structs and \
869+
enum variants instead");
870+
}
871+
}
872+
visit::walk_struct_def(self, s)
873+
}
874+
862875
fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
863876
let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
864877
"link_name") {
@@ -881,12 +894,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
881894
"box expression syntax is experimental; \
882895
you can call `Box::new` instead.");
883896
}
884-
ast::ExprStruct(_, ref fields, ref expr) => {
885-
if fields.is_empty() && expr.is_none() {
886-
self.gate_feature("braced_empty_structs", e.span,
887-
"empty structs with braces are unstable");
888-
}
889-
}
890897
_ => {}
891898
}
892899
visit::walk_expr(self, e);
@@ -911,12 +918,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
911918
pattern.span,
912919
"box pattern syntax is experimental");
913920
}
914-
ast::PatStruct(_, ref fields, dotdot) => {
915-
if fields.is_empty() && !dotdot {
916-
self.gate_feature("braced_empty_structs", pattern.span,
917-
"empty structs with braces are unstable");
918-
}
919-
}
920921
_ => {}
921922
}
922923
visit::walk_pat(self, pattern)
@@ -1086,6 +1087,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
10861087
cfg_target_feature: cx.has_feature("cfg_target_feature"),
10871088
cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
10881089
augmented_assignments: cx.has_feature("augmented_assignments"),
1090+
braced_empty_structs: cx.has_feature("braced_empty_structs"),
10891091
}
10901092
}
10911093

src/libsyntax/parse/parser.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4658,7 +4658,7 @@ impl<'a> Parser<'a> {
46584658
(fields, VariantKind::Dict)
46594659
// Tuple-style struct definition with optional where-clause.
46604660
} else if self.token == token::OpenDelim(token::Paren) {
4661-
let fields = try!(self.parse_tuple_struct_body(class_name, &mut generics));
4661+
let fields = try!(self.parse_tuple_struct_body(&mut generics));
46624662
(fields, VariantKind::Tuple)
46634663
} else {
46644664
let token_str = self.this_token_to_string();
@@ -4694,7 +4694,6 @@ impl<'a> Parser<'a> {
46944694
}
46954695

46964696
pub fn parse_tuple_struct_body(&mut self,
4697-
class_name: ast::Ident,
46984697
generics: &mut ast::Generics)
46994698
-> PResult<Vec<StructField>> {
47004699
// This is the case where we find `struct Foo<T>(T) where T: Copy;`
@@ -4715,12 +4714,6 @@ impl<'a> Parser<'a> {
47154714
Ok(spanned(lo, p.span.hi, struct_field_))
47164715
}));
47174716

4718-
if fields.is_empty() {
4719-
return Err(self.fatal(&format!("unit-like struct definition should be \
4720-
written as `struct {};`",
4721-
class_name)));
4722-
}
4723-
47244717
generics.where_clause = try!(self.parse_where_clause());
47254718
try!(self.expect(&token::Semi));
47264719
Ok(fields)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Can't use empty braced struct as constant or constructor function
12+
13+
#![feature(braced_empty_structs)]
14+
15+
struct Empty1 {}
16+
17+
enum E {
18+
Empty2 {}
19+
}
20+
21+
fn main() {
22+
let e1 = Empty1; //~ ERROR `Empty1` is the name of a struct or struct variant
23+
let e1 = Empty1(); //~ ERROR `Empty1` is the name of a struct or struct variant
24+
let e2 = E::Empty2; //~ ERROR `E::Empty2` is the name of a struct or struct variant
25+
let e2 = E::Empty2(); //~ ERROR `E::Empty2` is the name of a struct or struct variant
26+
}

src/test/compile-fail/empty-struct-with-braces-3.rs renamed to src/test/compile-fail/empty-struct-braces-gate-1.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
// except according to those terms.
1010

1111
// Feature gate test for empty struct with braces
12+
// Can't define an empty braced struct
1213

13-
struct Empty {} //~ ERROR empty structs with braces are unstable
14+
struct Empty1 {} //~ ERROR empty structs and enum variants with braces are unstable
15+
struct Empty2;
1416

15-
fn main() {
16-
let e = Empty {}; //~ ERROR empty structs with braces are unstable
17+
enum E {
18+
Empty4 {}, //~ ERROR empty structs and enum variants with braces are unstable
19+
Empty5,
20+
}
1721

18-
match e {
19-
Empty {} => {} //~ ERROR empty structs with braces are unstable
20-
}
22+
fn main() {
2123
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Feature gate test for empty struct with braces
12+
// Can't use braced expressions and patterns with structs defined without braces
13+
14+
struct Empty2;
15+
16+
enum E {
17+
Empty5,
18+
}
19+
20+
fn main() {
21+
let e2: Empty2 = Empty2 {}; //~ ERROR empty structs and enum variants with braces are unstable
22+
let e2: Empty2 = Empty2;
23+
// Issue #28692
24+
// let e5: E = E::Empty5 {}; // ERROR empty structs and enum variants with braces are unstable
25+
let e5: E = E::Empty5;
26+
27+
match e2 {
28+
Empty2 {} => {} //~ ERROR empty structs and enum variants with braces are unstable
29+
}
30+
match e2 {
31+
Empty2 => {}
32+
}
33+
match e2 {
34+
Empty2 { .. } => {} //~ ERROR empty structs and enum variants with braces are unstable
35+
}
36+
// Issue #28692
37+
// match e5 {
38+
// E::Empty5 {} => {} // ERROR empty structs and enum variants with braces are unstable
39+
// }
40+
match e5 {
41+
E::Empty5 => {}
42+
}
43+
// Issue #28692
44+
// match e5 {
45+
// E::Empty5 { .. } => {} // ERROR empty structs and enum variants with braces are unstable
46+
// }
47+
48+
let e22 = Empty2 { ..e2 }; //~ ERROR empty structs and enum variants with braces are unstable
49+
}

src/test/compile-fail/empty-struct-with-braces-2.rs renamed to src/test/compile-fail/empty-struct-braces-pat-1.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,26 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// Empty struct defined with braces shouldn't add names into value namespace
11+
// Can't use empty braced struct as constant pattern
1212

13-
#![feature(braced_empty_structs)]
1413
#![deny(warnings)]
14+
#![feature(braced_empty_structs)]
1515

16-
struct Empty {}
16+
struct Empty1 {}
17+
18+
enum E {
19+
Empty2 {}
20+
}
1721

1822
fn main() {
19-
let e = Empty {};
23+
let e1 = Empty1 {};
24+
let e2 = E::Empty2 {};
2025

21-
match e {
22-
Empty => () //~ ERROR unused variable: `Empty`
23-
//~^ ERROR variable `Empty` should have a snake case name such as `empty`
26+
// Issue #28692
27+
// match e1 {
28+
// Empty1 => () // ERROR incorrect error
29+
// }
30+
match e2 {
31+
E::Empty2 => () //~ ERROR `E::Empty2` does not name a non-struct variant or a tuple struct
2432
}
2533
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Can't use empty braced struct as enum pattern
12+
13+
#![feature(braced_empty_structs)]
14+
15+
struct Empty1 {}
16+
17+
enum E {
18+
Empty2 {}
19+
}
20+
21+
fn main() {
22+
let e1 = Empty1 {};
23+
let e2 = E::Empty2 {};
24+
25+
// Rejected by parser as yet
26+
// match e1 {
27+
// Empty1() => () // ERROR unresolved enum variant, struct or const `Empty1`
28+
// }
29+
match e1 {
30+
Empty1(..) => () //~ ERROR unresolved enum variant, struct or const `Empty1`
31+
}
32+
// Issue #28692
33+
// match e2 {
34+
// E::Empty2() => () // ERROR unresolved enum variant, struct or const `Empty2`
35+
// }
36+
// match e2 {
37+
// E::Empty2(..) => () // ERROR unresolved enum variant, struct or const `Empty2`
38+
// }
39+
}

src/test/compile-fail/empty-struct-with-braces-1.rs renamed to src/test/compile-fail/empty-struct-unit-expr.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// Empty struct defined with braces shouldn't add names into value namespace
11+
// Can't use unit struct as constructor function
1212

1313
#![feature(braced_empty_structs)]
1414

15-
struct Empty {}
15+
struct Empty1;
16+
17+
enum E {
18+
Empty2
19+
}
1620

1721
fn main() {
18-
let e = Empty; //~ ERROR `Empty` is the name of a struct or struct variant
22+
let e1 = Empty1(); //~ ERROR expected function, found `Empty1`
23+
let e2 = E::Empty2(); //~ ERROR expected function, found `E`
1924
}

0 commit comments

Comments
 (0)