Skip to content

Commit dc1c797

Browse files
committed
Auto merge of #28336 - petrochenkov:empstr, r=pnkfelix
Closes #24266 Closes #16819
2 parents 53ba768 + 1eb42f1 commit dc1c797

File tree

13 files changed

+187
-124
lines changed

13 files changed

+187
-124
lines changed

src/doc/reference.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -1178,11 +1178,22 @@ let px: i32 = match p { Point(x, _) => x };
11781178
```
11791179

11801180
A _unit-like struct_ is a structure without any fields, defined by leaving off
1181-
the list of fields entirely. Such types will have a single value. For example:
1181+
the list of fields entirely. Such a structure implicitly defines a constant of
1182+
its type with the same name. For example:
11821183

11831184
```
1185+
# #![feature(braced_empty_structs)]
11841186
struct Cookie;
1185-
let c = [Cookie, Cookie, Cookie, Cookie];
1187+
let c = [Cookie, Cookie {}, Cookie, Cookie {}];
1188+
```
1189+
1190+
is equivalent to
1191+
1192+
```
1193+
# #![feature(braced_empty_structs)]
1194+
struct Cookie {}
1195+
const Cookie: Cookie = Cookie {};
1196+
let c = [Cookie, Cookie {}, Cookie, Cookie {}];
11861197
```
11871198

11881199
The precise memory layout of a structure is not specified. One can specify a
@@ -2411,6 +2422,7 @@ The currently implemented features of the reference compiler are:
24112422
terms of encapsulation).
24122423
* - `default_type_parameter_fallback` - Allows type parameter defaults to
24132424
influence type inference.
2425+
* - `braced_empty_structs` - Allows use of empty structs with braces.
24142426

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

src/librustc_front/print/pprust.rs

+25-25
Original file line numberDiff line numberDiff line change
@@ -1218,34 +1218,34 @@ impl<'a> State<'a> {
12181218
fields: &[hir::Field],
12191219
wth: &Option<P<hir::Expr>>) -> io::Result<()> {
12201220
try!(self.print_path(path, true, 0));
1221-
if !(fields.is_empty() && wth.is_none()) {
1222-
try!(word(&mut self.s, "{"));
1223-
try!(self.commasep_cmnt(
1224-
Consistent,
1225-
&fields[..],
1226-
|s, field| {
1227-
try!(s.ibox(indent_unit));
1228-
try!(s.print_ident(field.ident.node));
1229-
try!(s.word_space(":"));
1230-
try!(s.print_expr(&*field.expr));
1231-
s.end()
1232-
},
1233-
|f| f.span));
1234-
match *wth {
1235-
Some(ref expr) => {
1236-
try!(self.ibox(indent_unit));
1237-
if !fields.is_empty() {
1238-
try!(word(&mut self.s, ","));
1239-
try!(space(&mut self.s));
1240-
}
1241-
try!(word(&mut self.s, ".."));
1242-
try!(self.print_expr(&**expr));
1243-
try!(self.end());
1221+
try!(word(&mut self.s, "{"));
1222+
try!(self.commasep_cmnt(
1223+
Consistent,
1224+
&fields[..],
1225+
|s, field| {
1226+
try!(s.ibox(indent_unit));
1227+
try!(s.print_ident(field.ident.node));
1228+
try!(s.word_space(":"));
1229+
try!(s.print_expr(&*field.expr));
1230+
s.end()
1231+
},
1232+
|f| f.span));
1233+
match *wth {
1234+
Some(ref expr) => {
1235+
try!(self.ibox(indent_unit));
1236+
if !fields.is_empty() {
1237+
try!(word(&mut self.s, ","));
1238+
try!(space(&mut self.s));
12441239
}
1245-
_ => try!(word(&mut self.s, ",")),
1240+
try!(word(&mut self.s, ".."));
1241+
try!(self.print_expr(&**expr));
1242+
try!(self.end());
1243+
}
1244+
_ => if !fields.is_empty() {
1245+
try!(word(&mut self.s, ","))
12461246
}
1247-
try!(word(&mut self.s, "}"));
12481247
}
1248+
try!(word(&mut self.s, "}"));
12491249
Ok(())
12501250
}
12511251

src/librustc_typeck/check/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1473,14 +1473,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14731473
_ => return None
14741474
};
14751475

1476-
if let ty::VariantKind::Dict = variant.kind() {
1476+
let var_kind = variant.kind();
1477+
if var_kind == ty::VariantKind::Dict || var_kind == ty::VariantKind::Unit {
14771478
Some((adt, variant))
14781479
} else {
14791480
None
14801481
}
14811482
}
14821483

1483-
14841484
pub fn write_nil(&self, node_id: ast::NodeId) {
14851485
self.write_ty(node_id, self.tcx().mk_nil());
14861486
}

src/libsyntax/feature_gate.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
191191

192192
// allow `#[unwind]`
193193
("unwind_attributes", "1.4.0", None, Active),
194+
195+
// allow empty structs/enum variants with braces
196+
("braced_empty_structs", "1.5.0", None, Active),
194197
];
195198
// (changing above list without updating src/doc/reference.md makes @cmr sad)
196199

