Auto merge of #33816 - nikomatsakis:projection-cache-2, r=arielb1

Projection cache and better warnings for #32330

This PR does three things:

- it lays the groundwork for the more precise subtyping rules discussed in #32330, but does not enable them;
- it issues warnings when the result of a leak-check or subtyping check relies on a late-bound region which will late become early-bound when #32330 is fixed;
- it introduces a cache for projection in the inference context.

I'm not 100% happy with the approach taken by the cache here, but it seems like a step in the right direction. It results in big wins on some test cases, but not as big as previous versions -- I think because it is caching the `Vec<Obligation>` (whereas before I just returned the normalized type with an empty vector). However, that change was needed to fix an ICE in @alexcrichton's future-rs module (I haven't fully tracked the cause of that ICE yet). Also, because trans/the collector use a fresh inference context for every call to `fulfill_obligation`, they don't profit nearly as much from this cache as they ought to.

Still, here are the results from the future-rs `retry.rs`:

```
06:26 <nmatsakis> time: 6.246; rss: 44MB  item-bodies checking
06:26 <nmatsakis> time: 54.783; rss: 63MB   translation item collection
06:26 <nmatsakis> time: 140.086; rss: 86MB    translation

06:26 <nmatsakis> time: 0.361; rss: 46MB  item-bodies checking
06:26 <nmatsakis> time: 5.299; rss: 63MB    translation item collection
06:26 <nmatsakis> time: 12.140; rss: 86MB translation
```

~~Another example is the example from #31849. For that, I get 34s to run item-bodies without any cache. The version of the cache included here takes 2s to run item-bodies type-checking. An alternative version which doesn't track nested obligations takes 0.2s, but that version ICEs on @alexcrichton's future-rs (and may well be incorrect, I've not fully convinced myself of that). So, a definite win, but I think there's definitely room for further progress.~~

Pushed a modified version which improves performance of the case from #31849:

```
lunch-box. time rustc --stage0 ~/tmp/issue-31849.rs  -Z no-trans
real    0m33.539s
user    0m32.932s
sys     0m0.570s
lunch-box. time rustc --stage2 ~/tmp/issue-31849.rs  -Z no-trans
real    0m0.195s
user    0m0.154s
sys     0m0.042s
```

Some sort of cache is also needed for unblocking further work on lazy normalization, since that will lean even more heavily on the cache, and will also require cycle detection.

r? @arielb1
This commit is contained in:
bors 2016-06-04 10:47:55 -07:00
commit 12238b984a
49 changed files with 2156 additions and 640 deletions

View file

@ -0,0 +1,39 @@
// Copyright 2012-2015 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.
#![feature(rustc_attrs)]
#![allow(warnings)]
pub type ParseResult<T> = Result<T, ()>;
pub enum Item<'a> { Literal(&'a str),
}
pub fn colon_or_space(s: &str) -> ParseResult<&str> {
unimplemented!()
}
pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str> {
unimplemented!()
}
pub fn parse<'a, I>(mut s: &str, items: I) -> ParseResult<()>
where I: Iterator<Item=Item<'a>> {
macro_rules! try_consume {
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
}
let offset = try_consume!(timezone_offset_zulu(s.trim_left(), colon_or_space));
let offset = try_consume!(timezone_offset_zulu(s.trim_left(), colon_or_space));
Ok(())
}
#[rustc_error]
fn main() { } //~ ERROR compilation successful

View file

@ -0,0 +1,34 @@
// 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.
#![feature(rustc_attrs)]
#![allow(warnings)]
// Check that you are allowed to implement using elision but write
// trait without elision (a bug in this cropped up during
// bootstrapping, so this is a regression test).
pub struct SplitWhitespace<'a> {
x: &'a u8
}
pub trait UnicodeStr {
fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>;
}
impl UnicodeStr for str {
#[inline]
fn split_whitespace(&self) -> SplitWhitespace {
unimplemented!()
}
}
#[rustc_error]
fn main() { } //~ ERROR compilation successful

View file

