Skip to content

Fix #[derive] for empty structs with braces #31822

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

Merged
merged 2 commits into from
Feb 23, 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
50 changes: 26 additions & 24 deletions src/libsyntax_ext/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use deriving::generic::*;
use deriving::generic::ty::*;

use syntax::ast::{MetaItem, Expr};
use syntax::ast::{MetaItem, Expr, VariantData};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ext::build::AstBuilder;
Expand Down Expand Up @@ -66,14 +66,17 @@ fn cs_clone(
cx.expr_call_global(field.span, fn_path.clone(), args)
};

let vdata;
match *substr.fields {
Struct(ref af) => {
Struct(vdata_, ref af) => {
ctor_path = cx.path(trait_span, vec![substr.type_ident]);
all_fields = af;
vdata = vdata_;
}
EnumMatching(_, variant, ref af) => {
ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
all_fields = af;
vdata = &variant.node.data;
},
EnumNonMatchingCollapsed (..) => {
cx.span_bug(trait_span,
Expand All @@ -86,30 +89,29 @@ fn cs_clone(
}
}

if !all_fields.is_empty() && all_fields[0].name.is_none() {
// enum-like
let subcalls = all_fields.iter().map(subcall).collect();
let path = cx.expr_path(ctor_path);
cx.expr_call(trait_span, path, subcalls)
} else {
// struct-like
let fields = all_fields.iter().map(|field| {
let ident = match field.name {
Some(i) => i,
None => {
cx.span_bug(trait_span,
&format!("unnamed field in normal struct in \
`derive({})`", name))
}
};
cx.field_imm(field.span, ident, subcall(field))
}).collect::<Vec<_>>();
match *vdata {
VariantData::Struct(..) => {
let fields = all_fields.iter().map(|field| {
let ident = match field.name {
Some(i) => i,
None => {
cx.span_bug(trait_span,
&format!("unnamed field in normal struct in \
`derive({})`", name))
}
};
cx.field_imm(field.span, ident, subcall(field))
}).collect::<Vec<_>>();

if fields.is_empty() {
// no fields, so construct like `None`
cx.expr_path(ctor_path)
} else {
cx.expr_struct(trait_span, ctor_path, fields)
}
VariantData::Tuple(..) => {
let subcalls = all_fields.iter().map(subcall).collect();
let path = cx.expr_path(ctor_path);
cx.expr_call(trait_span, path, subcalls)
}
VariantData::Unit(..) => {
cx.expr_path(ctor_path)
}
}
}
10 changes: 5 additions & 5 deletions src/libsyntax_ext/deriving/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
// build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
// or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
// based on the "shape".
let ident = match *substr.fields {
Struct(_) => substr.type_ident,
EnumMatching(_, v, _) => v.node.name,
let (ident, is_struct) = match *substr.fields {
Struct(vdata, _) => (substr.type_ident, vdata.is_struct()),
EnumMatching(_, v, _) => (v.node.name, v.node.data.is_struct()),
EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
}
Expand All @@ -78,9 +78,9 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
let fmt = substr.nonself_args[0].clone();

let stmts = match *substr.fields {
Struct(ref fields) | EnumMatching(_, _, ref fields) => {
Struct(_, ref fields) | EnumMatching(_, _, ref fields) => {
let mut stmts = vec![];
if fields.is_empty() || fields[0].name.is_none() {
if !is_struct {
// tuple struct/"normal" variant
let expr = cx.expr_method_call(span,
fmt,
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/encodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
let encode = cx.ident_of("encode");

return match *substr.fields {
Struct(ref fields) => {
Struct(_, ref fields) => {
let emit_struct_field = cx.ident_of("emit_struct_field");
let mut stmts = Vec::new();
for (i, &FieldInfo {
Expand Down
21 changes: 13 additions & 8 deletions src/libsyntax_ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ pub enum StaticFields {

/// A summary of the possible sets of fields.
pub enum SubstructureFields<'a> {
Struct(Vec<FieldInfo<'a>>),
Struct(&'a ast::VariantData, Vec<FieldInfo<'a>>),
/// Matching variants of the enum: variant index, ast::Variant,
/// fields: the field name is only non-`None` in the case of a struct
/// variant.
Expand Down Expand Up @@ -981,7 +981,7 @@ impl<'a> MethodDef<'a> {
type_ident,
self_args,
nonself_args,
&Struct(fields));
&Struct(struct_def, fields));

// make a series of nested matches, to destructure the
// structs. This is actually right-to-left, but it shouldn't
Expand Down Expand Up @@ -1460,8 +1460,9 @@ impl<'a> TraitDef<'a> {
fields in generic `derive`"),
// named fields
(_, false) => Named(named_idents),
// tuple structs (includes empty structs)
(_, _) => Unnamed(just_spans)
// empty structs
_ if struct_def.is_struct() => Named(named_idents),
_ => Unnamed(just_spans),
}
}

Expand All @@ -1486,7 +1487,11 @@ impl<'a> TraitDef<'a> {
P<Expr>,
&'a [ast::Attribute])>) {
if struct_def.fields().is_empty() {
return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
if struct_def.is_struct() {
return (cx.pat_struct(self.span, struct_path, vec![]), vec![]);
} else {
return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
}
}

let mut paths = Vec::new();
Expand Down Expand Up @@ -1521,7 +1526,7 @@ impl<'a> TraitDef<'a> {

// struct_type is definitely not Unknown, since struct_def.fields
// must be nonempty to reach here
let pattern = if struct_type == Record {
let pattern = if struct_def.is_struct() {
let field_pats = subpats.into_iter().zip(&ident_expr)
.map(|(pat, &(_, id, _, _))| {
// id is guaranteed to be Some
Expand Down Expand Up @@ -1566,7 +1571,7 @@ pub fn cs_fold<F>(use_foldl: bool,
F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
EnumMatching(_, _, ref all_fields) | Struct(_, ref all_fields) => {
if use_foldl {
all_fields.iter().fold(base, |old, field| {
f(cx,
Expand Down Expand Up @@ -1612,7 +1617,7 @@ pub fn cs_same_method<F>(f: F,
F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
EnumMatching(_, _, ref all_fields) | Struct(_, ref all_fields) => {
// call self_n.method(other_1_n, other_2_n, ...)
let called = all_fields.iter().map(|field| {
cx.expr_method_call(field.span,
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure)
let mut stmts = Vec::new();

let fields = match *substr.fields {
Struct(ref fs) => fs,
Struct(_, ref fs) => fs,
EnumMatching(index, variant, ref fs) => {
// Determine the discriminant. We will feed this value to the byte
// iteration function.
Expand Down
2 changes: 1 addition & 1 deletion src/test/auxiliary/custom_derive_plugin_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn expand(cx: &mut ExtCtxt,
fn totalsum_substructure(cx: &mut ExtCtxt, trait_span: Span,
substr: &Substructure) -> P<ast::Expr> {
let fields = match *substr.fields {
Struct(ref fs) | EnumMatching(_, _, ref fs) => fs,
Struct(_, ref fs) | EnumMatching(_, _, ref fs) => fs,
_ => cx.span_bug(trait_span, "impossible substructure")
};

Expand Down
45 changes: 45 additions & 0 deletions src/test/run-pass/empty-struct-braces-derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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.

// `#[derive(Trait)]` works for empty structs/variants with braces

#![feature(braced_empty_structs)]
#![feature(rustc_private)]

extern crate serialize as rustc_serialize;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
Default, Debug, RustcEncodable, RustcDecodable)]
struct S {}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
Debug, RustcEncodable, RustcDecodable)]
enum E {
V {},
U,
}

fn main() {
let s = S {};
let s1 = s;
let s2 = s.clone();
assert_eq!(s, s1);
assert_eq!(s, s2);
assert!(!(s < s1));
assert_eq!(format!("{:?}", s), "S");

let e = E::V {};
let e1 = e;
let e2 = e.clone();
assert_eq!(e, e1);
assert_eq!(e, e2);
assert!(!(e < e1));
assert_eq!(format!("{:?}", e), "V");
}