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>>, } +#[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 +{ + 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 `>::SomeType`. We are interested + // in looking for a trait definition like: + // + // ``` + // trait SomeTrait<'a> { + // type SomeType : 'a; + // } + // ``` + // + // we can thus deduce that `>::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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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: ::Input); +} + +impl Parser for () { + type Input = (); + fn parse(&mut self, input: ()) { + + } +} + +pub fn many() -> Box::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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait T0 { type O; } + +struct S(A); +impl T0 for S { type O = A; } + +trait T1: T0 { + // this looks okay but as we see below, `f` is unusable + fn m0::O) -> bool>(self, f: F) -> bool; +} + +// complains about the bounds on F here not being required by the trait +impl T1 for S { + fn m0 bool>(self, f: F) -> bool { f(self.0) } +} + +// // complains about mismatched types: as T0>::O vs. A +// impl T1 for S +// { +// fn m0::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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait T0 { type O; } + +struct S(A); +impl T0 for S { type O = A; } + +trait T1: T0 { + // this looks okay but as we see below, `f` is unusable + fn m0::O) -> bool>(self, f: F) -> bool; +} + +// complains about mismatched types: as T0>::O vs. A +impl T1 for S +{ + fn m0::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 or the MIT license +// , 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>; + /// 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> { fs::readdir(p) } + + fn prune(&self, _: &Path) -> bool { false } +} + +/// A directory walker of `P` using strategy `S`. +pub struct Subpaths { + stack: Vec, + strategy: S, +} + +impl Subpaths { + /// Create a directory walker with a root path and strategy. + pub fn new(p: &S::P, strategy: S) -> IoResult> { + let stack = try!(strategy.get_more(p)); + Ok(Subpaths { stack: stack, strategy: strategy }) + } +} + +impl Subpaths { + /// Create a directory walker with a root path and a default strategy. + pub fn walk(p: &S::P) -> IoResult> { + Subpaths::new(p, Default::default()) + } +} + +impl Default for Subpaths { + fn default() -> Subpaths { + Subpaths { stack: Vec::new(), strategy: Default::default() } + } +} + +impl Iterator for Subpaths { + type Item = S::P; + fn next (&mut self) -> Option { + 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 = 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 or the MIT license +// , 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 or the MIT license +// , 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() {} + +fn takes_foo() { + require_static::() +} + +fn main() { }