@ -0,0 +1,65 @@
// Copyright 2012 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.
#![feature(unboxed_closures)]
#![feature(rustc_attrs)]
// Test for projection cache. We should be able to project distinct
// lifetimes from `foo` as we reinstantiate it multiple times, but not
// if we do it just once. In this variant, the region `'a` is used in
// an contravariant position, which affects the results.
// revisions: ok oneuse transmute krisskross
#![allow(dead_code, unused_variables)]
fn foo<'a>() -> &'a u32 { loop { } }
fn bar<T>(t: T, x: T::Output) -> T::Output
where T: FnOnce<()>
{
t()
}
#[cfg(ok)] // two instantiations: OK
fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
let a = bar(foo, x);
let b = bar(foo, y);
(a, b)
}
#[cfg(oneuse)] // one instantiation: OK (surprisingly)
fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
let f /* : fn() -> &'static u32 */ = foo; // <-- inferred type annotated
let a = bar(f, x); // this is considered ok because fn args are contravariant...
let b = bar(f, y); // ...and hence we infer T to distinct values in each call.
(a, b)
}
// FIXME(#32330)
//#[cfg(transmute)] // one instantiations: BAD
//fn baz<'a,'b>(x: &'a u32) -> &'static u32 {
// bar(foo, x) //[transmute] ERROR E0495
//}
// FIXME(#32330)
//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
//fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
// let a = bar(foo, y); //[krisskross] ERROR E0495
// let b = bar(foo, x); //[krisskross] ERROR E0495
// (a, b)
//}
#[rustc_error]
fn main() { }
//[ok]~^ ERROR compilation successful
//[oneuse]~^^ ERROR compilation successful
//[transmute]~^^^ ERROR compilation successful
//[krisskross]~^^^^ ERROR compilation successful

View file

@ -0,0 +1,76 @@
// Copyright 2012 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.
#![feature(unboxed_closures)]
#![feature(rustc_attrs)]
// Test for projection cache. We should be able to project distinct
// lifetimes from `foo` as we reinstantiate it multiple times, but not
// if we do it just once. In this variant, the region `'a` is used in
// an invariant position, which affects the results.
// revisions: ok oneuse transmute krisskross
#![allow(dead_code, unused_variables)]
use std::marker::PhantomData;
struct Type<'a> {
// Invariant
data: PhantomData<fn(&'a u32) -> &'a u32>
}
fn foo<'a>() -> Type<'a> { loop { } }
fn bar<T>(t: T, x: T::Output) -> T::Output
where T: FnOnce<()>
{
t()
}
#[cfg(ok)] // two instantiations: OK
fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
let a = bar(foo, x);
let b = bar(foo, y);
(a, b)
}
// FIXME(#32330)
//#[cfg(oneuse)] // one instantiation: BAD
//fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
// let f = foo; // <-- No consistent type can be inferred for `f` here.
// let a = bar(f, x); //[oneuse] ERROR E0495
// let b = bar(f, y);
// (a, b)
//}
// FIXME(#32330)
//#[cfg(transmute)] // one instantiations: BAD
//fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
// // Cannot instantiate `foo` with any lifetime other than `'a`,
// // since it is provided as input.
//
// bar(foo, x) //[transmute] ERROR E0495
//}
// FIXME(#32330)
//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
//fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
// let a = bar(foo, y); //[krisskross] ERROR E0495
// let b = bar(foo, x); //[krisskross] ERROR E0495
// (a, b)
//}
#[rustc_error]
fn main() { }
//[ok]~^ ERROR compilation successful
//[oneuse]~^^ ERROR compilation successful
//[transmute]~^^^ ERROR compilation successful
//[krisskross]~^^^^ ERROR compilation successful

View file

@ -0,0 +1,49 @@
// 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.
// This test was derived from the wasm and parsell crates. They
// stopped compiling when #32330 is fixed.
#![allow(dead_code, unused_variables)]
#![deny(hr_lifetime_in_assoc_type)]
#![feature(unboxed_closures)]
use std::str::Chars;
pub trait HasOutput<Ch, Str> {
type Output;
}
#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
pub enum Token<'a> {
Begin(&'a str)
}
fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
unimplemented!()
}
fn foo<'a>(data: &mut Chars<'a>) {
bar(mk_unexpected_char_err)
//~^ ERROR lifetime parameter `'a` declared on fn `mk_unexpected_char_err`
//~| WARNING hard error in a future release
}
fn bar<F>(t: F)
// No type can satisfy this requirement, since `'a` does not
// appear in any of the input types:
where F: for<'a> Fn() -> Option<&'a i32>
//~^ ERROR associated type `Output` references lifetime `'a`, which does not
//~| WARNING hard error in a future release
{
}
fn main() {
}

View file

