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:
commit
b5302217f0
@ -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>,
|
||||
|
@ -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, ">"));
|
||||
}
|
||||
|
85
src/test/run-pass/type-id-higher-rank.rs
Normal file
85
src/test/run-pass/type-id-higher-rank.rs
Normal 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>()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user