Skip to content

Handle higher-rank lifetimes when generating type IDs #19821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 158 additions & 114 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) } );

let region = |_state: &mut sip::SipState, r: Region| {
match r {
ReStatic => {}

ReEmpty |
ReEarlyBound(..) |
ReLateBound(..) |
ReFree(..) |
ReScope(..) |
ReInfer(..) => {
tcx.sess.bug("non-static region found when hashing a type")
helper(tcx, ty, svh, &mut state);
return state.result();

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) } );

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")
}
}
}
};
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);
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_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);
};
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);
}
}

fn_sig(state, &c.sig);

return false;
}
}
ty_trait(box TyTrait { ref principal, bounds }) => {
byte!(17);
did(&mut state, principal.def_id);
hash!(bounds);
}
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);
}
}
});
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);
}

state.result()
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 {
Expand Down Expand Up @@ -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>,
Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ">"));
}
Expand Down
85 changes: 85 additions & 0 deletions src/test/run-pass/type-id-higher-rank.rs
Original file line number Diff line number Diff line change
@@ -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>()
}
}