@ -0,0 +1,119 @@
// 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.
// Targeted tests for the higher-ranked subtyping code.
#![feature(rustc_attrs)]
#![allow(dead_code)]
// revisions: bound_a_vs_bound_a
// revisions: bound_a_vs_bound_b
// revisions: bound_inv_a_vs_bound_inv_b
// revisions: bound_co_a_vs_bound_co_b
// revisions: bound_a_vs_free_x
// revisions: free_x_vs_free_x
// revisions: free_x_vs_free_y
// revisions: free_inv_x_vs_free_inv_y
// revisions: bound_a_b_vs_bound_a
// revisions: bound_co_a_b_vs_bound_co_a
// revisions: bound_contra_a_contra_b_ret_co_a
// revisions: bound_co_a_co_b_ret_contra_a
// revisions: bound_inv_a_b_vs_bound_inv_a
// revisions: bound_a_b_ret_a_vs_bound_a_ret_a
fn gimme<T>(_: Option<T>) { }
struct Inv<'a> { x: *mut &'a u32 }
struct Co<'a> { x: fn(&'a u32) }
struct Contra<'a> { x: &'a u32 }
macro_rules! check {
($rev:ident: ($t1:ty, $t2:ty)) => {
#[cfg($rev)]
fn subtype<'x,'y:'x,'z:'y>() {
gimme::<$t2>(None::<$t1>);
//[free_inv_x_vs_free_inv_y]~^ ERROR mismatched types
}
#[cfg($rev)]
fn supertype<'x,'y:'x,'z:'y>() {
gimme::<$t1>(None::<$t2>);
//[bound_a_vs_free_x]~^ ERROR mismatched types
//[free_x_vs_free_y]~^^ ERROR mismatched types
//[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR mismatched types
//[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR mismatched types
//[free_inv_x_vs_free_inv_y]~^^^^^ ERROR mismatched types
//[bound_a_b_vs_bound_a]~^^^^^^ ERROR mismatched types
//[bound_co_a_b_vs_bound_co_a]~^^^^^^^ ERROR mismatched types
//[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR mismatched types
//[bound_co_a_co_b_ret_contra_a]~^^^^^^^^^ ERROR mismatched types
}
}
}
// If both have bound regions, they are equivalent, regardless of
// variant.
check! { bound_a_vs_bound_a: (for<'a> fn(&'a u32),
for<'a> fn(&'a u32)) }
check! { bound_a_vs_bound_b: (for<'a> fn(&'a u32),
for<'b> fn(&'b u32)) }
check! { bound_inv_a_vs_bound_inv_b: (for<'a> fn(Inv<'a>),
for<'b> fn(Inv<'b>)) }
check! { bound_co_a_vs_bound_co_b: (for<'a> fn(Co<'a>),
for<'b> fn(Co<'b>)) }
// Bound is a subtype of free.
check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
fn(&'x u32)) }
// Two free regions are relatable if subtyping holds.
check! { free_x_vs_free_x: (fn(&'x u32),
fn(&'x u32)) }
check! { free_x_vs_free_y: (fn(&'x u32),
fn(&'y u32)) }
check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
fn(Inv<'y>)) }
// Somewhat surprisingly, a fn taking two distinct bound lifetimes and
// a fn taking one bound lifetime can be interchangable, but only if
// we are co- or contra-variant with respect to both lifetimes.
//
// The reason is:
// - if we are covariant, then 'a and 'b can be set to the call-site
// intersection;
// - if we are contravariant, then 'a can be inferred to 'static.
//
// FIXME(#32330) this is true, but we are not currently impl'ing this
// full semantics
check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32),
for<'a> fn(&'a u32, &'a u32)) }
check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>),
for<'a> fn(Co<'a>, Co<'a>)) }
check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>,
for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>,
for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) }
// If we make those lifetimes invariant, then the two types are not interchangable.
check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
for<'a> fn(Inv<'a>, Inv<'a>)) }
check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32,
for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
#[rustc_error]
fn main() {
//[bound_a_vs_bound_a]~^ ERROR compilation successful
//[bound_a_vs_bound_b]~^^ ERROR compilation successful
//[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
//[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
//[free_x_vs_free_x]~^^^^^ ERROR compilation successful
}

View file

@ -28,7 +28,7 @@ impl<'a> Test<'a> for Foo<'a> {
impl<'a> NoLifetime for Foo<'a> {
fn get<'p, T : Test<'a>>(&self) -> T {
//~^ ERROR lifetime parameters or bounds on method `get` do not match the trait declaration
//~^ ERROR E0195
return *self as T;
}
}

View file

@ -39,7 +39,6 @@ impl<'a> Publisher<'a> for MyStruct<'a> {
// Not obvious, but there is an implicit lifetime here -------^
//~^^ ERROR cannot infer
//~| ERROR cannot infer
//~| ERROR cannot infer
//
// The fact that `Publisher` is using an implicit lifetime is
// what was causing the debruijn accounting to be off, so

View file

@ -14,7 +14,7 @@ use std::marker::PhantomData;
struct Bar<'x, 'y, 'z> { bar: &'y i32, baz: i32, marker: PhantomData<(&'x(),&'y(),&'z())> }
fn bar1<'a>(x: &Bar) -> (&'a i32, &'a i32, &'a i32) {
//~^ HELP: consider using an explicit lifetime parameter as shown: fn bar1<'a>(x: &'a Bar) -> (&'a i32, &'a i32, &'a i32)
//~^ HELP consider using an explicit lifetime parameter as shown: fn bar1<'b, 'c, 'a>(x: &'a Bar<'b, 'a, 'c>) -> (&'a i32, &'a i32, &'a i32)
(x.bar, &x.baz, &x.baz)
//~^ ERROR E0312
//~| ERROR cannot infer

View file

@ -49,7 +49,7 @@ struct Baz<'x> {
impl<'a> Baz<'a> {
fn baz2<'b>(&self, x: &isize) -> (&'b isize, &'b isize) {
//~^ HELP consider using an explicit lifetime parameter as shown: fn baz2<'b>(&self, x: &'b isize) -> (&'a isize, &'a isize)
//~^ HELP consider using an explicit lifetime parameter as shown: fn baz2<'b>(&self, x: &'a isize) -> (&'a isize, &'a isize)
(self.bar, x) //~ ERROR E0312
//~^ ERROR E0312
}

View file

@ -19,7 +19,7 @@ trait SomeTrait { fn get(&self) -> isize; }
fn make_object1<A:SomeTrait>(v: A) -> Box<SomeTrait+'static> {
box v as Box<SomeTrait+'static>
//~^ ERROR the parameter type `A` may not live long enough
//~^^ ERROR the parameter type `A` may not live long enough
//~| ERROR the parameter type `A` may not live long enough
}
fn make_object2<'a,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'a> {
@ -29,7 +29,7 @@ fn make_object2<'a,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'a> {
fn make_object3<'a,'b,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'b> {
box v as Box<SomeTrait+'b>
//~^ ERROR the parameter type `A` may not live long enough
//~^^ ERROR the parameter type `A` may not live long enough
//~| ERROR the parameter type `A` may not live long enough
}
fn main() { }

View file

@ -28,11 +28,7 @@ impl<'a> GetRef<'a> for Box<'a> {
impl<'a> Box<'a> {
fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
g2.get()
//~^ ERROR mismatched types
//~| expected type `&'a isize`
//~| found type `&'b isize`
//~| lifetime mismatch
//~^ ERROR E0312
}
}

View file

@ -27,7 +27,7 @@ impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> {
fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
g1.get()
//~^ ERROR mismatched types
//~^ ERROR E0312
}
fn main() {

View file

@ -10,7 +10,7 @@
struct Invariant<'a> {
f: Box<for<'b> FnOnce() -> &'b mut &'a isize + 'static>,
f: Box<FnOnce() -> *mut &'a isize + 'static>,
}
fn to_same_lifetime<'r>(b_isize: Invariant<'r>) {

View file

@ -15,10 +15,10 @@ trait Contravariant {
fn foo(&self) { }
}
impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) {
impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
}
impl Contravariant for for<'a> fn(&'a u8, &'a u8) {
impl Contravariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
}
///////////////////////////////////////////////////////////////////////////
@ -27,10 +27,10 @@ trait Covariant {
fn foo(&self) { }
}
impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) {
impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
}
impl Covariant for for<'a> fn(&'a u8, &'a u8) {
impl Covariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
}
///////////////////////////////////////////////////////////////////////////
@ -39,10 +39,10 @@ trait Invariant {
fn foo(&self) { }
}
impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) {
impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
}
impl Invariant for for<'a> fn(&'a u8, &'a u8) {
impl Invariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
}
fn main() { }

