From 13e7f9c0a7ced7c20ea909e46bc6c520a9a2b260 Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sat, 13 Dec 2014 14:40:38 -0800 Subject: [PATCH 1/3] Handle higher-rank lifetimes when generating type IDs Normalize late-bound regions in bare functions, stack closures, and traits and include them in the generated hash. Closes #19791 --- src/librustc/middle/ty.rs | 272 ++++++++++++++++++++++---------------- 1 file changed, 158 insertions(+), 114 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 50c324c49c382..3a59fb5016075 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 { @@ -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>, From 3925b4d5c94c10f6e3cff05afcb5866d62a7235c Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sat, 13 Dec 2014 14:42:38 -0800 Subject: [PATCH 2/3] Add regression test for #19791 --- src/test/run-pass/type-id-higher-rank.rs | 85 ++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/run-pass/type-id-higher-rank.rs diff --git a/src/test/run-pass/type-id-higher-rank.rs b/src/test/run-pass/type-id-higher-rank.rs new file mode 100644 index 0000000000000..efda7771403a3 --- /dev/null +++ b/src/test/run-pass/type-id-higher-rank.rs @@ -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 or the MIT license +// , 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::(); + let b = TypeId::of:: fn(&'static int, &'a int)>(); + let c = TypeId::of:: fn(&'a int, &'b int)>(); + let d = TypeId::of:: 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:: fn(fn(&'a int) -> &'a int)>(); + let f = TypeId::of:: fn(&'a int) -> &'a int)>(); + assert!(e != f); + } + // Stack closures + { + let a = TypeId::of::<|&'static int, &'static int|>(); + let b = TypeId::of:: |&'static int, &'a int|>(); + let c = TypeId::of:: |&'a int, &'b int|>(); + let d = TypeId::of:: |&'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:: |(|&'a int| -> &'a int)|>(); + let f = TypeId::of::<|for<'a> |&'a int| -> &'a int|>(); + assert!(e != f); + } + // Boxed unboxed closures + { + let a = TypeId::of::>(); + let b = TypeId::of:: Fn(&'static int, &'a int)>>(); + let c = TypeId::of:: Fn(&'a int, &'b int)>>(); + let d = TypeId::of:: 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:: Fn(Box &'a int>)>>(); + let f = TypeId::of:: 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) -> TypeId { + TypeId::of::() + } +} From 0a1798dd1e7fad9f04a99c58623661ffb1747de0 Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sat, 13 Dec 2014 19:11:04 -0800 Subject: [PATCH 3/3] Fix pretty printing of HRTB syntax --- src/libsyntax/print/pprust.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index db122f271a9c4..b08cf1112fbcf 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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, ">")); }