about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/traits/fulfill.rs1
-rw-r--r--src/librustc_typeck/check/regionck.rs94
-rw-r--r--src/test/compile-fail/regions-assoc-type-region-bound-in-trait-not-met.rs31
-rw-r--r--src/test/compile-fail/regions-assoc-type-static-bound-in-trait-not-met.rs26
-rw-r--r--src/test/run-fail/issue-20971.rs34
-rw-r--r--src/test/run-pass/issue-20763-1.rs32
-rw-r--r--src/test/run-pass/issue-20763-2.rs27
-rw-r--r--src/test/run-pass/issue-20797.rs91
-rw-r--r--src/test/run-pass/regions-assoc-type-region-bound.rs28
-rw-r--r--src/test/run-pass/regions-assoc-type-static-bound.rs22
10 files changed, 384 insertions, 2 deletions
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 42624555ae4..568286e39d5 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -84,6 +84,7 @@ pub struct FulfillmentContext<'tcx> {
     region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>,
 }
 
+#[derive(Clone)]
 pub struct RegionObligation<'tcx> {
     pub sub_region: ty::Region,
     pub sup_type: Ty<'tcx>,
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 3b5027dbb9e..6403c1af8e1 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -297,14 +297,25 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
     fn visit_region_obligations(&mut self, node_id: ast::NodeId)
     {
         debug!("visit_region_obligations: node_id={}", node_id);
-        let fulfillment_cx = self.fcx.inh.fulfillment_cx.borrow();
-        for r_o in fulfillment_cx.region_obligations(node_id).iter() {
+
+        // Make a copy of the region obligations vec because we'll need
+        // to be able to borrow the fulfillment-cx below when projecting.
+        let region_obligations =
+            self.fcx.inh.fulfillment_cx.borrow()
+                                       .region_obligations(node_id)
+                                       .to_vec();
+
+        for r_o in region_obligations.iter() {
             debug!("visit_region_obligations: r_o={}",
                    r_o.repr(self.tcx()));
             let sup_type = self.resolve_type(r_o.sup_type);
             let origin = infer::RelateRegionParamBound(r_o.cause.span);
             type_must_outlive(self, origin, sup_type, r_o.sub_region);
         }
+
+        // Processing the region obligations should not cause the list to grow further:
+        assert_eq!(region_obligations.len(),
+                   self.fcx.inh.fulfillment_cx.borrow().region_obligations(node_id).len());
     }
 
     /// 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>,
                                    generic.to_ty(rcx.tcx()),
                                    param_env.caller_bounds.predicates.as_slice().to_vec());
 
+    // In the case of a projection T::Foo, we may be able to extract bounds from the trait def:
+    match *generic {
+        GenericKind::Param(..) => { }
+        GenericKind::Projection(ref projection_ty) => {
+            param_bounds.push_all(
+                &projection_bounds(rcx, origin.span(), projection_ty)[]);
+        }
+    }
+
     // Add in the default bound of fn body that applies to all in
     // scope type parameters:
     param_bounds.push(param_env.implicit_region_bound);
@@ -1511,3 +1531,73 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
                                          region,
                                          param_bounds);
 }