@@ -775,7 +778,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
775778
}
776779
}
777780

778-
ast::ItemStruct(..) => {
781+
ast::ItemStruct(ref def, _) => {
779782
if attr::contains_name(&i.attrs[..], "simd") {
780783
self.gate_feature("simd", i.span,
781784
"SIMD types are experimental and possibly buggy");
@@ -794,6 +797,10 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
794797
}
795798
}
796799
}
800+
if def.fields.is_empty() && def.ctor_id.is_none() {
801+
self.gate_feature("braced_empty_structs", i.span,
802+
"empty structs with braces are unstable");
803+
}
797804
}
798805

799806
ast::ItemDefaultImpl(..) => {
@@ -843,6 +850,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
843850
"box expression syntax is experimental; \
844851
you can call `Box::new` instead.");
845852
}
853+
ast::ExprStruct(_, ref fields, ref expr) => {
854+
if fields.is_empty() && expr.is_none() {
855+
self.gate_feature("braced_empty_structs", e.span,
856+
"empty structs with braces are unstable");
857+
}
858+
}
846859
_ => {}
847860
}
848861
visit::walk_expr(self, e);
@@ -867,6 +880,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
867880
pattern.span,
868881
"box pattern syntax is experimental");
869882
}
883+
ast::PatStruct(_, ref fields, dotdot) => {
884+
if fields.is_empty() && !dotdot {
885+
self.gate_feature("braced_empty_structs", pattern.span,
886+
"empty structs with braces are unstable");
887+
}
888+
}
870889
_ => {}
871890
}
872891
visit::walk_pat(self, pattern)

src/libsyntax/parse/parser.rs

+3-18
Original file line numberDiff line numberDiff line change
@@ -2231,14 +2231,6 @@ impl<'a> Parser<'a> {
22312231
&[token::CloseDelim(token::Brace)]));
22322232
}
22332233

2234-
if fields.is_empty() && base.is_none() {
2235-
let last_span = self.last_span;
2236-
self.span_err(last_span,
2237-
"structure literal must either \
2238-
have at least one field or use \
2239-
structure update syntax");
2240-
}
2241-
22422234
hi = self.span.hi;
22432235
try!(self.expect(&token::CloseDelim(token::Brace)));
22442236
ex = ExprStruct(pth, fields, base);
@@ -4713,14 +4705,14 @@ impl<'a> Parser<'a> {
47134705
(Vec::new(), Some(ast::DUMMY_NODE_ID))
47144706
} else {
47154707
// If we see: `struct Foo<T> where T: Copy { ... }`
4716-
(try!(self.parse_record_struct_body(&class_name)), None)
4708+
(try!(self.parse_record_struct_body()), None)
47174709
}
47184710
// No `where` so: `struct Foo<T>;`
47194711
} else if try!(self.eat(&token::Semi) ){
47204712
(Vec::new(), Some(ast::DUMMY_NODE_ID))
47214713
// Record-style struct definition
47224714
} else if self.token == token::OpenDelim(token::Brace) {
4723-
let fields = try!(self.parse_record_struct_body(&class_name));
4715+
let fields = try!(self.parse_record_struct_body());
47244716
(fields, None)
47254717
// Tuple-style struct definition with optional where-clause.
47264718
} else if self.token == token::OpenDelim(token::Paren) {
@@ -4740,20 +4732,13 @@ impl<'a> Parser<'a> {
47404732
None))
47414733
}
47424734

