Skip to content

Commit 036d8c4

Browse files
committed
rollup merge of #21252: nikomatsakis/assoc-type-ice-hunt-take-2
Project region bounds out of the trait when deciding whether a projection type outlives a given regions. Fixes #20890. Fixes #21150.
2 parents f4df69a + 626db33 commit 036d8c4

File tree

10 files changed

+384
-2
lines changed

10 files changed

+384
-2
lines changed

src/librustc/middle/traits/fulfill.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub struct FulfillmentContext<'tcx> {
8484
region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>,
8585
}
8686

87+
#[derive(Clone)]
8788
pub struct RegionObligation<'tcx> {
8889
pub sub_region: ty::Region,
8990
pub sup_type: Ty<'tcx>,

src/librustc_typeck/check/regionck.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,25 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
297297
fn visit_region_obligations(&mut self, node_id: ast::NodeId)
298298
{
299299
debug!("visit_region_obligations: node_id={}", node_id);
300-
let fulfillment_cx = self.fcx.inh.fulfillment_cx.borrow();
301-
for r_o in fulfillment_cx.region_obligations(node_id).iter() {
300+
301+
// Make a copy of the region obligations vec because we'll need
302+
// to be able to borrow the fulfillment-cx below when projecting.
303+
let region_obligations =
304+
self.fcx.inh.fulfillment_cx.borrow()
305+
.region_obligations(node_id)
306+
.to_vec();
307+
308+
for r_o in region_obligations.iter() {
302309
debug!("visit_region_obligations: r_o={}",
303310
r_o.repr(self.tcx()));
304311
let sup_type = self.resolve_type(r_o.sup_type);
305312
let origin = infer::RelateRegionParamBound(r_o.cause.span);
306313
type_must_outlive(self, origin, sup_type, r_o.sub_region);
307314
}
315+
316+
// Processing the region obligations should not cause the list to grow further:
317+
assert_eq!(region_obligations.len(),
318+
self.fcx.inh.fulfillment_cx.borrow().region_obligations(node_id).len());
308319
}
309320

310321
/// This method populates the region map's `free_region_map`. It walks over the transformed
@@ -1480,6 +1491,15 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
14801491
generic.to_ty(rcx.tcx()),
14811492
param_env.caller_bounds.predicates.as_slice().to_vec());
14821493

