Skip to content

Allow quantification of lifetimes outside the self type in where clauses #21961

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 2 commits into from
Feb 10, 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
3 changes: 2 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ register_diagnostics! {
E0312, // lifetime of reference outlives lifetime of borrowed content
E0313, // lifetime of borrowed pointer outlives lifetime of captured variable
E0314, // closure outlives stack frame
E0315 // cannot invoke closure outside of its lifetime
E0315, // cannot invoke closure outside of its lifetime
E0316 // nested quantification of lifetimes
}

__build_diagnostic_array! { DIAGNOSTICS }
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,8 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
trait_ref: ast::TraitRef {
path: new_path,
ref_id: tr.ref_id,
}
},
span: poly_tr.span,
}, modifier)
}
}
Expand Down
64 changes: 52 additions & 12 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,31 @@ pub enum DefRegion {
/* lifetime decl */ ast::NodeId),
}

// maps the id of each lifetime reference to the lifetime decl
// that it corresponds to
// Maps the id of each lifetime reference to the lifetime decl
// that it corresponds to.
pub type NamedRegionMap = NodeMap<DefRegion>;

struct LifetimeContext<'a> {
sess: &'a Session,
named_region_map: &'a mut NamedRegionMap,
scope: Scope<'a>,
def_map: &'a DefMap,
// Deep breath. Our representation for poly trait refs contains a single
// binder and thus we only allow a single level of quantification. However,
// the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>`
// and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the de Bruijn indices
// correct when representing these constraints, we should only introduce one
// scope. However, we want to support both locations for the quantifier and
// during lifetime resolution we want precise information (so we can't
// desugar in an earlier phase).

// SO, if we encounter a quantifier at the outer scope, we set
// trait_ref_hack to true (and introduce a scope), and then if we encounter
// a quantifier at the inner scope, we error. If trait_ref_hack is false,
// then we introduce the scope at the inner quantifier.

// I'm sorry.
trait_ref_hack: bool,
}

enum ScopeChain<'a> {
Expand All @@ -80,6 +96,7 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio
named_region_map: &mut named_region_map,
scope: &ROOT_SCOPE,
def_map: def_map,
trait_ref_hack: false,
}, krate);
sess.abort_if_errors();
named_region_map
Expand Down Expand Up @@ -198,9 +215,22 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
match predicate {
&ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ ref bounded_ty,
ref bounds,
ref bound_lifetimes,
.. }) => {
self.visit_ty(&**bounded_ty);
visit::walk_ty_param_bounds_helper(self, bounds);
if bound_lifetimes.len() > 0 {
self.trait_ref_hack = true;
let result = self.with(LateScope(bound_lifetimes, self.scope),
|old_scope, this| {
this.check_lifetime_defs(old_scope, bound_lifetimes);
this.visit_ty(&**bounded_ty);
visit::walk_ty_param_bounds_helper(this, bounds);
});
self.trait_ref_hack = false;
result
} else {
self.visit_ty(&**bounded_ty);
visit::walk_ty_param_bounds_helper(self, bounds);
}
}
&ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime,
ref bounds,
Expand All @@ -222,18 +252,27 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
}
}

fn visit_poly_trait_ref(&mut self, trait_ref:
&ast::PolyTraitRef,
fn visit_poly_trait_ref(&mut self,
trait_ref: &ast::PolyTraitRef,
_modifier: &ast::TraitBoundModifier) {
debug!("visit_poly_trait_ref trait_ref={:?}", trait_ref);

self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |old_scope, this| {
this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes);
for lifetime in &trait_ref.bound_lifetimes {
this.visit_lifetime_def(lifetime);
if !self.trait_ref_hack || trait_ref.bound_lifetimes.len() > 0 {
if self.trait_ref_hack {
println!("{:?}", trait_ref.span);
span_err!(self.sess, trait_ref.span, E0316,
"nested quantification of lifetimes");
}
this.visit_trait_ref(&trait_ref.trait_ref)
})
self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |old_scope, this| {
this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes);
for lifetime in &trait_ref.bound_lifetimes {
this.visit_lifetime_def(lifetime);
}
this.visit_trait_ref(&trait_ref.trait_ref)
})
} else {
self.visit_trait_ref(&trait_ref.trait_ref)
}
}

