From 5376b1c79870c80d0081540c72ae060d3ed5d1f5 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 5 Sep 2014 12:21:02 -0700 Subject: [PATCH] librustc: Parse and resolve higher-rank lifetimes in traits. They will ICE during typechecking if used, because they depend on trait reform. This is part of unboxed closures. --- src/librustc/middle/resolve_lifetime.rs | 74 ++++++++++++++++++- .../middle/typeck/infer/error_reporting.rs | 1 + src/libsyntax/ast.rs | 2 + src/libsyntax/ext/build.rs | 3 +- src/libsyntax/fold.rs | 15 +++- src/libsyntax/parse/parser.rs | 26 ++++++- src/libsyntax/print/pprust.rs | 10 +++ src/libsyntax/visit.rs | 5 +- .../compile-fail/regions-name-duplicated.rs | 1 + src/test/compile-fail/regions-name-static.rs | 1 + 10 files changed, 128 insertions(+), 10 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 22acdca5f9bd..42ae87712249 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -21,6 +21,7 @@ use driver::session::Session; use middle::subst; use syntax::ast; use syntax::codemap::Span; +use syntax::owned_slice::OwnedSlice; use syntax::parse::token::special_idents; use syntax::parse::token; use syntax::print::pprust::{lifetime_to_string}; @@ -98,8 +99,22 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { ast::ItemTy(_, ref generics) | ast::ItemEnum(_, ref generics) | ast::ItemStruct(_, ref generics) | - ast::ItemImpl(ref generics, _, _, _) | - ast::ItemTrait(ref generics, _, _, _) => &generics.lifetimes + ast::ItemTrait(ref generics, _, _, _) => { + self.with(|scope, f| { + f(EarlyScope(subst::TypeSpace, + &generics.lifetimes, + scope)) + }, |v| v.check_lifetime_defs(&generics.lifetimes)); + &generics.lifetimes + } + ast::ItemImpl(ref generics, _, _, _) => { + self.with(|scope, f| { + f(EarlyScope(subst::TypeSpace, + &generics.lifetimes, + scope)) + }, |v| v.check_lifetime_defs(&generics.lifetimes)); + &generics.lifetimes + } }; self.with(|_, f| f(EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE)), |v| { @@ -155,6 +170,20 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } self.resolve_lifetime_ref(lifetime_ref); } + + fn visit_generics(&mut self, generics: &ast::Generics) { + for ty_param in generics.ty_params.iter() { + self.visit_ty_param_bounds(&ty_param.bounds); + match ty_param.default { + Some(ref ty) => self.visit_ty(&**ty), + None => {} + } + } + for predicate in generics.where_clause.predicates.iter() { + self.visit_ident(predicate.span, predicate.ident); + self.visit_ty_param_bounds(&predicate.bounds); + } + } } impl<'a> LifetimeContext<'a> { @@ -167,6 +196,47 @@ impl<'a> LifetimeContext<'a> { })) } + fn visit_ty_param_bounds(&mut self, + bounds: &OwnedSlice) { + for bound in bounds.iter() { + match *bound { + ast::TraitTyParamBound(ref trait_ref) => { + self.visit_trait_ref(trait_ref); + } + ast::UnboxedFnTyParamBound(ref fn_decl) => { + self.visit_unboxed_fn_ty_param_bound(&**fn_decl); + } + ast::RegionTyParamBound(ref lifetime) => { + self.visit_lifetime_ref(lifetime); + } + } + } + } + + fn visit_trait_ref(&mut self, trait_ref: &ast::TraitRef) { + self.with(|scope, f| { + f(LateScope(trait_ref.ref_id, &trait_ref.lifetimes, scope)) + }, |v| { + v.check_lifetime_defs(&trait_ref.lifetimes); + for lifetime in trait_ref.lifetimes.iter() { + v.visit_lifetime_decl(lifetime); + } + v.visit_path(&trait_ref.path, trait_ref.ref_id); + }) + } + + fn visit_unboxed_fn_ty_param_bound(&mut self, + bound: &ast::UnboxedFnBound) { + self.with(|scope, f| { + f(LateScope(bound.ref_id, &bound.lifetimes, scope)) + }, |v| { + for argument in bound.decl.inputs.iter() { + v.visit_ty(&*argument.ty); + } + v.visit_ty(&*bound.decl.output); + }) + } + /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. fn visit_fn_decl(&mut self, n: ast::NodeId, diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 6ea80a59230a..d35885cbf17f 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -1110,6 +1110,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> { ast::TraitTyParamBound(ast::TraitRef { path: new_path, ref_id: tr.ref_id, + lifetimes: tr.lifetimes.clone(), }) } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 5c84745c20c6..74c69762be16 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -223,6 +223,7 @@ pub type TyParamBounds = OwnedSlice; pub struct UnboxedFnBound { pub path: Path, pub decl: P, + pub lifetimes: Vec, pub ref_id: NodeId, } @@ -1219,6 +1220,7 @@ pub struct Attribute_ { pub struct TraitRef { pub path: Path, pub ref_id: NodeId, + pub lifetimes: Vec, } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 16ecd83180ed..0586868eb458 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -435,7 +435,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn trait_ref(&self, path: ast::Path) -> ast::TraitRef { ast::TraitRef { path: path, - ref_id: ast::DUMMY_NODE_ID + ref_id: ast::DUMMY_NODE_ID, + lifetimes: Vec::new(), } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 7ebb11c148bd..91a339a73f7a 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -668,11 +668,13 @@ pub fn noop_fold_ty_param_bound(tpb: TyParamBound, fld: &mut T) UnboxedFnBound { ref path, ref decl, + ref lifetimes, ref_id } => { UnboxedFnTyParamBound(P(UnboxedFnBound { path: fld.fold_path(path.clone()), decl: fld.fold_fn_decl(decl.clone()), + lifetimes: fld.fold_lifetime_defs(lifetimes.clone()), ref_id: fld.new_id(ref_id), })) } @@ -808,10 +810,17 @@ pub fn noop_fold_struct_def(struct_def: P, fld: &mut T) -> }) } -pub fn noop_fold_trait_ref(TraitRef {ref_id, path}: TraitRef, fld: &mut T) -> TraitRef { - TraitRef { - ref_id: fld.new_id(ref_id), +pub fn noop_fold_trait_ref(p: TraitRef, fld: &mut T) -> TraitRef { + let id = fld.new_id(p.ref_id); + let TraitRef { + path, + lifetimes, + .. + } = p; + ast::TraitRef { path: fld.fold_path(path), + ref_id: id, + lifetimes: fld.fold_lifetime_defs(lifetimes), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 1ff6c3f9418e..cbc710821f93 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -34,7 +34,8 @@ use ast::{FnOnceUnboxedClosureKind}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod}; use ast::{Ident, NormalFn, Inherited, ImplItem, Item, Item_, ItemStatic}; use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl}; -use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy, Lit, Lit_}; +use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy}; +use ast::{LifetimeDef, Lit, Lit_}; use ast::{LitBool, LitChar, LitByte, LitBinary}; use ast::{LitNil, LitStr, LitInt, Local, LocalLet}; use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal}; @@ -3791,8 +3792,21 @@ impl<'a> Parser<'a> { { let mut result = vec!(); loop { + let lifetime_defs = if self.eat(&token::LT) { + let lifetime_defs = self.parse_lifetime_defs(); + self.expect_gt(); + lifetime_defs + } else { + Vec::new() + }; match self.token { token::LIFETIME(lifetime) => { + if lifetime_defs.len() > 0 { + let span = self.last_span; + self.span_err(span, "lifetime declarations are not \ + allowed here") + } + result.push(RegionTyParamBound(ast::Lifetime { id: ast::DUMMY_NODE_ID, span: self.span, @@ -3818,12 +3832,14 @@ impl<'a> Parser<'a> { cf: return_style, variadic: false, }), + lifetimes: lifetime_defs, ref_id: ast::DUMMY_NODE_ID, }))); } else { result.push(TraitTyParamBound(ast::TraitRef { path: path, ref_id: ast::DUMMY_NODE_ID, + lifetimes: lifetime_defs, })) } } @@ -3852,6 +3868,7 @@ impl<'a> Parser<'a> { ast::TraitRef { path: path, ref_id: ast::DUMMY_NODE_ID, + lifetimes: Vec::new(), } } @@ -4482,8 +4499,11 @@ impl<'a> Parser<'a> { // New-style trait. Reinterpret the type as a trait. let opt_trait_ref = match ty.node { TyPath(ref path, None, node_id) => { - Some(TraitRef { path: (*path).clone(), - ref_id: node_id }) + Some(TraitRef { + path: (*path).clone(), + ref_id: node_id, + lifetimes: Vec::new(), + }) } TyPath(_, Some(_), _) => { self.span_err(ty.span, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 473179a037a5..1fbd4af8627a 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -900,6 +900,16 @@ impl<'a> State<'a> { } fn print_trait_ref(&mut self, t: &ast::TraitRef) -> IoResult<()> { + if t.lifetimes.len() > 0 { + try!(self.print_generics(&ast::Generics { + lifetimes: t.lifetimes.clone(), + ty_params: OwnedSlice::empty(), + where_clause: ast::WhereClause { + id: ast::DUMMY_NODE_ID, + predicates: Vec::new(), + }, + })); + } self.print_path(&t.path, false) } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 32084856817e..3b2ed30b76d8 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -202,7 +202,9 @@ pub fn walk_explicit_self<'v, V: Visitor<'v>>(visitor: &mut V, /// Like with walk_method_helper this doesn't correspond to a method /// in Visitor, and so it gets a _helper suffix. -pub fn walk_trait_ref_helper<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitRef) { +pub fn walk_trait_ref_helper<'v,V>(visitor: &mut V, trait_ref: &'v TraitRef) + where V: Visitor<'v> { + walk_lifetime_decls(visitor, &trait_ref.lifetimes); visitor.visit_path(&trait_ref.path, trait_ref.ref_id) } @@ -495,6 +497,7 @@ pub fn walk_ty_param_bounds<'v, V: Visitor<'v>>(visitor: &mut V, visitor.visit_ty(&*argument.ty) } visitor.visit_ty(&*function_declaration.decl.output); + walk_lifetime_decls(visitor, &function_declaration.lifetimes); } RegionTyParamBound(ref lifetime) => { visitor.visit_lifetime_ref(lifetime); diff --git a/src/test/compile-fail/regions-name-duplicated.rs b/src/test/compile-fail/regions-name-duplicated.rs index 518fe0b00b6c..58eaa4c57fe1 100644 --- a/src/test/compile-fail/regions-name-duplicated.rs +++ b/src/test/compile-fail/regions-name-duplicated.rs @@ -9,6 +9,7 @@ // except according to those terms. struct Foo<'a, 'a> { //~ ERROR lifetime name `'a` declared twice +//~^ ERROR lifetime name `'a` declared twice x: &'a int } diff --git a/src/test/compile-fail/regions-name-static.rs b/src/test/compile-fail/regions-name-static.rs index 9f50ad366602..bc8ca87d7e29 100644 --- a/src/test/compile-fail/regions-name-static.rs +++ b/src/test/compile-fail/regions-name-static.rs @@ -9,6 +9,7 @@ // except according to those terms. struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `'static` +//~^ ERROR illegal lifetime parameter name: `'static` x: &'static int }