Skip to content

Commit 62cb751

Browse files
committed
Improve Eq deriving
1 parent 2a2c9d3 commit 62cb751

File tree

7 files changed

+109
-36
lines changed

7 files changed

+109
-36
lines changed

src/libcore/cmp.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
129129
/// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
130130
/// no extra methods, it is only informing the compiler that this is an
131131
/// equivalence relation rather than a partial equivalence relation. Note that
132-
/// the `derive` strategy requires all fields are `PartialEq`, which isn't
132+
/// the `derive` strategy requires all fields are `Eq`, which isn't
133133
/// always desired.
134134
///
135135
/// ## How can I implement `Eq`?
@@ -165,6 +165,17 @@ pub trait Eq: PartialEq<Self> {
165165
fn assert_receiver_is_total_eq(&self) {}
166166
}
167167

168+
// FIXME: this struct is used solely by #[derive] to
169+
// assert that every component of a type implements Eq.
170+
//
171+
// This struct should never appear in user code.
172+
#[doc(hidden)]
173+
#[allow(missing_debug_implementations)]
174+
#[unstable(feature = "derive_eq",
175+
reason = "deriving hack, should not be public",
176+
issue = "0")]
177+
pub struct AssertParamIsEq<T: Eq + ?Sized> { _field: ::marker::PhantomData<T> }
178+
168179
/// An `Ordering` is the result of a comparison between two values.
169180
///
170181
/// # Examples

src/libsyntax/ext/build.rs

+18
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub trait AstBuilder {
9797
typ: P<ast::Ty>,
9898
ex: P<ast::Expr>)
9999
-> P<ast::Stmt>;
100+
fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt;
100101
fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt;
101102

102103
// blocks
@@ -577,6 +578,23 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
577578
})
578579
}
579580