fn visit_trait_ref(&mut self, trait_ref: &ast::TraitRef) {
Expand All @@ -251,6 +290,7 @@ impl<'a> LifetimeContext<'a> {
named_region_map: *named_region_map,
scope: &wrap_scope,
def_map: self.def_map,
trait_ref_hack: self.trait_ref_hack,
};
debug!("entering scope {:?}", this.scope);
f(self.scope, &mut this);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1478,7 +1478,7 @@ impl<'tcx> PolyTraitRef<'tcx> {
/// compiler's representation for things like `for<'a> Fn(&'a int)`
/// (which would be represented by the type `PolyTraitRef ==
/// Binder<TraitRef>`). Note that when we skolemize, instantiate,
/// erase, or otherwise "discharge" these bound reons, we change the
/// erase, or otherwise "discharge" these bound regions, we change the
/// type from `Binder<T>` to just `T` (see
/// e.g. `liberate_late_bound_regions`).
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ pub fn instantiate_poly_trait_ref<'tcx>(
{
let mut projections = Vec::new();

// the trait reference introduces a binding level here, so
// The trait reference introduces a binding level here, so
// we need to shift the `rscope`. It'd be nice if we could
// do away with this rscope stuff and work this knowledge
// into resolve_lifetimes, as we do with non-omitted
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ pub enum WherePredicate {
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct WhereBoundPredicate {
pub span: Span,
pub bound_lifetimes: Vec<LifetimeDef>,
pub bounded_ty: P<Ty>,
pub bounds: OwnedSlice<TyParamBound>,
}
Expand Down Expand Up @@ -1535,6 +1536,8 @@ pub struct PolyTraitRef {

/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`
pub trait_ref: TraitRef,

pub span: Span,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
Expand Down
9 changes: 5 additions & 4 deletions src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub trait AstBuilder {
default: Option<P<ast::Ty>>) -> ast::TyParam;

fn trait_ref(&self, path: ast::Path) -> ast::TraitRef;
fn poly_trait_ref(&self, path: ast::Path) -> ast::PolyTraitRef;
fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef;
fn typarambound(&self, path: ast::Path) -> ast::TyParamBound;
fn lifetime(&self, span: Span, ident: ast::Name) -> ast::Lifetime;
fn lifetime_def(&self,
Expand Down Expand Up @@ -442,15 +442,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
}
}

fn poly_trait_ref(&self, path: ast::Path) -> ast::PolyTraitRef {
fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {
ast::PolyTraitRef {
bound_lifetimes: Vec::new(),
trait_ref: self.trait_ref(path)
trait_ref: self.trait_ref(path),
span: span,
}
}

fn typarambound(&self, path: ast::Path) -> ast::TyParamBound {
ast::TraitTyParamBound(self.poly_trait_ref(path), ast::TraitBoundModifier::None)
ast::TraitTyParamBound(self.poly_trait_ref(path.span, path), ast::TraitBoundModifier::None)
}

fn lifetime(&self, span: Span, name: ast::Name) -> ast::Lifetime {
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ impl<'a> TraitDef<'a> {
ast::WherePredicate::BoundPredicate(ref wb) => {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
span: self.span,
bound_lifetimes: wb.bound_lifetimes.clone(),
bounded_ty: wb.bounded_ty.clone(),
bounds: OwnedSlice::from_vec(wb.bounds.iter().map(|b| b.clone()).collect())
})
Expand Down
7 changes: 5 additions & 2 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,10 +806,12 @@ pub fn noop_fold_where_predicate<T: Folder>(
fld: &mut T)
-> WherePredicate {
match pred {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{bounded_ty,
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{bound_lifetimes,
bounded_ty,
bounds,
span}) => {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
bound_lifetimes: fld.fold_lifetime_defs(bound_lifetimes),
bounded_ty: fld.fold_ty(bounded_ty),
bounds: bounds.move_map(|x| fld.fold_ty_param_bound(x)),
span: fld.new_span(span)
Expand Down Expand Up @@ -895,7 +897,8 @@ pub fn noop_fold_trait_ref<T: Folder>(p: TraitRef, fld: &mut T) -> TraitRef {
pub fn noop_fold_poly_trait_ref<T: Folder>(p: PolyTraitRef, fld: &mut T) -> PolyTraitRef {
ast::PolyTraitRef {
bound_lifetimes: fld.fold_lifetime_defs(p.bound_lifetimes),
trait_ref: fld.fold_trait_ref(p.trait_ref)
trait_ref: fld.fold_trait_ref(p.trait_ref),
span: fld.new_span(p.span),
}
}

Expand Down
33 changes: 28 additions & 5 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,8 @@ impl<'a> Parser<'a> {
*/

// parse <'lt>
let lo = self.span.lo;

let lifetime_defs = self.parse_late_bound_lifetime_defs();

// examine next token to decide to do
Expand All @@ -1047,9 +1049,11 @@ impl<'a> Parser<'a> {
self.token.is_ident() ||
self.token.is_path()
{
let hi = self.span.hi;
let trait_ref = self.parse_trait_ref();
let poly_trait_ref = ast::PolyTraitRef { bound_lifetimes: lifetime_defs,
trait_ref: trait_ref };
trait_ref: trait_ref,
span: mk_sp(lo, hi)};
let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
self.parse_ty_param_bounds(BoundParsingMode::Bare)
} else {
Expand Down Expand Up @@ -4070,7 +4074,8 @@ impl<'a> Parser<'a> {
if let Some(unbound) = unbound {
let mut bounds_as_vec = bounds.into_vec();
bounds_as_vec.push(TraitTyParamBound(PolyTraitRef { bound_lifetimes: vec![],
trait_ref: unbound },
trait_ref: unbound,
span: span },
TraitBoundModifier::Maybe));
bounds = OwnedSlice::from_vec(bounds_as_vec);
};
Expand Down Expand Up @@ -4223,6 +4228,16 @@ impl<'a> Parser<'a> {
}

_ => {
let bound_lifetimes = if self.eat_keyword(keywords::For) {
// Higher ranked constraint.
self.expect(&token::Lt);
let lifetime_defs = self.parse_lifetime_defs();
self.expect_gt();
lifetime_defs
} else {
vec![]
};

let bounded_ty = self.parse_ty();

if self.eat(&token::Colon) {
Expand All @@ -4233,12 +4248,13 @@ impl<'a> Parser<'a> {
if bounds.len() == 0 {
self.span_err(span,
"each predicate in a `where` clause must have \
at least one bound in it");
at least one bound in it");
}

generics.where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
ast::WhereBoundPredicate {
span: span,
bound_lifetimes: bound_lifetimes,
bounded_ty: bounded_ty,
bounds: bounds,
}));
Expand Down Expand Up @@ -4674,8 +4690,12 @@ impl<'a> Parser<'a> {

/// Parse trait Foo { ... }
fn parse_item_trait(&mut self, unsafety: Unsafety) -> ItemInfo {

let ident = self.parse_ident();
let mut tps = self.parse_generics();
// This is not very accurate, but since unbound only exists to catch
// obsolete syntax, the span is unlikely to ever be used.
let unbound_span = self.span;
let unbound = self.parse_for_sized();

// Parse supertrait bounds.
Expand All @@ -4684,7 +4704,8 @@ impl<'a> Parser<'a> {
if let Some(unbound) = unbound {
let mut bounds_as_vec = bounds.into_vec();
bounds_as_vec.push(TraitTyParamBound(PolyTraitRef { bound_lifetimes: vec![],
trait_ref: unbound },
trait_ref: unbound,
span: unbound_span },
TraitBoundModifier::Maybe));
bounds = OwnedSlice::from_vec(bounds_as_vec);
};
Expand Down Expand Up @@ -4803,11 +4824,13 @@ impl<'a> Parser<'a> {

/// Parse for<'l> a::B<String,i32>
fn parse_poly_trait_ref(&mut self) -> PolyTraitRef {
let lo = self.span.lo;
let lifetime_defs = self.parse_late_bound_lifetime_defs();

ast::PolyTraitRef {
bound_lifetimes: lifetime_defs,
trait_ref: self.parse_trait_ref()
trait_ref: self.parse_trait_ref(),
span: mk_sp(lo, self.last_span.hi),
}
}

Expand Down
14 changes: 10 additions & 4 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,11 +1025,11 @@ impl<'a> State<'a> {
self.print_path(&t.path, false)
}

fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) -> IoResult<()> {
if !t.bound_lifetimes.is_empty() {
fn print_formal_lifetime_list(&mut self, lifetimes: &[ast::LifetimeDef]) -> IoResult<()> {
if !lifetimes.is_empty() {
try!(word(&mut self.s, "for<"));
let mut comma = false;
for lifetime_def in &t.bound_lifetimes {
for lifetime_def in lifetimes {
if comma {
try!(self.word_space(","))
}
Expand All @@ -1038,7 +1038,11 @@ impl<'a> State<'a> {
}
try!(word(&mut self.s, ">"));
}
Ok(())
}

fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) -> IoResult<()> {
try!(self.print_formal_lifetime_list(&t.bound_lifetimes));
self.print_trait_ref(&t.trait_ref)
}

Expand Down Expand Up @@ -2517,9 +2521,11 @@ impl<'a> State<'a> {
}

match predicate {
&ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bounded_ty,
&ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bound_lifetimes,
ref bounded_ty,
ref bounds,
..}) => {
try!(self.print_formal_lifetime_list(bound_lifetimes));
try!(self.print_type(&**bounded_ty));
try!(self.print_bounds(":", bounds));
}
Expand Down
Loading