1
Fork 0

Auto merge of #28336 - petrochenkov:empstr, r=pnkfelix

Closes https://github.com/rust-lang/rust/issues/24266
Closes https://github.com/rust-lang/rust/issues/16819
This commit is contained in:
bors 2015-09-18 16:57:21 +00:00
commit dc1c7975b0
13 changed files with 187 additions and 124 deletions

View file

@ -1178,11 +1178,22 @@ let px: i32 = match p { Point(x, _) => x };
```
A _unit-like struct_ is a structure without any fields, defined by leaving off
the list of fields entirely. Such types will have a single value. For example:
the list of fields entirely. Such a structure implicitly defines a constant of
its type with the same name. For example:
```
# #![feature(braced_empty_structs)]
struct Cookie;
let c = [Cookie, Cookie, Cookie, Cookie];
let c = [Cookie, Cookie {}, Cookie, Cookie {}];
```
is equivalent to
```
# #![feature(braced_empty_structs)]
struct Cookie {}
const Cookie: Cookie = Cookie {};
let c = [Cookie, Cookie {}, Cookie, Cookie {}];
```
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:
terms of encapsulation).
* - `default_type_parameter_fallback` - Allows type parameter defaults to
influence type inference.
* - `braced_empty_structs` - Allows use of empty structs with braces.
If a feature is promoted to a language feature, then all existing programs will
start to receive compilation warnings about `#![feature]` directives which enabled

View file

@ -1218,34 +1218,34 @@ impl<'a> State<'a> {
fields: &[hir::Field],
wth: &Option<P<hir::Expr>>) -> io::Result<()> {
try!(self.print_path(path, true, 0));
if !(fields.is_empty() && wth.is_none()) {
try!(word(&mut self.s, "{"));
try!(self.commasep_cmnt(
Consistent,
&fields[..],
|s, field| {
try!(s.ibox(indent_unit));
try!(s.print_ident(field.ident.node));
try!(s.word_space(":"));
try!(s.print_expr(&*field.expr));
s.end()
},
|f| f.span));
match *wth {
Some(ref expr) => {
try!(self.ibox(indent_unit));
if !fields.is_empty() {
try!(word(&mut self.s, ","));
try!(space(&mut self.s));
}
try!(word(&mut self.s, ".."));
try!(self.print_expr(&**expr));
try!(self.end());
try!(word(&mut self.s, "{"));
try!(self.commasep_cmnt(
Consistent,
&fields[..],
|s, field| {
try!(s.ibox(indent_unit));
try!(s.print_ident(field.ident.node));
try!(s.word_space(":"));
try!(s.print_expr(&*field.expr));
s.end()
},
|f| f.span));
match *wth {
Some(ref expr) => {
try!(self.ibox(indent_unit));
if !fields.is_empty() {
try!(word(&mut self.s, ","));
try!(space(&mut self.s));
}
_ => try!(word(&mut self.s, ",")),
try!(word(&mut self.s, ".."));
try!(self.print_expr(&**expr));
try!(self.end());
}
_ => if !fields.is_empty() {
try!(word(&mut self.s, ","))
}
try!(word(&mut self.s, "}"));
}
try!(word(&mut self.s, "}"));
Ok(())
}

View file

@ -1473,14 +1473,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => return None
};
if let ty::VariantKind::Dict = variant.kind() {
let var_kind = variant.kind();
if var_kind == ty::VariantKind::Dict || var_kind == ty::VariantKind::Unit {
Some((adt, variant))
} else {
None
}
}
pub fn write_nil(&self, node_id: ast::NodeId) {
self.write_ty(node_id, self.tcx().mk_nil());
}

View file

@ -191,6 +191,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
// allow `#[unwind]`
("unwind_attributes", "1.4.0", None, Active),
// allow empty structs/enum variants with braces
("braced_empty_structs", "1.5.0", None, Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
@ -775,7 +778,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
}
}
ast::ItemStruct(..) => {
ast::ItemStruct(ref def, _) => {
if attr::contains_name(&i.attrs[..], "simd") {
self.gate_feature("simd", i.span,
"SIMD types are experimental and possibly buggy");
@ -794,6 +797,10 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
}
}
}
if def.fields.is_empty() && def.ctor_id.is_none() {
self.gate_feature("braced_empty_structs", i.span,
"empty structs with braces are unstable");
}
}
ast::ItemDefaultImpl(..) => {
@ -843,6 +850,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
"box expression syntax is experimental; \
you can call `Box::new` instead.");
}
ast::ExprStruct(_, ref fields, ref expr) => {
if fields.is_empty() && expr.is_none() {
self.gate_feature("braced_empty_structs", e.span,
"empty structs with braces are unstable");
}
}
_ => {}
}
visit::walk_expr(self, e);
@ -867,6 +880,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
pattern.span,
"box pattern syntax is experimental");
}
ast::PatStruct(_, ref fields, dotdot) => {
if fields.is_empty() && !dotdot {
self.gate_feature("braced_empty_structs", pattern.span,
"empty structs with braces are unstable");
}
}
_ => {}
}
visit::walk_pat(self, pattern)