1494+
// In the case of a projection T::Foo, we may be able to extract bounds from the trait def:
1495+
match *generic {
1496+
GenericKind::Param(..) => { }
1497+
GenericKind::Projection(ref projection_ty) => {
1498+
param_bounds.push_all(
1499+
&projection_bounds(rcx, origin.span(), projection_ty)[]);
1500+
}
1501+
}
1502+
14831503
// Add in the default bound of fn body that applies to all in
14841504
// scope type parameters:
14851505
param_bounds.push(param_env.implicit_region_bound);
@@ -1511,3 +1531,73 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
15111531
region,
15121532
param_bounds);
15131533
}
1534+
1535+
fn projection_bounds<'a,'tcx>(rcx: &Rcx<'a, 'tcx>,
1536+
span: Span,
1537+
projection_ty: &ty::ProjectionTy<'tcx>)
1538+
-> Vec<ty::Region>
1539+
{
1540+
let fcx = rcx.fcx;
1541+
let tcx = fcx.tcx();
1542+
let infcx = fcx.infcx();
1543+
1544+
debug!("projection_bounds(projection_ty={})",
1545+
projection_ty.repr(tcx));
1546+
1547+
let ty = ty::mk_projection(tcx, projection_ty.trait_ref.clone(), projection_ty.item_name);
1548+
1549+
// Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested
1550+
// in looking for a trait definition like:
1551+
//
1552+
// ```
1553+
// trait SomeTrait<'a> {
1554+
// type SomeType : 'a;
1555+
// }
1556+
// ```
1557+
//
1558+
// we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`.
1559+
let trait_def = ty::lookup_trait_def(tcx, projection_ty.trait_ref.def_id);
1560+
let predicates = trait_def.generics.predicates.as_slice().to_vec();
1561+
traits::elaborate_predicates(tcx, predicates)
1562+
.filter_map(|predicate| {
1563+
// we're only interesting in `T : 'a` style predicates:
1564+
let outlives = match predicate {
1565+
ty::Predicate::TypeOutlives(data) => data,
1566+
_ => { return None; }
1567+
};
1568+
1569+
debug!("projection_bounds: outlives={} (1)",
1570+
outlives.repr(tcx));
1571+
1572+
// apply the substitutions (and normalize any projected types)
1573+
let outlives = fcx.instantiate_type_scheme(span,
1574+
projection_ty.trait_ref.substs,
1575+
&outlives);
1576+
1577+
debug!("projection_bounds: outlives={} (2)",
1578+
outlives.repr(tcx));
1579+
1580+
let region_result = infcx.try(|_| {
1581+
let (outlives, _) =
1582+
infcx.replace_late_bound_regions_with_fresh_var(
1583+
span,
1584+
infer::AssocTypeProjection(projection_ty.item_name),
1585+
&outlives);
1586+
1587+
debug!("projection_bounds: outlives={} (3)",
1588+
outlives.repr(tcx));
1589+
1590+
// check whether this predicate applies to our current projection
1591+
match infer::mk_eqty(infcx, false, infer::Misc(span), ty, outlives.0) {
1592+
Ok(()) => { Ok(outlives.1) }
1593+
Err(_) => { Err(()) }
1594+
}
1595+
});
1596+
1597+
debug!("projection_bounds: region_result={}",
1598+
region_result.repr(tcx));
1599+
1600+
region_result.ok()
1601+
})
1602+
.collect()
1603+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that the compiler checks that arbitrary region bounds declared
12+
// in the trait must be satisfied on the impl. Issue #20890.
13+
14+
trait Foo<'a> { type Value: 'a; }
15+
16+
impl<'a> Foo<'a> for &'a i16 {
17+
// OK.
18+
type Value = &'a i32;
19+
}
20+
21+
impl<'a> Foo<'static> for &'a i32 {
22+
//~^ ERROR cannot infer
23+
type Value = &'a i32;
24+
}
25+
26+
impl<'a,'b> Foo<'b> for &'a i64 {
27+
//~^ ERROR cannot infer
28+
type Value = &'a i32;
29+
}
30+
31+
fn main() { }
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that the compiler checks that the 'static bound declared in
12+
// the trait must be satisfied on the impl. Issue #20890.
13+
14+
trait Foo { type Value: 'static; }
15+
16+
impl<'a> Foo for &'a i32 {
17+
//~^ ERROR cannot infer
18+
type Value = &'a i32;
19+
}
20+
21+
impl<'a> Foo for i32 {
22+
// OK.
23+
type Value = i32;
24+
}
25+
26+
fn main() { }

src/test/run-fail/issue-20971.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test for Issue #20971.
12+
13+
// error-pattern:Hello, world!
14+
15+
pub trait Parser {
16+
type Input;
17+
fn parse(&mut self, input: <Self as Parser>::Input);
18+
}
19+
20+
impl Parser for () {
21+
type Input = ();
22+
fn parse(&mut self, input: ()) {
23+
24+
}
25+
}
26+
27+
pub fn many() -> Box<Parser<Input=<() as Parser>::Input> + 'static> {
28+
panic!("Hello, world!")
29+
}
30+
31+
fn main() {
32+
many()
33+
.parse(());
34+
}

src/test/run-pass/issue-20763-1.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait T0 { type O; }
12+
13+
struct S<A>(A);
14+
impl<A> T0 for S<A> { type O = A; }
15+
16+
trait T1: T0 {
17+
// this looks okay but as we see below, `f` is unusable
18+
fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool;
19+
}
20+
21+
// complains about the bounds on F here not being required by the trait
22+
impl<A> T1 for S<A> {
23+
fn m0<F: Fn(A) -> bool>(self, f: F) -> bool { f(self.0) }
24+
}
25+
26+
// // complains about mismatched types: <S<A> as T0>::O vs. A
27+
// impl<A> T1 for S<A>
28+
// {
29+
// fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool { f(self.0) }
30+
// }
31+
32+
fn main() { }

