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.
pub fn hash_crate_independent(tcx: &ctxt, ty: Ty, svh: &Svh) -> u64 {
let mut state = sip::SipState::new();
macro_rules! byte( ($b:expr) => { ($b as u8).hash(&mut state) } );
macro_rules! hash( ($e:expr) => { $e.hash(&mut state) } );
helper(tcx, ty, svh, &mut state);
return state.result();
let region = |_state: &mut sip::SipState, r: Region| {
match r {
ReStatic => {}
fn helper(tcx: &ctxt, ty: Ty, svh: &Svh, state: &mut sip::SipState) {
macro_rules! byte( ($b:expr) => { ($b as u8).hash(state) } );
macro_rules! hash( ($e:expr) => { $e.hash(state) } );
ReEmpty |
ReEarlyBound(..) |
ReLateBound(..) |
ReFree(..) |
ReScope(..) |
ReInfer(..) => {
tcx.sess.bug("non-static region found when hashing a type")
}
}
};
let did = |state: &mut sip::SipState, did: DefId| {
let h = if ast_util::is_local(did) {
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);
};
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);
}
let region = |state: &mut sip::SipState, r: Region| {
match r {
ReStatic => {}
ReLateBound(db, BrAnon(i)) => {
db.hash(state);
i.hash(state);
}
ReEmpty |
ReEarlyBound(..) |
ReLateBound(..) |
ReFree(..) |
ReScope(..) |
ReInfer(..) => {
tcx.sess.bug("unexpected region found when hashing a type")
}
}
ty_trait(box TyTrait { ref principal, bounds }) => {
byte!(17);
did(&mut state, principal.def_id);
hash!(bounds);
};
let did = |state: &mut sip::SipState, did: DefId| {
let h = if ast_util::is_local(did) {
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);
did(&mut state, d);
}
ty_tup(ref inner) => {
byte!(19);
hash!(inner.len());
}
ty_param(p) => {
byte!(20);
hash!(p.idx);
did(&mut state, p.def_id);
}
ty_open(_) => byte!(22),
ty_infer(_) => unreachable!(),
ty_err => byte!(23),
ty_unboxed_closure(d, r, _) => {
byte!(24);
did(&mut state, d);
region(&mut state, r);
}
}
});
};
maybe_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(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 {
@ -6284,6 +6311,23 @@ pub fn erase_late_bound_regions<'tcx, HR>(
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`.
pub fn replace_late_bound_regions<'tcx, HR, F>(
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<()> {
if !t.bound_lifetimes.is_empty() {
try!(word(&mut self.s, "for<"));
let mut comma = false;
for lifetime_def in t.bound_lifetimes.iter() {
if comma {
try!(self.word_space(","))
}
try!(self.print_lifetime_def(lifetime_def));
comma = true;
}
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>()
}
}