@@ -65,6 +65,7 @@ use ptr::P;
65
65
use std:: fmt;
66
66
use std:: rc:: Rc ;
67
67
use std:: borrow:: Cow ;
68
+ use std:: hash:: { Hash , Hasher } ;
68
69
use serialize:: { Encodable , Decodable , Encoder , Decoder } ;
69
70
70
71
/// A name is a part of an identifier, representing a string or gensym. It's
@@ -84,7 +85,7 @@ pub struct SyntaxContext(pub u32);
84
85
/// An identifier contains a Name (index into the interner
85
86
/// table) and a SyntaxContext to track renaming and
86
87
/// macro expansion per Flatt et al., "Macros That Work Together"
87
- #[ derive( Clone , Copy , Eq , Hash ) ]
88
+ #[ derive( Clone , Copy , Eq ) ]
88
89
pub struct Ident {
89
90
pub name : Name ,
90
91
pub ctxt : SyntaxContext
@@ -133,22 +134,35 @@ impl Ident {
133
134
134
135
impl PartialEq for Ident {
135
136
fn eq ( & self , other : & Ident ) -> bool {
136
- if self . ctxt == other. ctxt {
137
- self . name == other. name
138
- } else {
139
- // IF YOU SEE ONE OF THESE FAILS: it means that you're comparing
140
- // idents that have different contexts. You can't fix this without
141
- // knowing whether the comparison should be hygienic or non-hygienic.
142
- // if it should be non-hygienic (most things are), just compare the
143
- // 'name' fields of the idents.
137
+ if self . ctxt != other. ctxt {
138
+ // There's no one true way to compare Idents. They can be compared
139
+ // non-hygienically `id1.name == id2.name`, hygienically
140
+ // `mtwt::resolve(id1) == mtwt::resolve(id2)`, or even member-wise
141
+ // `(id1.name, id1.ctxt) == (id2.name, id2.ctxt)` depending on the situation.
142
+ // Ideally, PartialEq should not be implemented for Ident at all, but that
143
+ // would be too impractical, because many larger structures (Token, in particular)
144
+ // including Idents as their parts derive PartialEq and use it for non-hygienic
145
+ // comparisons. That's why PartialEq is implemented and defaults to non-hygienic
146
+ // comparison. Hash is implemented too and is consistent with PartialEq, i.e. only
147
+ // the name of Ident is hashed. Still try to avoid comparing idents in your code
148
+ // (especially as keys in hash maps), use one of the three methods listed above
149
+ // explicitly.
144
150
//
145
- // On the other hand, if the comparison does need to be hygienic,
146
- // one example and its non-hygienic counterpart would be:
147
- // syntax::parse::token::Token::mtwt_eq
148
- // syntax::ext::tt::macro_parser::token_name_eq
151
+ // If you see this panic, then some idents from different contexts were compared
152
+ // non-hygienically. It's likely a bug. Use one of the three comparison methods
153
+ // listed above explicitly.
154
+
149
155
panic ! ( "idents with different contexts are compared with operator `==`: \
150
156
{:?}, {:?}.", self , other) ;
151
157
}
158
+
159
+ self . name == other. name
160
+ }
161
+ }
162
+
163
+ impl Hash for Ident {
164
+ fn hash < H : Hasher > ( & self , state : & mut H ) {
165
+ self . name . hash ( state)
152
166
}
153
167
}
154
168
0 commit comments