Skip to content

Fixes for region bounds in traits #21252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc/middle/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
94 changes: 92 additions & 2 deletions src/librustc_typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
@@ -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() { }
Original file line number Diff line number Diff line change
@@ -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() { }
34 changes: 34 additions & 0 deletions src/test/run-fail/issue-20971.rs
Original file line number Diff line number Diff line change
@@ -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(());
}
32 changes: 32 additions & 0 deletions src/test/run-pass/issue-20763-1.rs
Original file line number Diff line number Diff line change
@@ -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() { }
27 changes: 27 additions & 0 deletions src/test/run-pass/issue-20763-2.rs
Original file line number Diff line number Diff line change
@@ -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() { }
91 changes: 91 additions & 0 deletions src/test/run-pass/issue-20797.rs
Original file line number Diff line number Diff line change
@@ -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();
}
Loading