diff options
| author | bors <bors@rust-lang.org> | 2014-08-14 16:36:19 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-08-14 16:36:19 +0000 |
| commit | 404978ea722c0257cc763540c93243e8a21f82ed (patch) | |
| tree | c6106879c326924dc3d39ce71c0a797901bafe04 /src/libsyntax | |
| parent | 56b86aaf35d96ec8cf8555205426eaed62427963 (diff) | |
| parent | 8d272321417df3a954802e42a66adda87ade5a49 (diff) | |
| download | rust-404978ea722c0257cc763540c93243e8a21f82ed.tar.gz rust-404978ea722c0257cc763540c93243e8a21f82ed.zip | |
auto merge of #16122 : pcwalton/rust/lifetimes-in-unboxed-closures, r=pnkfelix
This patch primarily does two things: (1) it prevents lifetimes from
leaking out of unboxed closures; (2) it allows unboxed closure type
notation, call notation, and construction notation to construct closures
matching any of the three traits.
This breaks code that looked like:
let mut f;
{
let x = &5i;
f = |&mut:| *x + 10;
}
Change this code to avoid having a reference escape. For example:
{
let x = &5i;
let mut f; // <-- move here to avoid dangling reference
f = |&mut:| *x + 10;
}
I believe this is enough to consider unboxed closures essentially
implemented. Further issues (for example, higher-rank lifetimes) should
be filed as followups.
Closes #14449.
[breaking-change]
r? @pnkfelix
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 167 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 67 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 2 |
5 files changed, 152 insertions, 99 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 46039a70fb2..7d9a23e91ba 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -528,7 +528,7 @@ pub enum Expr_ { ExprMatch(Gc<Expr>, Vec<Arm>), ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>), ExprProc(P<FnDecl>, P<Block>), - ExprUnboxedFn(CaptureClause, P<FnDecl>, P<Block>), + ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>), ExprBlock(P<Block>), ExprAssign(Gc<Expr>, Gc<Expr>), @@ -900,6 +900,7 @@ pub struct BareFnTy { #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub struct UnboxedFnTy { + pub kind: UnboxedClosureKind, pub decl: P<FnDecl>, } @@ -1297,6 +1298,13 @@ pub enum ForeignItem_ { ForeignItemStatic(P<Ty>, /* is_mutbl */ bool), } +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub enum UnboxedClosureKind { + FnUnboxedClosureKind, + FnMutUnboxedClosureKind, + FnOnceUnboxedClosureKind, +} + /// The data we save and restore about an inlined item or method. This is not /// part of the AST that we parse from a file, but it becomes part of the tree /// that we trans. diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 02f1f4646a4..cebe1862528 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -368,6 +368,7 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> { TyUnboxedFn(ref f) => { TyUnboxedFn(box(GC) UnboxedFnTy { decl: fld.fold_fn_decl(&*f.decl), + kind: f.kind, }) } TyTup(ref tys) => TyTup(tys.iter().map(|&ty| fld.fold_ty(ty)).collect()), @@ -641,6 +642,7 @@ pub fn noop_fold_ty_param_bound<T: Folder>(tpb: &TyParamBound, fld: &mut T) UnboxedFnTyParamBound(ref unboxed_function_type) => { UnboxedFnTyParamBound(UnboxedFnTy { decl: fld.fold_fn_decl(&*unboxed_function_type.decl), + kind: unboxed_function_type.kind, }) } OtherRegionTyParamBound(s) => OtherRegionTyParamBound(s) @@ -1103,8 +1105,9 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> { ExprProc(folder.fold_fn_decl(&**decl), folder.fold_block(body.clone())) } - ExprUnboxedFn(capture_clause, ref decl, ref body) => { + ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => { ExprUnboxedFn(capture_clause, + kind, folder.fold_fn_decl(&**decl), folder.fold_block(*body)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f0920603ad1..f6db577a004 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -30,6 +30,8 @@ use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; use ast::{ExprVec, ExprVstore, ExprVstoreSlice}; use ast::{ExprVstoreMutSlice, ExprWhile, ExprForLoop, Field, FnDecl}; use ast::{ExprVstoreUniq, Once, Many}; +use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind}; +use ast::{FnOnceUnboxedClosureKind}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod}; use ast::{Ident, NormalFn, Inherited, Item, Item_, ItemStatic}; use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl}; @@ -53,7 +55,8 @@ use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; use ast::{TyNil, TyParam, TyParamBound, TyParen, TyPath, TyPtr, TyRptr}; use ast::{TyTup, TyU32, TyUnboxedFn, TyUniq, TyVec, UnUniq}; -use ast::{UnboxedFnTy, UnboxedFnTyParamBound, UnnamedField, UnsafeBlock}; +use ast::{UnboxedClosureKind, UnboxedFnTy, UnboxedFnTyParamBound}; +use ast::{UnnamedField, UnsafeBlock}; use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::Visibility; @@ -1087,6 +1090,34 @@ impl<'a> Parser<'a> { }) } + /// Parses an optional unboxed closure kind (`&:`, `&mut:`, or `:`). + pub fn parse_optional_unboxed_closure_kind(&mut self) + -> Option<UnboxedClosureKind> { + if self.token == token::BINOP(token::AND) && + self.look_ahead(1, |t| { + token::is_keyword(keywords::Mut, t) + }) && + self.look_ahead(2, |t| *t == token::COLON) { + self.bump(); + self.bump(); + self.bump(); + return Some(FnMutUnboxedClosureKind) + } + + if self.token == token::BINOP(token::AND) && + self.look_ahead(1, |t| *t == token::COLON) { + self.bump(); + self.bump(); + return Some(FnUnboxedClosureKind) + } + + if self.eat(&token::COLON) { + return Some(FnOnceUnboxedClosureKind) + } + + return None + } + /// Parse a TyClosure type pub fn parse_ty_closure(&mut self) -> Ty_ { /* @@ -1115,27 +1146,19 @@ impl<'a> Parser<'a> { Vec::new() }; - let (is_unboxed, inputs) = if self.eat(&token::OROR) { - (false, Vec::new()) + let (optional_unboxed_closure_kind, inputs) = if self.eat(&token::OROR) { + (None, Vec::new()) } else { self.expect_or(); - let is_unboxed = self.token == token::BINOP(token::AND) && - self.look_ahead(1, |t| { - token::is_keyword(keywords::Mut, t) - }) && - self.look_ahead(2, |t| *t == token::COLON); - if is_unboxed { - self.bump(); - self.bump(); - self.bump(); - } + let optional_unboxed_closure_kind = + self.parse_optional_unboxed_closure_kind(); let inputs = self.parse_seq_to_before_or( &token::COMMA, |p| p.parse_arg_general(false)); self.expect_or(); - (is_unboxed, inputs) + (optional_unboxed_closure_kind, inputs) }; let (region, bounds) = { @@ -1155,18 +1178,22 @@ impl<'a> Parser<'a> { variadic: false }); - if is_unboxed { - TyUnboxedFn(box(GC) UnboxedFnTy { - decl: decl, - }) - } else { - TyClosure(box(GC) ClosureTy { - fn_style: fn_style, - onceness: onceness, - bounds: bounds, - decl: decl, - lifetimes: lifetime_defs, - }, region) + match optional_unboxed_closure_kind { + Some(unboxed_closure_kind) => { + TyUnboxedFn(box(GC) UnboxedFnTy { + kind: unboxed_closure_kind, + decl: decl, + }) + } + None => { + TyClosure(box(GC) ClosureTy { + fn_style: fn_style, + onceness: onceness, + bounds: bounds, + decl: decl, + lifetimes: lifetime_defs, + }, region) + } } } @@ -2703,7 +2730,8 @@ impl<'a> Parser<'a> { pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause) -> Gc<Expr> { let lo = self.span.lo; - let (decl, is_unboxed) = self.parse_fn_block_decl(); + let (decl, optional_unboxed_closure_kind) = + self.parse_fn_block_decl(); let body = self.parse_expr(); let fakeblock = P(ast::Block { view_items: Vec::new(), @@ -2714,14 +2742,20 @@ impl<'a> Parser<'a> { span: body.span, }); - if is_unboxed { - self.mk_expr(lo, - body.span.hi, - ExprUnboxedFn(capture_clause, decl, fakeblock)) - } else { - self.mk_expr(lo, - body.span.hi, - ExprFnBlock(capture_clause, decl, fakeblock)) + match optional_unboxed_closure_kind { + Some(unboxed_closure_kind) => { + self.mk_expr(lo, + body.span.hi, + ExprUnboxedFn(capture_clause, + unboxed_closure_kind, + decl, + fakeblock)) + } + None => { + self.mk_expr(lo, + body.span.hi, + ExprFnBlock(capture_clause, decl, fakeblock)) + } } } @@ -3553,28 +3587,22 @@ impl<'a> Parser<'a> { } fn parse_unboxed_function_type(&mut self) -> UnboxedFnTy { - let inputs = if self.eat(&token::OROR) { - Vec::new() - } else { - self.expect_or(); + let (optional_unboxed_closure_kind, inputs) = + if self.eat(&token::OROR) { + (None, Vec::new()) + } else { + self.expect_or(); - if self.token == token::BINOP(token::AND) && - self.look_ahead(1, |t| { - token::is_keyword(keywords::Mut, t) - }) && - self.look_ahead(2, |t| *t == token::COLON) { - self.bump(); - self.bump(); - self.bump(); - } + let optional_unboxed_closure_kind = + self.parse_optional_unboxed_closure_kind(); - let inputs = self.parse_seq_to_before_or(&token::COMMA, - |p| { - p.parse_arg_general(false) - }); - self.expect_or(); - inputs - }; + let inputs = self.parse_seq_to_before_or(&token::COMMA, + |p| { + p.parse_arg_general(false) + }); + self.expect_or(); + (optional_unboxed_closure_kind, inputs) + }; let (return_style, output) = self.parse_ret_ty(); UnboxedFnTy { @@ -3583,7 +3611,11 @@ impl<'a> Parser<'a> { output: output, cf: return_style, variadic: false, - }) + }), + kind: match optional_unboxed_closure_kind { + Some(kind) => kind, + None => FnMutUnboxedClosureKind, + }, } } @@ -4026,29 +4058,22 @@ impl<'a> Parser<'a> { } // parse the |arg, arg| header on a lambda - fn parse_fn_block_decl(&mut self) -> (P<FnDecl>, bool) { - let (is_unboxed, inputs_captures) = { + fn parse_fn_block_decl(&mut self) + -> (P<FnDecl>, Option<UnboxedClosureKind>) { + let (optional_unboxed_closure_kind, inputs_captures) = { if self.eat(&token::OROR) { - (false, Vec::new()) + (None, Vec::new()) } else { self.expect(&token::BINOP(token::OR)); - let is_unboxed = self.token == token::BINOP(token::AND) && - self.look_ahead(1, |t| { - token::is_keyword(keywords::Mut, t) - }) && - self.look_ahead(2, |t| *t == token::COLON); - if is_unboxed { - self.bump(); - self.bump(); - self.bump(); - } + let optional_unboxed_closure_kind = + self.parse_optional_unboxed_closure_kind(); let args = self.parse_seq_to_before_end( &token::BINOP(token::OR), seq_sep_trailing_disallowed(token::COMMA), |p| p.parse_fn_block_arg() ); self.bump(); - (is_unboxed, args) + (optional_unboxed_closure_kind, args) } }; let output = if self.eat(&token::RARROW) { @@ -4066,7 +4091,7 @@ impl<'a> Parser<'a> { output: output, cf: Return, variadic: false - }), is_unboxed) + }), optional_unboxed_closure_kind) } /// Parses the `(arg, arg) -> return_type` header on a procedure. diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ed285e2aa44..4ee73406f03 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -9,8 +9,10 @@ // except according to those terms. use abi; -use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound}; -use ast::{TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided}; +use ast::{FnMutUnboxedClosureKind, FnOnceUnboxedClosureKind}; +use ast::{FnUnboxedClosureKind, P, OtherRegionTyParamBound}; +use ast::{StaticRegionTyParamBound, TraitTyParamBound, UnboxedClosureKind}; +use ast::{UnboxedFnTyParamBound, Required, Provided}; use ast; use ast_util; use owned_slice::OwnedSlice; @@ -228,7 +230,7 @@ pub fn method_to_string(p: &ast::Method) -> String { } pub fn fn_block_to_string(p: &ast::FnDecl) -> String { - $to_string(|s| s.print_fn_block_args(p, false)) + $to_string(|s| s.print_fn_block_args(p, None)) } pub fn path_to_string(p: &ast::Path) -> String { @@ -594,7 +596,7 @@ impl<'a> State<'a> { &None, Some(&generics), None, - false)); + None)); } ast::TyClosure(f, ref region) => { let generics = ast::Generics { @@ -611,7 +613,7 @@ impl<'a> State<'a> { &f.bounds, Some(&generics), None, - false)); + None)); } ast::TyProc(ref f) => { let generics = ast::Generics { @@ -628,7 +630,7 @@ impl<'a> State<'a> { &f.bounds, Some(&generics), None, - false)); + None)); } ast::TyUnboxedFn(f) => { try!(self.print_ty_fn(None, @@ -641,7 +643,7 @@ impl<'a> State<'a> { &None, None, None, - true)); + Some(f.kind))); } ast::TyPath(ref path, ref bounds, _) => { try!(self.print_bounded_path(path, bounds)); @@ -1054,7 +1056,7 @@ impl<'a> State<'a> { &None, Some(&m.generics), Some(m.explicit_self.node), - false)); + None)); word(&mut self.s, ";") } @@ -1481,7 +1483,7 @@ impl<'a> State<'a> { // we are inside. // // if !decl.inputs.is_empty() { - try!(self.print_fn_block_args(&**decl, false)); + try!(self.print_fn_block_args(&**decl, None)); try!(space(&mut self.s)); // } @@ -1505,7 +1507,7 @@ impl<'a> State<'a> { // empty box to satisfy the close. try!(self.ibox(0)); } - ast::ExprUnboxedFn(capture_clause, ref decl, ref body) => { + ast::ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => { try!(self.print_capture_clause(capture_clause)); // in do/for blocks we don't want to show an empty @@ -1513,7 +1515,7 @@ impl<'a> State<'a> { // we are inside. // // if !decl.inputs.is_empty() { - try!(self.print_fn_block_args(&**decl, true)); + try!(self.print_fn_block_args(&**decl, Some(kind))); try!(space(&mut self.s)); // } @@ -2052,13 +2054,17 @@ impl<'a> State<'a> { } } - pub fn print_fn_block_args(&mut self, - decl: &ast::FnDecl, - is_unboxed: bool) - -> IoResult<()> { + pub fn print_fn_block_args( + &mut self, + decl: &ast::FnDecl, + unboxed_closure_kind: Option<UnboxedClosureKind>) + -> IoResult<()> { try!(word(&mut self.s, "|")); - if is_unboxed { - try!(self.word_space("&mut:")); + match unboxed_closure_kind { + None => {} + Some(FnUnboxedClosureKind) => try!(self.word_space("&:")), + Some(FnMutUnboxedClosureKind) => try!(self.word_space("&mut:")), + Some(FnOnceUnboxedClosureKind) => try!(self.word_space(":")), } try!(self.print_fn_args(decl, None)); try!(word(&mut self.s, "|")); @@ -2148,7 +2154,7 @@ impl<'a> State<'a> { &None, None, None, - true) + Some(unboxed_function_type.kind)) } OtherRegionTyParamBound(_) => Ok(()) }) @@ -2366,7 +2372,8 @@ impl<'a> State<'a> { opt_bounds: &Option<OwnedSlice<ast::TyParamBound>>, generics: Option<&ast::Generics>, opt_explicit_self: Option<ast::ExplicitSelf_>, - is_unboxed: bool) + opt_unboxed_closure_kind: + Option<ast::UnboxedClosureKind>) -> IoResult<()> { try!(self.ibox(indent_unit)); @@ -2383,7 +2390,7 @@ impl<'a> State<'a> { try!(self.print_fn_style(fn_style)); try!(self.print_opt_abi_and_extern_if_nondefault(opt_abi)); try!(self.print_onceness(onceness)); - if !is_unboxed { + if opt_unboxed_closure_kind.is_none() { try!(word(&mut self.s, "fn")); } } @@ -2399,20 +2406,30 @@ impl<'a> State<'a> { match generics { Some(g) => try!(self.print_generics(g)), _ => () } try!(zerobreak(&mut self.s)); - if is_unboxed || opt_sigil == Some('&') { + if opt_unboxed_closure_kind.is_some() || opt_sigil == Some('&') { try!(word(&mut self.s, "|")); } else { try!(self.popen()); } - if is_unboxed { - try!(word(&mut self.s, "&mut")); - try!(self.word_space(":")); + match opt_unboxed_closure_kind { + Some(ast::FnUnboxedClosureKind) => { + try!(word(&mut self.s, "&")); + try!(self.word_space(":")); + } + Some(ast::FnMutUnboxedClosureKind) => { + try!(word(&mut self.s, "&mut")); + try!(self.word_space(":")); + } + Some(ast::FnOnceUnboxedClosureKind) => { + try!(self.word_space(":")); + } + None => {} } try!(self.print_fn_args(decl, opt_explicit_self)); - if is_unboxed || opt_sigil == Some('&') { + if opt_unboxed_closure_kind.is_some() || opt_sigil == Some('&') { try!(word(&mut self.s, "|")); } else { if decl.variadic { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 372cee9ad09..79a870422a6 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -795,7 +795,7 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, en expression.id, env.clone()) } - ExprUnboxedFn(_, ref function_declaration, ref body) => { + ExprUnboxedFn(_, _, ref function_declaration, ref body) => { visitor.visit_fn(&FkFnBlock, &**function_declaration, &**body, |