src/test/run-pass/issue-20763-2.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait T0 { type O; }
12+
13+
struct S<A>(A);
14+
impl<A> T0 for S<A> { type O = A; }
15+
16+
trait T1: T0 {
17+
// this looks okay but as we see below, `f` is unusable
18+
fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool;
19+
}
20+
21+
// complains about mismatched types: <S<A> as T0>::O vs. A
22+
impl<A> T1 for S<A>
23+
{
24+
fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool { f(self.0) }
25+
}
26+
27+
fn main() { }

src/test/run-pass/issue-20797.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test for #20797.
12+
13+
use std::default::Default;
14+
use std::io::IoResult;
15+
use std::io::fs;
16+
use std::io::fs::PathExtensions;
17+
18+
/// A strategy for acquiring more subpaths to walk.
19+
pub trait Strategy {
20+
type P: PathExtensions;
21+
/// Get additional subpaths from a given path.
22+
fn get_more(&self, item: &Self::P) -> IoResult<Vec<Self::P>>;
23+
/// Determine whether a path should be walked further.
24+
/// This is run against each item from `get_more()`.
25+
fn prune(&self, p: &Self::P) -> bool;
26+
}
27+
28+
/// The basic fully-recursive strategy. Nothing is pruned.
29+
#[derive(Copy, Default)]
30+
pub struct Recursive;
31+
32+
impl Strategy for Recursive {
33+
type P = Path;
34+
fn get_more(&self, p: &Path) -> IoResult<Vec<Path>> { fs::readdir(p) }
35+
36+
fn prune(&self, _: &Path) -> bool { false }
37+
}
38+
39+
/// A directory walker of `P` using strategy `S`.
40+
pub struct Subpaths<S: Strategy> {
41+
stack: Vec<S::P>,
42+
strategy: S,
43+
}
44+
45+
impl<S: Strategy> Subpaths<S> {
46+
/// Create a directory walker with a root path and strategy.
47+
pub fn new(p: &S::P, strategy: S) -> IoResult<Subpaths<S>> {
48+
let stack = try!(strategy.get_more(p));
49+
Ok(Subpaths { stack: stack, strategy: strategy })
50+
}
51+
}
52+
53+
impl<S: Default + Strategy> Subpaths<S> {
54+
/// Create a directory walker with a root path and a default strategy.
55+
pub fn walk(p: &S::P) -> IoResult<Subpaths<S>> {
56+
Subpaths::new(p, Default::default())
57+
}
58+
}
59+
60+
impl<S: Default + Strategy> Default for Subpaths<S> {
61+
fn default() -> Subpaths<S> {
62+
Subpaths { stack: Vec::new(), strategy: Default::default() }
63+
}
64+
}
65+
66+
impl<S: Strategy> Iterator for Subpaths<S> {
67+
type Item = S::P;
68+
fn next (&mut self) -> Option<S::P> {
69+
let mut opt_path = self.stack.pop();
70+
while opt_path.is_some() && self.strategy.prune(opt_path.as_ref().unwrap()) {
71+
opt_path = self.stack.pop();
72+
}
73+
match opt_path {
74+
Some(path) => {
75+
if PathExtensions::is_dir(&path) {
76+
let result = self.strategy.get_more(&path);
77+
match result {
78+
Ok(dirs) => { self.stack.extend(dirs.into_iter()); },
79+
Err(..) => { }
80+
}
81+
}
82+
Some(path)
83+
}
84+
None => None,
85+
}
86+
}
87+
}
88+
89+
fn main() {
90+
let mut walker: Subpaths<Recursive> = Subpaths::walk(&Path::new("/home")).unwrap();
91+
}

0 commit comments

Comments
 (0)