Skip to content

Commit 6e98896

Browse files
committed
derive: improve hygiene for type parameters (see #2810)
When deriving Hash, RustcEncodable and RustcDecodable, the syntax extension needs a type parameter to use in the inner method. They used to use __H, __S and __D respectively. If this conflicts with a type parameter already declared for the item, bad times result (see the test). There is no hygiene for type parameters, but this commit introduces a better heuristic by concatenating the names of all extant type parameters (and prepending __H).
1 parent 3c19df6 commit 6e98896

File tree

6 files changed

+49
-17
lines changed

6 files changed

+49
-17
lines changed

src/libsyntax_ext/deriving/cmp/ord.rs

+1-1
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::{MetaItem, Expr, BinOpKind, self};
14+
use syntax::ast::{MetaItem, Expr, self};
1515
use syntax::codemap::Span;
1616
use syntax::ext::base::{ExtCtxt, Annotatable};
1717
use syntax::ext::build::AstBuilder;

src/libsyntax_ext/deriving/decodable.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
//! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
1212
13+
use deriving;
1314
use deriving::generic::*;
1415
use deriving::generic::ty::*;
1516

@@ -54,6 +55,8 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
5455
return
5556
}
5657

58+
let typaram = &*deriving::hygienic_type_parameter(item, "__D");
59+
5760
let trait_def = TraitDef {
5861
span: span,
5962
attributes: Vec::new(),
@@ -66,18 +69,17 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
6669
name: "decode",
6770
generics: LifetimeBounds {
6871
lifetimes: Vec::new(),
69-
bounds: vec!(("__D", vec!(Path::new_(
70-
vec!(krate, "Decoder"), None,
71-
vec!(), true))))
72+
bounds: vec![(typaram,
73+
vec![Path::new_(vec!(krate, "Decoder"), None, vec!(), true)])]
7274
},
7375
explicit_self: None,
74-
args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))),
76+
args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
7577
Borrowed(None, Mutability::Mutable))),
7678
ret_ty: Literal(Path::new_(
7779
pathvec_std!(cx, core::result::Result),
7880
None,
7981
vec!(Box::new(Self_), Box::new(Literal(Path::new_(
80-
vec!["__D", "Error"], None, vec![], false
82+
vec![typaram, "Error"], None, vec![], false
8183
)))),
8284
true
8385
)),

src/libsyntax_ext/deriving/encodable.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
//! }
8989
//! ```
9090
91+
use deriving;
9192
use deriving::generic::*;
9293
use deriving::generic::ty::*;
9394

@@ -130,6 +131,8 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
130131
return;
131132
}
132133

134+
let typaram = &*deriving::hygienic_type_parameter(item, "__S");
135+
133136
let trait_def = TraitDef {
134137
span: span,
135138
attributes: Vec::new(),
@@ -142,18 +145,17 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
142145
name: "encode",
143146
generics: LifetimeBounds {
144147
lifetimes: Vec::new(),
145-
bounds: vec!(("__S", vec!(Path::new_(
146-
vec!(krate, "Encoder"), None,
147-
vec!(), true))))
148+
bounds: vec![(typaram,
149+
vec![Path::new_(vec![krate, "Encoder"], None, vec!(), true)])]
148150
},
149151
explicit_self: borrowed_explicit_self(),
150-
args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))),
152+
args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
151153
Borrowed(None, Mutability::Mutable))),
152154
ret_ty: Literal(Path::new_(
153155
pathvec_std!(cx, core::result::Result),
154156
None,
155157
vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
156-
vec!["__S", "Error"], None, vec![], false
158+
vec![typaram, "Error"], None, vec![], false
157159
)))),
158160
true
159161
)),

src/libsyntax_ext/deriving/hash.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use deriving;
1112
use deriving::generic::*;
1213
use deriving::generic::ty::*;
1314

@@ -26,7 +27,10 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
2627

2728
let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None,
2829
vec!(), true);
29-
let arg = Path::new_local("__H");
30+
31+
let typaram = &*deriving::hygienic_type_parameter(item, "__H");
32+
33+
let arg = Path::new_local(typaram);
3034
let hash_trait_def = TraitDef {
3135
span: span,
3236
attributes: Vec::new(),
@@ -39,7 +43,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
3943
name: "hash",
4044
generics: LifetimeBounds {
4145
lifetimes: Vec::new(),
42-
bounds: vec![("__H",
46+
bounds: vec![(typaram,
4347
vec![path_std!(cx, core::hash::Hasher)])],
4448
},
4549
explicit_self: borrowed_explicit_self(),

src/libsyntax_ext/deriving/mod.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@
99
// except according to those terms.
1010

1111
//! The compiler code necessary to implement the `#[derive]` extensions.
12-
//!
13-
//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
14-
//! the standard library, and "std" is the core library.
1512
16-
use syntax::ast::{MetaItem, MetaItemKind};
13+
use syntax::ast::{MetaItem, MetaItemKind, self};
1714
use syntax::attr::AttrMetaMethods;
1815
use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable};
1916
use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
@@ -197,3 +194,26 @@ fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
197194
name, replacement));
198195
}
199196
}
197+
198+
/// Construct a name for the inner type parameter that can't collide with any type parameters of
199+
/// the item. This is achieved by starting with a base and then concatenating the names of all
200+
/// other type parameters.
201+
fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
202+
let mut typaram = String::from(base);
203+
if let Annotatable::Item(ref item) = *item {
204+
match item.node {
205+
ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
206+
ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
207+
208+
for ty in ty_params.iter() {
209+
typaram.push_str(&ty.ident.name.as_str());
210+
}
211+
}
212+
213+
_ => {}
214+
}
215+
}
216+
217+
typaram
218+
}
219+

src/test/run-pass/deriving-hash.rs

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ struct Person {
2424
#[derive(Hash)]
2525
enum E { A=1, B }
2626

27+
// test for hygiene name collisions
28+
#[derive(Hash)] struct __H__H;
29+
#[derive(Hash)] enum Collision<__H> { __H { __H__H: __H } }
30+
2731
fn hash<T: Hash>(t: &T) -> u64 {
2832
let mut s = SipHasher::new_with_keys(0, 0);
2933
t.hash(&mut s);

0 commit comments

Comments
 (0)