Skip to content

Commit f37d6fc

Browse files
committed
libsyntax: Do not derive Hash for Ident
1 parent f492ec4 commit f37d6fc

File tree

2 files changed

+29
-15
lines changed

2 files changed

+29
-15
lines changed

src/libsyntax/ast.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use ptr::P;
6565
use std::fmt;
6666
use std::rc::Rc;
6767
use std::borrow::Cow;
68+
use std::hash::{Hash, Hasher};
6869
use serialize::{Encodable, Decodable, Encoder, Decoder};
6970

7071
/// A name is a part of an identifier, representing a string or gensym. It's
@@ -84,7 +85,7 @@ pub struct SyntaxContext(pub u32);
8485
/// An identifier contains a Name (index into the interner
8586
/// table) and a SyntaxContext to track renaming and
8687
/// macro expansion per Flatt et al., "Macros That Work Together"
87-
#[derive(Clone, Copy, Eq, Hash)]
88+
#[derive(Clone, Copy, Eq)]
8889
pub struct Ident {
8990
pub name: Name,
9091
pub ctxt: SyntaxContext
@@ -133,22 +134,35 @@ impl Ident {
133134

134135
impl PartialEq for Ident {
135136
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.
144150
//
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+
149155
panic!("idents with different contexts are compared with operator `==`: \
150156
{:?}, {:?}.", self, other);
151157
}
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)
152166
}
153167
}
154168

src/libsyntax/ext/mtwt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use std::collections::HashMap;
3535
pub struct SCTable {
3636
table: RefCell<Vec<SyntaxContext_>>,
3737
mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
38-
rename_memo: RefCell<HashMap<(SyntaxContext,Name,SyntaxContext,Name),SyntaxContext>>,
38+
rename_memo: RefCell<HashMap<(SyntaxContext,(Name,SyntaxContext),Name),SyntaxContext>>,
3939
}
4040

4141
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
@@ -82,7 +82,7 @@ fn apply_rename_internal(id: Ident,
8282
to: Name,
8383
ctxt: SyntaxContext,
8484
table: &SCTable) -> SyntaxContext {
85-
let key = (ctxt, id.name, id.ctxt, to);
85+
let key = (ctxt, (id.name, id.ctxt), to);
8686

8787
*table.rename_memo.borrow_mut().entry(key).or_insert_with(|| {
8888
SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt)))

0 commit comments

Comments
 (0)