581+
// Generate `let _: Type;`, usually used for type assertions.
582+
fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
583+
let local = P(ast::Local {
584+
pat: self.pat_wild(span),
585+
ty: Some(ty),
586+
init: None,
587+
id: ast::DUMMY_NODE_ID,
588+
span: span,
589+
attrs: ast::ThinVec::new(),
590+
});
591+
ast::Stmt {
592+
id: ast::DUMMY_NODE_ID,
593+
node: ast::StmtKind::Local(local),
594+
span: span,
595+
}
596+
}
597+
580598
fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
581599
ast::Stmt {
582600
id: ast::DUMMY_NODE_ID,

src/libsyntax_ext/deriving/clone.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,7 @@ fn cs_clone_shallow(name: &str,
115115
let assert_path = cx.path_all(span, true,
116116
cx.std_path(&["clone", helper_name]),
117117
vec![], vec![ty], vec![]);
118-
let local = P(ast::Local {
119-
pat: cx.pat_wild(span),
120-
ty: Some(cx.ty_path(assert_path)),
121-
init: None,
122-
id: ast::DUMMY_NODE_ID,
123-
span: span,
124-
attrs: ast::ThinVec::new(),
125-
});
126-
let stmt = ast::Stmt {
127-
id: ast::DUMMY_NODE_ID,
128-
node: ast::StmtKind::Local(local),
129-
span: span,
130-
};
131-
stmts.push(stmt);
118+
stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
132119
}
133120
fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
134121
for field in variant.fields() {

src/libsyntax_ext/deriving/cmp/eq.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use deriving::generic::*;
1212
use deriving::generic::ty::*;
1313

14-
use syntax::ast::{Expr, MetaItem};
14+
use syntax::ast::{self, Expr, MetaItem};
1515
use syntax::ext::base::{Annotatable, ExtCtxt};
1616
use syntax::ext::build::AstBuilder;
1717
use syntax::parse::token::InternedString;
@@ -23,22 +23,6 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
2323
mitem: &MetaItem,
2424
item: &Annotatable,
2525
push: &mut FnMut(Annotatable)) {
26-
fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
27-
cs_same_method(|cx, span, exprs| {
28-
// create `a.<method>(); b.<method>(); c.<method>(); ...`
29-
// (where method is `assert_receiver_is_total_eq`)
30-
let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect();
31-
let block = cx.block(span, stmts);
32-
cx.expr_block(block)
33-
},
34-
Box::new(|cx, sp, _, _| {
35-
cx.span_bug(sp, "non matching enums in derive(Eq)?")
36-
}),
37-
cx,
38-
span,
39-
substr)
40-
}
41-
4226
let inline = cx.meta_word(span, InternedString::new("inline"));
4327
let hidden = cx.meta_list_item_word(span, InternedString::new("hidden"));
4428
let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]);
@@ -50,7 +34,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
5034
additional_bounds: Vec::new(),
5135
generics: LifetimeBounds::empty(),
5236
is_unsafe: false,
53-
supports_unions: false,
37+
supports_unions: true,
5438
methods: vec![MethodDef {
5539
name: "assert_receiver_is_total_eq",
5640
generics: LifetimeBounds::empty(),
@@ -66,5 +50,38 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
6650
}],
6751
associated_types: Vec::new(),
6852
};
69-
trait_def.expand(cx, mitem, item, push)
53+
trait_def.expand_ext(cx, mitem, item, push, true)
54+
}
55+
56+
fn cs_total_eq_assert(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
57+
fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>,
58+
ty: P<ast::Ty>, span: Span, helper_name: &str) {
59+
// Generate statement `let _: helper_name<ty>;`,
60+
// set the expn ID so we can use the unstable struct.
61+
let span = super::allow_unstable(cx, span, "derive(Eq)");
62+
let assert_path = cx.path_all(span, true,
63+
cx.std_path(&["cmp", helper_name]),
64+
vec![], vec![ty], vec![]);
65+
stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
66+
}
67+
fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>, variant: &ast::VariantData) {
68+
for field in variant.fields() {
69+
// let _: AssertParamIsEq<FieldTy>;
70+
assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq");
71+
}
72+
}
73+
74+
let mut stmts = Vec::new();
75+
match *substr.fields {
76+
StaticStruct(vdata, ..) => {
77+
process_variant(cx, &mut stmts, vdata);
78+
}
79+
StaticEnum(enum_def, ..) => {
80+
for variant in &enum_def.variants {
81+
process_variant(cx, &mut stmts, &variant.node.data);
82+
}
83+
}
84+
_ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`")
85+
}
86+
cx.expr_block(cx.block(trait_span, stmts))
7087
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2016 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(untagged_unions)]
12+
13+
#[derive(Eq)] // OK
14+
union U1 {
15+
a: u8,
16+
}
17+
18+
impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } }
19+
20+
#[derive(PartialEq)]
21+
struct PartialEqNotEq;
22+
23+
#[derive(Eq)]
24+
union U2 {
25+
a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied
26+
}
27+
28+
impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } }
29+
30+
fn main() {}

src/test/compile-fail/union/union-derive.rs

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
#[derive(
1616
PartialEq, //~ ERROR this trait cannot be derived for unions
17-
Eq, //~ ERROR this trait cannot be derived for unions
1817
PartialOrd, //~ ERROR this trait cannot be derived for unions
1918
Ord, //~ ERROR this trait cannot be derived for unions
2019
Hash, //~ ERROR this trait cannot be derived for unions

src/test/run-pass/union/union-derive.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,33 @@
1515
#[derive(
1616
Copy,
1717
Clone,
18+
Eq,
1819
)]
1920
union U {
2021
a: u8,
2122
b: u16,
2223
}
2324

24-
#[derive(Clone, Copy)]
25+
impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } }
26+
27+
#[derive(
28+
Clone,
29+
Copy,
30+
Eq
31+
)]
2532
union W<T> {
2633
a: T,
2734
}
2835

36+
impl<T> PartialEq for W<T> { fn eq(&self, rhs: &Self) -> bool { true } }
37+
2938
fn main() {
3039
let u = U { b: 0 };
3140
let u1 = u;
3241
let u2 = u.clone();
42+
assert!(u1 == u2);
3343

3444
let w = W { a: 0 };
3545
let w1 = w.clone();
46+
assert!(w == w1);
3647
}

0 commit comments

Comments
 (0)