View file

@ -2231,14 +2231,6 @@ impl<'a> Parser<'a> {
&[token::CloseDelim(token::Brace)]));
}
if fields.is_empty() && base.is_none() {
let last_span = self.last_span;
self.span_err(last_span,
"structure literal must either \
have at least one field or use \
structure update syntax");
}
hi = self.span.hi;
try!(self.expect(&token::CloseDelim(token::Brace)));
ex = ExprStruct(pth, fields, base);
@ -4713,14 +4705,14 @@ impl<'a> Parser<'a> {
(Vec::new(), Some(ast::DUMMY_NODE_ID))
} else {
// If we see: `struct Foo<T> where T: Copy { ... }`
(try!(self.parse_record_struct_body(&class_name)), None)
(try!(self.parse_record_struct_body()), None)
}
// No `where` so: `struct Foo<T>;`
} else if try!(self.eat(&token::Semi) ){
(Vec::new(), Some(ast::DUMMY_NODE_ID))
// Record-style struct definition
} else if self.token == token::OpenDelim(token::Brace) {
let fields = try!(self.parse_record_struct_body(&class_name));
let fields = try!(self.parse_record_struct_body());
(fields, None)
// Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(token::Paren) {
@ -4740,20 +4732,13 @@ impl<'a> Parser<'a> {
None))
}
pub fn parse_record_struct_body(&mut self,
class_name: &ast::Ident) -> PResult<Vec<StructField>> {
pub fn parse_record_struct_body(&mut self) -> PResult<Vec<StructField>> {
let mut fields = Vec::new();
if try!(self.eat(&token::OpenDelim(token::Brace)) ){
while self.token != token::CloseDelim(token::Brace) {
fields.push(try!(self.parse_struct_decl_field(true)));
}
if fields.is_empty() {
return Err(self.fatal(&format!("unit-like struct definition should be \
written as `struct {};`",
class_name)));
}
try!(self.bump());
} else {
let token_str = self.this_token_to_string();

View file

@ -1855,34 +1855,34 @@ impl<'a> State<'a> {
fields: &[ast::Field],
wth: &Option<P<ast::Expr>>) -> io::Result<()> {
try!(self.print_path(path, true, 0));
if !(fields.is_empty() && wth.is_none()) {
try!(word(&mut self.s, "{"));
try!(self.commasep_cmnt(
Consistent,
&fields[..],
|s, field| {
try!(s.ibox(indent_unit));
try!(s.print_ident(field.ident.node));
try!(s.word_space(":"));
try!(s.print_expr(&*field.expr));
s.end()
},
|f| f.span));
match *wth {
Some(ref expr) => {
try!(self.ibox(indent_unit));
if !fields.is_empty() {
try!(word(&mut self.s, ","));
try!(space(&mut self.s));
}
try!(word(&mut self.s, ".."));
try!(self.print_expr(&**expr));
try!(self.end());
try!(word(&mut self.s, "{"));
try!(self.commasep_cmnt(
Consistent,
&fields[..],
|s, field| {
try!(s.ibox(indent_unit));
try!(s.print_ident(field.ident.node));
try!(s.word_space(":"));
try!(s.print_expr(&*field.expr));
s.end()
},
|f| f.span));
match *wth {
Some(ref expr) => {
try!(self.ibox(indent_unit));
if !fields.is_empty() {
try!(word(&mut self.s, ","));
try!(space(&mut self.s));
}
_ => try!(word(&mut self.s, ",")),
try!(word(&mut self.s, ".."));
try!(self.print_expr(&**expr));
try!(self.end());
}
_ => if !fields.is_empty() {
try!(word(&mut self.s, ","))
}
try!(word(&mut self.s, "}"));
}
try!(word(&mut self.s, "}"));
Ok(())
}

View file

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// 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.
//
@ -8,13 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// Empty struct defined with braces shouldn't add names into value namespace
struct Foo;
#![feature(braced_empty_structs)]
fn f2() {
let _end_stmt = Foo { };
//~^ ERROR: structure literal must either have at least one field
struct Empty {}
fn main() {
let e = Empty; //~ ERROR `Empty` is the name of a struct or struct variant
}
fn main() {}

View file

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// 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.
//
@ -8,13 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// Empty struct defined with braces shouldn't add names into value namespace
struct Foo;
#![feature(braced_empty_structs)]
#![deny(warnings)]
fn h4() {
let _end_of_tuple = (3, Foo { });
//~^ ERROR: structure literal must either have at least one field
struct Empty {}
fn main() {
let e = Empty {};
match e {
Empty => () //~ ERROR unused variable: `Empty`
//~^ ERROR variable `Empty` should have a snake case name such as `empty`
}
}
fn main() {}

View file

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// 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.
//
@ -8,13 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// Feature gate test for empty struct with braces
struct Foo;
struct Empty {} //~ ERROR empty structs with braces are unstable
fn g3() {
let _mid_tuple = (Foo { }, 2);
//~^ ERROR: structure literal must either have at least one field
fn main() {
let e = Empty {}; //~ ERROR empty structs with braces are unstable
match e {
Empty {} => {} //~ ERROR empty structs with braces are unstable
}
}
fn main() {}

View file

@ -22,8 +22,8 @@ fn main() {
let Foo { .. } = x; //~ ERROR `Foo` does not name a struct
let x = Bar;
Bar { ..x }; //~ ERROR `Bar` does not name a structure
let Bar { .. } = x; //~ ERROR `Bar` does not name a struct
Bar { ..x };
let Bar { .. } = x;
match Enum::Bar {
Enum::Bar { .. } //~ ERROR `Enum::Bar` does not name a struct

View file

@ -1,20 +0,0 @@
// Copyright 2013 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.
// compile-flags: -Z parse-only
struct Foo;
fn i5() {
let _end_of_block = { Foo { } };
//~^ ERROR: structure literal must either have at least one field
}
fn main() {}

View file

@ -0,0 +1,56 @@
// 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.
// Empty struct defined with braces add names into type namespace
// Empty struct defined without braces add names into both type and value namespaces
#![feature(braced_empty_structs)]
struct Empty1 {}
struct Empty2;
struct Empty3 {}
const Empty3: Empty3 = Empty3 {};
fn main() {
let e1: Empty1 = Empty1 {};
let e2: Empty2 = Empty2 {};
let e2: Empty2 = Empty2;
let e3: Empty3 = Empty3 {};
let e3: Empty3 = Empty3;
match e1 {
Empty1 {} => ()
}
match e2 {
Empty2 {} => ()
}
match e2 {
Empty2 => ()
}
match e3 {
Empty3 {} => ()
}
match e3 {
Empty3 => ()
}
match e1 {
Empty1 { .. } => ()
}
match e2 {
Empty2 { .. } => ()
}
match e3 {
Empty3 { .. } => ()
}
let e11 = Empty1 { ..e1 };
let e22 = Empty2 { ..e2 };
let e33 = Empty3 { ..e3 };
}

View file

@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// 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.
//
@ -8,9 +8,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
//`#[cfg]` on struct field permits empty unusable struct
struct Foo {}
//~^ ERROR: unit-like struct definition should be written as `struct Foo;`
#![feature(braced_empty_structs)]
fn main() {}
struct S {
#[cfg(untrue)]
a: int,
}
fn main() {
let s = S {};
}