View file

@ -458,7 +458,7 @@ struct S<'a> {
}
impl<'a> Named for S<'a> {
fn new<'b>(name: &'static str) -> S<'b> {
fn new(name: &'static str) -> S<'a> {
S { name: name, mark: Cell::new(0), next: Cell::new(None) }
}
fn name(&self) -> &str { self.name }
@ -476,7 +476,7 @@ struct S2<'a> {
}
impl<'a> Named for S2<'a> {
fn new<'b>(name: &'static str) -> S2<'b> {
fn new(name: &'static str) -> S2<'a> {
S2 { name: name, mark: Cell::new(0), next: Cell::new((None, None)) }
}
fn name(&self) -> &str { self.name }
@ -496,7 +496,7 @@ struct V<'a> {
}
impl<'a> Named for V<'a> {
fn new<'b>(name: &'static str) -> V<'b> {
fn new(name: &'static str) -> V<'a> {
V { name: name,
mark: Cell::new(0),
contents: vec![Cell::new(None), Cell::new(None)]
@ -518,7 +518,7 @@ struct H<'a> {
}
impl<'a> Named for H<'a> {
fn new<'b>(name: &'static str) -> H<'b> {
fn new(name: &'static str) -> H<'a> {
H { name: name, mark: Cell::new(0), next: Cell::new(None) }
}
fn name(&self) -> &str { self.name }
@ -549,7 +549,7 @@ struct HM<'a> {
}
impl<'a> Named for HM<'a> {
fn new<'b>(name: &'static str) -> HM<'b> {
fn new(name: &'static str) -> HM<'a> {
HM { name: name,
mark: Cell::new(0),
contents: Cell::new(None)
@ -583,7 +583,7 @@ struct VD<'a> {
}
impl<'a> Named for VD<'a> {
fn new<'b>(name: &'static str) -> VD<'b> {
fn new(name: &'static str) -> VD<'a> {
VD { name: name,
mark: Cell::new(0),
contents: Cell::new(None)
@ -604,7 +604,7 @@ struct VM<'a> {
}
impl<'a> Named for VM<'a> {
fn new<'b>(name: &'static str) -> VM<'b> {
fn new(name: &'static str) -> VM<'a> {
VM { name: name,
mark: Cell::new(0),
contents: Cell::new(None)
@ -625,7 +625,7 @@ struct LL<'a> {
}
impl<'a> Named for LL<'a> {
fn new<'b>(name: &'static str) -> LL<'b> {
fn new(name: &'static str) -> LL<'a> {
LL { name: name,
mark: Cell::new(0),
contents: Cell::new(None)
@ -646,7 +646,7 @@ struct BH<'a> {
}
impl<'a> Named for BH<'a> {
fn new<'b>(name: &'static str) -> BH<'b> {
fn new(name: &'static str) -> BH<'a> {
BH { name: name,
mark: Cell::new(0),
contents: Cell::new(None)
@ -687,7 +687,7 @@ struct BTM<'a> {
}
impl<'a> Named for BTM<'a> {
fn new<'b>(name: &'static str) -> BTM<'b> {
fn new(name: &'static str) -> BTM<'a> {
BTM { name: name,
mark: Cell::new(0),
contents: Cell::new(None)
@ -728,7 +728,7 @@ struct BTS<'a> {
}
impl<'a> Named for BTS<'a> {
fn new<'b>(name: &'static str) -> BTS<'b> {
fn new(name: &'static str) -> BTS<'a> {
BTS { name: name,
mark: Cell::new(0),
contents: Cell::new(None)

View file

@ -0,0 +1,75 @@
// Copyright 2015 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.
// Regression test for #31849: the problem here was actually a performance
// cliff, but I'm adding the test for reference.
pub trait Upcast<T> {
fn upcast(self) -> T;
}
impl<S1, S2, T1, T2> Upcast<(T1, T2)> for (S1,S2)
where S1: Upcast<T1>,
S2: Upcast<T2>,
{
fn upcast(self) -> (T1, T2) { (self.0.upcast(), self.1.upcast()) }
}
impl Upcast<()> for ()
{
fn upcast(self) -> () { () }
}
pub trait ToStatic {
type Static: 'static;
fn to_static(self) -> Self::Static where Self: Sized;
}
impl<T, U> ToStatic for (T, U)
where T: ToStatic,
U: ToStatic
{
type Static = (T::Static, U::Static);
fn to_static(self) -> Self::Static { (self.0.to_static(), self.1.to_static()) }
}
impl ToStatic for ()
{
type Static = ();
fn to_static(self) -> () { () }
}
trait Factory {
type Output;
fn build(&self) -> Self::Output;
}
impl<S,T> Factory for (S, T)
where S: Factory,
T: Factory,
S::Output: ToStatic,
<S::Output as ToStatic>::Static: Upcast<S::Output>,
{
type Output = (S::Output, T::Output);
fn build(&self) -> Self::Output { (self.0.build().to_static().upcast(), self.1.build()) }
}
impl Factory for () {
type Output = ();
fn build(&self) -> Self::Output { () }
}
fn main() {
// More parens, more time.
let it = ((((((((((),()),()),()),()),()),()),()),()),());
it.build();
}