+
+fn projection_bounds<'a,'tcx>(rcx: &Rcx<'a, 'tcx>,
+                              span: Span,
+                              projection_ty: &ty::ProjectionTy<'tcx>)
+                              -> Vec<ty::Region>
+{
+    let fcx = rcx.fcx;
+    let tcx = fcx.tcx();
+    let infcx = fcx.infcx();
+
+    debug!("projection_bounds(projection_ty={})",
+           projection_ty.repr(tcx));
+
+    let ty = ty::mk_projection(tcx, projection_ty.trait_ref.clone(), projection_ty.item_name);
+
+    // Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested
+    // in looking for a trait definition like:
+    //
+    // ```
+    // trait SomeTrait<'a> {
+    //     type SomeType : 'a;
+    // }
+    // ```
+    //
+    // we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`.
+    let trait_def = ty::lookup_trait_def(tcx, projection_ty.trait_ref.def_id);
+    let predicates = trait_def.generics.predicates.as_slice().to_vec();
+    traits::elaborate_predicates(tcx, predicates)
+        .filter_map(|predicate| {
+            // we're only interesting in `T : 'a` style predicates:
+            let outlives = match predicate {
+                ty::Predicate::TypeOutlives(data) => data,
+                _ => { return None; }
+            };
+
+            debug!("projection_bounds: outlives={} (1)",
+                   outlives.repr(tcx));
+
+            // apply the substitutions (and normalize any projected types)
+            let outlives = fcx.instantiate_type_scheme(span,
+                                                       projection_ty.trait_ref.substs,
+                                                       &outlives);
+
+            debug!("projection_bounds: outlives={} (2)",
+                   outlives.repr(tcx));
+
+            let region_result = infcx.try(|_| {
+                let (outlives, _) =
+                    infcx.replace_late_bound_regions_with_fresh_var(
+                        span,
+                        infer::AssocTypeProjection(projection_ty.item_name),
+                        &outlives);
+
+                debug!("projection_bounds: outlives={} (3)",
+                       outlives.repr(tcx));
+
+                // check whether this predicate applies to our current projection
+                match infer::mk_eqty(infcx, false, infer::Misc(span), ty, outlives.0) {
+                    Ok(()) => { Ok(outlives.1) }
+                    Err(_) => { Err(()) }
+                }
+            });
+
+            debug!("projection_bounds: region_result={}",
+                   region_result.repr(tcx));
+
+            region_result.ok()
+        })
+        .collect()
+}
diff --git a/src/test/compile-fail/regions-assoc-type-region-bound-in-trait-not-met.rs b/src/test/compile-fail/regions-assoc-type-region-bound-in-trait-not-met.rs
new file mode 100644
index 00000000000..f833361e3b5
--- /dev/null
+++ b/src/test/compile-fail/regions-assoc-type-region-bound-in-trait-not-met.rs
@@ -0,0 +1,31 @@
+// 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.
+
+// Test that the compiler checks that arbitrary region bounds declared
+// in the trait must be satisfied on the impl. Issue #20890.
+
+trait Foo<'a> { type Value: 'a; }
+
+impl<'a> Foo<'a> for &'a i16 {
+    // OK.
+    type Value = &'a i32;
+}
+
+impl<'a> Foo<'static> for &'a i32 {
+    //~^ ERROR cannot infer
+    type Value = &'a i32;
+}
+
+impl<'a,'b> Foo<'b> for &'a i64 {
+    //~^ ERROR cannot infer
+    type Value = &'a i32;
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/regions-assoc-type-static-bound-in-trait-not-met.rs b/src/test/compile-fail/regions-assoc-type-static-bound-in-trait-not-met.rs
new file mode 100644
index 00000000000..0871d8b01f6
--- /dev/null
+++ b/src/test/compile-fail/regions-assoc-type-static-bound-in-trait-not-met.rs
@@ -0,0 +1,26 @@
+// 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.
+
+// Test that the compiler checks that the 'static bound declared in
+// the trait must be satisfied on the impl. Issue #20890.
+
+trait Foo { type Value: 'static; }
+
+impl<'a> Foo for &'a i32 {
+    //~^ ERROR cannot infer
+    type Value = &'a i32;
+}
+
+impl<'a> Foo for i32 {
+    // OK.
+    type Value = i32;
+}
+
+fn main() { }
diff --git a/src/test/run-fail/issue-20971.rs b/src/test/run-fail/issue-20971.rs
new file mode 100644
index 00000000000..818f0e13941
--- /dev/null
+++ b/src/test/run-fail/issue-20971.rs
@@ -0,0 +1,34 @@
+// 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 Issue #20971.
+
+// error-pattern:Hello, world!
+
+pub trait Parser {
+    type Input;
+    fn parse(&mut self, input: <Self as Parser>::Input);
+}
+
+impl Parser for () {
+    type Input = ();
+    fn parse(&mut self, input: ()) {
+
+    }
+}
+
+pub fn many() -> Box<Parser<Input=<() as Parser>::Input> + 'static> {
+    panic!("Hello, world!")
+}
+
+fn main() {
+    many()
+        .parse(());
+}
diff --git a/src/test/run-pass/issue-20763-1.rs b/src/test/run-pass/issue-20763-1.rs
new file mode 100644
index 00000000000..911ee715da2
--- /dev/null
+++ b/src/test/run-pass/issue-20763-1.rs
@@ -0,0 +1,32 @@
+// 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.
+
+trait T0 { type O; }
+
+struct S<A>(A);
+impl<A> T0 for S<A> { type O = A; }
+
+trait T1: T0 {
+    // this looks okay but as we see below, `f` is unusable
+    fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool;
+}
+
+// complains about the bounds on F here not being required by the trait
+impl<A> T1 for S<A> {
+    fn m0<F: Fn(A) -> bool>(self, f: F) -> bool { f(self.0) }
+}
+
+// // complains about mismatched types: <S<A> as T0>::O vs. A
+// impl<A> T1 for S<A>
+// {
+//     fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool { f(self.0) }
+// }
+
+fn main() { }
diff --git a/src/test/run-pass/issue-20763-2.rs b/src/test/run-pass/issue-20763-2.rs
new file mode 100644
index 00000000000..a17c7b6ade4
--- /dev/null
+++ b/src/test/run-pass/issue-20763-2.rs
@@ -0,0 +1,27 @@
+// 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.
+
+trait T0 { type O; }
+
+struct S<A>(A);
+impl<A> T0 for S<A> { type O = A; }
+
+trait T1: T0 {
+    // this looks okay but as we see below, `f` is unusable
+    fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool;
+}
+
+// complains about mismatched types: <S<A> as T0>::O vs. A
+impl<A> T1 for S<A>
+{
+    fn m0<F: Fn(<Self as T0>::O) -> bool>(self, f: F) -> bool { f(self.0) }
+}
+
+fn main() { }
diff --git a/src/test/run-pass/issue-20797.rs b/src/test/run-pass/issue-20797.rs
new file mode 100644
index 00000000000..c4f3c33f269
--- /dev/null
+++ b/src/test/run-pass/issue-20797.rs
@@ -0,0 +1,91 @@
+// 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 #20797.
+
+use std::default::Default;
+use std::io::IoResult;
+use std::io::fs;
+use std::io::fs::PathExtensions;
+
+/// A strategy for acquiring more subpaths to walk.
+pub trait Strategy {
+  type P: PathExtensions;
+  /// Get additional subpaths from a given path.
+  fn get_more(&self, item: &Self::P) -> IoResult<Vec<Self::P>>;
+  /// Determine whether a path should be walked further.
+  /// This is run against each item from `get_more()`.
+  fn prune(&self, p: &Self::P) -> bool;
+}
+
+/// The basic fully-recursive strategy. Nothing is pruned.
+#[derive(Copy, Default)]
+pub struct Recursive;
+
+impl Strategy for Recursive {
+  type P = Path;
+  fn get_more(&self, p: &Path) -> IoResult<Vec<Path>> { fs::readdir(p) }
+
+  fn prune(&self, _: &Path) -> bool { false }
+}
+
+/// A directory walker of `P` using strategy `S`.
+pub struct Subpaths<S: Strategy> {
+    stack: Vec<S::P>,
+    strategy: S,
+}
+
+impl<S: Strategy> Subpaths<S> {
+  /// Create a directory walker with a root path and strategy.
+  pub fn new(p: &S::P, strategy: S) -> IoResult<Subpaths<S>> {
+    let stack = try!(strategy.get_more(p));
+    Ok(Subpaths { stack: stack, strategy: strategy })
+  }
+}
+
+impl<S: Default + Strategy> Subpaths<S> {
+  /// Create a directory walker with a root path and a default strategy.
+  pub fn walk(p: &S::P) -> IoResult<Subpaths<S>> {
+      Subpaths::new(p, Default::default())
+  }
+}
+
+impl<S: Default + Strategy> Default for Subpaths<S> {
+  fn default() -> Subpaths<S> {
+    Subpaths { stack: Vec::new(), strategy: Default::default() }
+  }
+}
+
+impl<S: Strategy> Iterator for Subpaths<S> {
+  type Item = S::P;
+  fn next (&mut self) -> Option<S::P> {
+    let mut opt_path = self.stack.pop();
+    while opt_path.is_some() && self.strategy.prune(opt_path.as_ref().unwrap()) {
+      opt_path = self.stack.pop();
+    }
+    match opt_path {
+      Some(path) => {
+        if PathExtensions::is_dir(&path) {
+          let result = self.strategy.get_more(&path);
+          match result {
+            Ok(dirs) => { self.stack.extend(dirs.into_iter()); },
+            Err(..) => { }
+          }
+        }
+        Some(path)
+      }
+      None => None,
+    }
+  }
+}
+
+fn main() {
+  let mut walker: Subpaths<Recursive> = Subpaths::walk(&Path::new("/home")).unwrap();
+}
diff --git a/src/test/run-pass/regions-assoc-type-region-bound.rs b/src/test/run-pass/regions-assoc-type-region-bound.rs
new file mode 100644
index 00000000000..77e1a214764
--- /dev/null
+++ b/src/test/run-pass/regions-assoc-type-region-bound.rs
@@ -0,0 +1,28 @@
+// 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.
+
+// Test that the compiler considers the 'a bound declared in the
+// trait. Issue #20890.
+
+trait Foo<'a> {
+    type Value: 'a;
+
+    fn get(&self) -> &'a Self::Value;
+}
+
+fn takes_foo<'a,F: Foo<'a>>(f: &'a F) {
+    // This call would be illegal, because it results in &'a F::Value,
+    // and the only way we know that `F::Value : 'a` is because of the
+    // trait declaration.
+
+    f.get();
+}
+
+fn main() { }
diff --git a/src/test/run-pass/regions-assoc-type-static-bound.rs b/src/test/run-pass/regions-assoc-type-static-bound.rs
new file mode 100644
index 00000000000..6b629a9035d
--- /dev/null
+++ b/src/test/run-pass/regions-assoc-type-static-bound.rs
@@ -0,0 +1,22 @@
+// 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.
+
+// Test that the compiler considers the 'static bound declared in the
+// trait. Issue #20890.
+
+trait Foo { type Value: 'static; }
+
+fn require_static<T: 'static>() {}
+
+fn takes_foo<F: Foo>() {
+    require_static::<F::Value>()
+}
+
+fn main() { }