make HR algorithms account for region subtyping

Currently, we consider region subtyping a failure
if a skolemized lifetime is relatable to any
other lifetime in any way at all. But a more precise
formulation is to say that a skolemized lifetime:

- must not have any *incoming* edges in the region graph
- only has *outgoing* edges to nodes that are `'static`

To enforce the latter requirement, we add edges from `'static -> 'x` for
each lifetime '`x' reachable from a skolemized region.

We now have to add a new `pop_skolemized` routine to do cleanup.
Whereas before if there were *any* edges relating to a skolemized
region, we would return `Err` and hence rollback the transaction, we now
tolerate some edges and return `Ok`. Therefore, the `pop_skolemized`
routine runs and cleans up those edges.
This commit is contained in:
Niko Matsakis 2016-04-20 19:51:56 -04:00
parent aecce2ba6e
commit 11984340bf
11 changed files with 639 additions and 168 deletions

View file

@ -0,0 +1,116 @@
// 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
}
}
}
// 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.
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
//[bound_a_b_vs_bound_a]~^^^^^^ ERROR compilation successful
//[bound_co_a_b_vs_bound_co_a]~^^^^^^^ ERROR compilation successful
//[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR compilation successful
//[bound_co_a_co_b_ret_contra_a]~^^^^^^^^^ ERROR compilation successful
}

View file

@ -28,8 +28,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 E0478
}
fn main() { }

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() { }