rollup merge of #19821: bkoropoff/issue-19791

Normalize late-bound regions in bare functions, stack closures, and traits and include them in the generated hash.

Closes #19791

r? @nikomatsakis (does my normalization make sense?)
cc @alexcrichton
This commit is contained in:
Alex Crichton 2014-12-17 08:34:04 -08:00
commit b5302217f0
3 changed files with 248 additions and 114 deletions

View File

@ -5825,126 +5825,153 @@ pub fn trait_item_of_item(tcx: &ctxt, def_id: ast::DefId)
/// context it's calculated within. This is used by the `type_id` intrinsic. /// context it's calculated within. This is used by the `type_id` intrinsic.
pub fn hash_crate_independent(tcx: &ctxt, ty: Ty, svh: &Svh) -> u64 { pub fn hash_crate_independent(tcx: &ctxt, ty: Ty, svh: &Svh) -> u64 {
let mut state = sip::SipState::new(); let mut state = sip::SipState::new();
macro_rules! byte( ($b:expr) => { ($b as u8).hash(&mut state) } ); helper(tcx, ty, svh, &mut state);
macro_rules! hash( ($e:expr) => { $e.hash(&mut state) } ); return state.result();
let region = |_state: &mut sip::SipState, r: Region| { fn helper(tcx: &ctxt, ty: Ty, svh: &Svh, state: &mut sip::SipState) {
match r { macro_rules! byte( ($b:expr) => { ($b as u8).hash(state) } );
ReStatic => {} macro_rules! hash( ($e:expr) => { $e.hash(state) } );
ReEmpty | let region = |state: &mut sip::SipState, r: Region| {
ReEarlyBound(..) | match r {
ReLateBound(..) | ReStatic => {}
ReFree(..) | ReLateBound(db, BrAnon(i)) => {
ReScope(..) | db.hash(state);
ReInfer(..) => { i.hash(state);
tcx.sess.bug("non-static region found when hashing a type") }
} ReEmpty |
} ReEarlyBound(..) |
}; ReLateBound(..) |
let did = |state: &mut sip::SipState, did: DefId| { ReFree(..) |
let h = if ast_util::is_local(did) { ReScope(..) |
svh.clone() ReInfer(..) => {
} else { tcx.sess.bug("unexpected region found when hashing a type")
tcx.sess.cstore.get_crate_hash(did.krate)
};
h.as_str().hash(state);
did.node.hash(state);
};
let mt = |state: &mut sip::SipState, mt: mt| {
mt.mutbl.hash(state);
};
ty::walk_ty(ty, |ty| {
match ty.sty {
ty_bool => byte!(2),
ty_char => byte!(3),
ty_int(i) => {
byte!(4);
hash!(i);
}
ty_uint(u) => {
byte!(5);
hash!(u);
}
ty_float(f) => {
byte!(6);
hash!(f);
}
ty_str => {
byte!(7);
}
ty_enum(d, _) => {
byte!(8);
did(&mut state, d);
}
ty_uniq(_) => {
byte!(9);
}
ty_vec(_, Some(n)) => {
byte!(10);
n.hash(&mut state);
}
ty_vec(_, None) => {
byte!(11);
}
ty_ptr(m) => {
byte!(12);
mt(&mut state, m);
}
ty_rptr(r, m) => {
byte!(13);
region(&mut state, r);
mt(&mut state, m);
}
ty_bare_fn(ref b) => {
byte!(14);
hash!(b.unsafety);
hash!(b.abi);
}
ty_closure(ref c) => {
byte!(15);
hash!(c.unsafety);
hash!(c.onceness);
hash!(c.bounds);
match c.store {
UniqTraitStore => byte!(0),
RegionTraitStore(r, m) => {
byte!(1)
region(&mut state, r);
assert_eq!(m, ast::MutMutable);
}
} }
} }
ty_trait(box TyTrait { ref principal, bounds }) => { };
byte!(17); let did = |state: &mut sip::SipState, did: DefId| {
did(&mut state, principal.def_id); let h = if ast_util::is_local(did) {
hash!(bounds); svh.clone()
} else {
tcx.sess.cstore.get_crate_hash(did.krate)
};
h.as_str().hash(state);
did.node.hash(state);
};
let mt = |state: &mut sip::SipState, mt: mt| {
mt.mutbl.hash(state);
};
let fn_sig = |state: &mut sip::SipState, sig: &FnSig| {
let sig = anonymize_late_bound_regions(tcx, sig);
for a in sig.inputs.iter() { helper(tcx, *a, svh, state); }
if let ty::FnConverging(output) = sig.output {
helper(tcx, output, svh, state);
} }
ty_struct(d, _) => { };
byte!(18); maybe_walk_ty(ty, |ty| {
did(&mut state, d); match ty.sty {
} ty_bool => byte!(2),
ty_tup(ref inner) => { ty_char => byte!(3),
byte!(19); ty_int(i) => {
hash!(inner.len()); byte!(4);
} hash!(i);
ty_param(p) => { }
byte!(20); ty_uint(u) => {
hash!(p.idx); byte!(5);
did(&mut state, p.def_id); hash!(u);
} }
ty_open(_) => byte!(22), ty_float(f) => {
ty_infer(_) => unreachable!(), byte!(6);
ty_err => byte!(23), hash!(f);
ty_unboxed_closure(d, r, _) => { }
byte!(24); ty_str => {
did(&mut state, d); byte!(7);
region(&mut state, r); }
} ty_enum(d, _) => {
} byte!(8);
}); did(state, d);
}
ty_uniq(_) => {
byte!(9);
}
ty_vec(_, Some(n)) => {
byte!(10);
n.hash(state);
}
ty_vec(_, None) => {
byte!(11);
}
ty_ptr(m) => {
byte!(12);
mt(state, m);
}
ty_rptr(r, m) => {
byte!(13);
region(state, r);
mt(state, m);
}
ty_bare_fn(ref b) => {
byte!(14);
hash!(b.unsafety);
hash!(b.abi);
fn_sig(state, &b.sig);
return false;
}
ty_closure(ref c) => {
byte!(15);
hash!(c.unsafety);
hash!(c.onceness);
hash!(c.bounds);
match c.store {
UniqTraitStore => byte!(0),
RegionTraitStore(r, m) => {
byte!(1);
region(state, r);
assert_eq!(m, ast::MutMutable);
}
}
state.result() fn_sig(state, &c.sig);
return false;
}
ty_trait(box TyTrait { ref principal, bounds }) => {
byte!(17);
did(state, principal.def_id);
hash!(bounds);
let principal = anonymize_late_bound_regions(tcx, principal);
for subty in principal.substs.types.iter() {
helper(tcx, *subty, svh, state);
}
return false;
}
ty_struct(d, _) => {
byte!(18);
did(state, d);
}
ty_tup(ref inner) => {
byte!(19);
hash!(inner.len());
}
ty_param(p) => {
byte!(20);
hash!(p.idx);
did(state, p.def_id);
}
ty_open(_) => byte!(22),
ty_infer(_) => unreachable!(),
ty_err => byte!(23),
ty_unboxed_closure(d, r, _) => {
byte!(24);
did(state, d);
region(state, r);
}
}
true
});
}
} }
impl Variance { impl Variance {
@ -6284,6 +6311,23 @@ pub fn erase_late_bound_regions<'tcx, HR>(
replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic).0 replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic).0
} }
/// Rewrite any late-bound regions so that they are anonymous. Region numbers are
/// assigned starting at 1 and increasing monotonically in the order traversed
/// by the fold operation.
///
/// The chief purpose of this function is to canonicalize regions so that two
/// `FnSig`s or `TraitRef`s which are equivalent up to region naming will become
/// structurally identical. For example, `for<'a, 'b> fn(&'a int, &'b int)` and
/// `for<'a, 'b> fn(&'b int, &'a int)` will become identical after anonymization.
pub fn anonymize_late_bound_regions<'tcx, HR>(tcx: &ctxt<'tcx>, sig: &HR) -> HR
where HR: HigherRankedFoldable<'tcx> {
let mut counter = 0;
replace_late_bound_regions(tcx, sig, |_, db| {
counter += 1;
ReLateBound(db, BrAnon(counter))
}).0
}
/// Replaces the late-bound-regions in `value` that are bound by `value`. /// Replaces the late-bound-regions in `value` that are bound by `value`.
pub fn replace_late_bound_regions<'tcx, HR, F>( pub fn replace_late_bound_regions<'tcx, HR, F>(
tcx: &ty::ctxt<'tcx>, tcx: &ty::ctxt<'tcx>,

View File

@ -1005,8 +1005,13 @@ impl<'a> State<'a> {
fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) -> IoResult<()> { fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) -> IoResult<()> {
if !t.bound_lifetimes.is_empty() { if !t.bound_lifetimes.is_empty() {
try!(word(&mut self.s, "for<")); try!(word(&mut self.s, "for<"));
let mut comma = false;
for lifetime_def in t.bound_lifetimes.iter() { for lifetime_def in t.bound_lifetimes.iter() {
if comma {
try!(self.word_space(","))
}
try!(self.print_lifetime_def(lifetime_def)); try!(self.print_lifetime_def(lifetime_def));
comma = true;
} }
try!(word(&mut self.s, ">")); try!(word(&mut self.s, ">"));
} }

View File

@ -0,0 +1,85 @@
// Copyright 2014 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.
// Test that type IDs correctly account for higher-rank lifetimes
// Also acts as a regression test for an ICE (issue #19791)
#![feature(unboxed_closures)]
use std::intrinsics::TypeId;
fn main() {
// Bare fns
{
let a = TypeId::of::<fn(&'static int, &'static int)>();
let b = TypeId::of::<for<'a> fn(&'static int, &'a int)>();
let c = TypeId::of::<for<'a, 'b> fn(&'a int, &'b int)>();
let d = TypeId::of::<for<'a, 'b> fn(&'b int, &'a int)>();
assert!(a != b);
assert!(a != c);
assert!(a != d);
assert!(b != c);
assert!(b != d);
assert_eq!(c, d);
// Make sure De Bruijn indices are handled correctly
let e = TypeId::of::<for<'a> fn(fn(&'a int) -> &'a int)>();
let f = TypeId::of::<fn(for<'a> fn(&'a int) -> &'a int)>();
assert!(e != f);
}
// Stack closures
{
let a = TypeId::of::<|&'static int, &'static int|>();
let b = TypeId::of::<for<'a> |&'static int, &'a int|>();
let c = TypeId::of::<for<'a, 'b> |&'a int, &'b int|>();
let d = TypeId::of::<for<'a, 'b> |&'b int, &'a int|>();
assert!(a != b);
assert!(a != c);
assert!(a != d);
assert!(b != c);
assert!(b != d);
assert_eq!(c, d);
// Make sure De Bruijn indices are handled correctly
let e = TypeId::of::<for<'a> |(|&'a int| -> &'a int)|>();
let f = TypeId::of::<|for<'a> |&'a int| -> &'a int|>();
assert!(e != f);
}
// Boxed unboxed closures
{
let a = TypeId::of::<Box<Fn(&'static int, &'static int)>>();
let b = TypeId::of::<Box<for<'a> Fn(&'static int, &'a int)>>();
let c = TypeId::of::<Box<for<'a, 'b> Fn(&'a int, &'b int)>>();
let d = TypeId::of::<Box<for<'a, 'b> Fn(&'b int, &'a int)>>();
assert!(a != b);
assert!(a != c);
assert!(a != d);
assert!(b != c);
assert!(b != d);
assert_eq!(c, d);
// Make sure De Bruijn indices are handled correctly
let e = TypeId::of::<Box<for<'a> Fn(Box<Fn(&'a int) -> &'a int>)>>();
let f = TypeId::of::<Box<Fn(Box<for<'a> Fn(&'a int) -> &'a int>)>>();
assert!(e != f);
}
// Raw unboxed closures
// Note that every unboxed closure has its own anonymous type,
// so no two IDs should equal each other, even when compatible
{
let a = id(|&: _: &int, _: &int| {});
let b = id(|&: _: &int, _: &int| {});
assert!(a != b);
}
fn id<T:'static>(_: T) -> TypeId {
TypeId::of::<T>()
}
}