4743-
pub fn parse_record_struct_body(&mut self,
4744-
class_name: &ast::Ident) -> PResult<Vec<StructField>> {
4735+
pub fn parse_record_struct_body(&mut self) -> PResult<Vec<StructField>> {
47454736
let mut fields = Vec::new();
47464737
if try!(self.eat(&token::OpenDelim(token::Brace)) ){
47474738
while self.token != token::CloseDelim(token::Brace) {
47484739
fields.push(try!(self.parse_struct_decl_field(true)));
47494740
}
47504741

4751-
if fields.is_empty() {
4752-
return Err(self.fatal(&format!("unit-like struct definition should be \
4753-
written as `struct {};`",
4754-
class_name)));
4755-
}
4756-
47574742
try!(self.bump());
47584743
} else {
47594744
let token_str = self.this_token_to_string();

src/libsyntax/print/pprust.rs

+25-25
Original file line numberDiff line numberDiff line change
@@ -1855,34 +1855,34 @@ impl<'a> State<'a> {
18551855
fields: &[ast::Field],
18561856
wth: &Option<P<ast::Expr>>) -> io::Result<()> {
18571857
try!(self.print_path(path, true, 0));
1858-
if !(fields.is_empty() && wth.is_none()) {
1859-
try!(word(&mut self.s, "{"));
1860-
try!(self.commasep_cmnt(
1861-
Consistent,
1862-
&fields[..],
1863-
|s, field| {
1864-
try!(s.ibox(indent_unit));
1865-
try!(s.print_ident(field.ident.node));
1866-
try!(s.word_space(":"));
1867-
try!(s.print_expr(&*field.expr));
1868-
s.end()
1869-
},
1870-
|f| f.span));
1871-
match *wth {
1872-
Some(ref expr) => {
1873-
try!(self.ibox(indent_unit));
1874-
if !fields.is_empty() {
1875-
try!(word(&mut self.s, ","));
1876-
try!(space(&mut self.s));
1877-
}
1878-
try!(word(&mut self.s, ".."));
1879-
try!(self.print_expr(&**expr));
1880-
try!(self.end());
1858+
try!(word(&mut self.s, "{"));
1859+
try!(self.commasep_cmnt(
1860+
Consistent,
1861+
&fields[..],
1862+
|s, field| {
1863+
try!(s.ibox(indent_unit));
1864+
try!(s.print_ident(field.ident.node));
1865+
try!(s.word_space(":"));
1866+
try!(s.print_expr(&*field.expr));
1867+
s.end()
1868+
},
1869+
|f| f.span));
1870+
match *wth {
1871+
Some(ref expr) => {
1872+
try!(self.ibox(indent_unit));
1873+
if !fields.is_empty() {
1874+
try!(word(&mut self.s, ","));
1875+
try!(space(&mut self.s));
18811876
}
1882-
_ => try!(word(&mut self.s, ",")),
1877+
try!(word(&mut self.s, ".."));
1878+
try!(self.print_expr(&**expr));
1879+
try!(self.end());
1880+
}
1881+
_ => if !fields.is_empty() {
1882+
try!(word(&mut self.s, ","))
18831883
}
1884-
try!(word(&mut self.s, "}"));
18851884
}
1885+
try!(word(&mut self.s, "}"));
18861886
Ok(())
18871887
}
18881888

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -8,13 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
11+
// Empty struct defined with braces shouldn't add names into value namespace
1212

13-
struct Foo;
13+
#![feature(braced_empty_structs)]
1414

15-
fn f2() {
16-
let _end_stmt = Foo { };
17-
//~^ ERROR: structure literal must either have at least one field
18-
}
15+
struct Empty {}
1916

20-
fn main() {}
17+
fn main() {
18+
let e = Empty; //~ ERROR `Empty` is the name of a struct or struct variant
19+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -8,13 +8,18 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
11+
// Empty struct defined with braces shouldn't add names into value namespace
1212

13-
struct Foo;
13+
#![feature(braced_empty_structs)]
14+
#![deny(warnings)]
1415

15-
fn h4() {
16-
let _end_of_tuple = (3, Foo { });
17-
//~^ ERROR: structure literal must either have at least one field
18-
}
16+
struct Empty {}
17+
18+
fn main() {
19+
let e = Empty {};
1920

20-
fn main() {}
21+
match e {
22+
Empty => () //~ ERROR unused variable: `Empty`
23+
//~^ ERROR variable `Empty` should have a snake case name such as `empty`
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -8,13 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
11+
// Feature gate test for empty struct with braces
1212

13-
struct Foo;
13+
struct Empty {} //~ ERROR empty structs with braces are unstable
1414

15-
fn g3() {
16-
let _mid_tuple = (Foo { }, 2);
17-
//~^ ERROR: structure literal must either have at least one field
18-
}
15+
fn main() {
16+
let e = Empty {}; //~ ERROR empty structs with braces are unstable
1917

20-
fn main() {}
18+
match e {
19+
Empty {} => {} //~ ERROR empty structs with braces are unstable
20+
}
21+
}

src/test/compile-fail/issue-27831.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ fn main() {
2222
let Foo { .. } = x; //~ ERROR `Foo` does not name a struct
2323

2424
let x = Bar;
25-
Bar { ..x }; //~ ERROR `Bar` does not name a structure
26-
let Bar { .. } = x; //~ ERROR `Bar` does not name a struct
25+
Bar { ..x };
26+
let Bar { .. } = x;
2727

2828
match Enum::Bar {
2929
Enum::Bar { .. } //~ ERROR `Enum::Bar` does not name a struct

src/test/parse-fail/struct-no-fields-5.rs

-20
This file was deleted.

0 commit comments

Comments
 (0)