about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-08-14 16:36:19 +0000
committerbors <bors@rust-lang.org>2014-08-14 16:36:19 +0000
commit404978ea722c0257cc763540c93243e8a21f82ed (patch)
treec6106879c326924dc3d39ce71c0a797901bafe04 /src/libsyntax
parent56b86aaf35d96ec8cf8555205426eaed62427963 (diff)
parent8d272321417df3a954802e42a66adda87ade5a49 (diff)
downloadrust-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.rs10
-rw-r--r--src/libsyntax/fold.rs5
-rw-r--r--src/libsyntax/parse/parser.rs167
-rw-r--r--src/libsyntax/print/pprust.rs67
-rw-r--r--src/libsyntax/visit.rs2
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,