diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index bb6deac5a02b..d369e818d479 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -980,6 +980,7 @@ fn type_kind(cx: ctxt, ty: t) -> ast::kind { alt proto { ast::proto_block. { ast::kind_noncopyable } ast::proto_shared(_) { ast::kind_copyable } + ast::proto_send. { ast::kind_sendable } ast::proto_bare. { ast::kind_sendable } } } diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index a20d1ae5c6f5..d9d5260416e8 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -113,6 +113,7 @@ tag proto_sugar { tag proto { proto_bare; + proto_send; proto_shared(proto_sugar); proto_block; } @@ -198,7 +199,7 @@ tag expr_ { expr_for(@local, @expr, blk); expr_do_while(blk, @expr); expr_alt(@expr, [arm]); - expr_fn(_fn); + expr_fn(_fn, @capture); expr_block(blk); /* @@ -233,6 +234,15 @@ tag expr_ { expr_mac(mac); } +// At the moment, one can only capture local variables. +type capture_ = { + is_send: bool, + copies: [ident], + moves: [ident] +}; + +type capture = spanned; + /* // Says whether this is a block the user marked as // "unchecked" diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 7ff08181e1a5..e927ffbbeffa 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -14,6 +14,12 @@ tag restriction { UNRESTRICTED; RESTRICT_NO_CALL_EXPRS; RESTRICT_NO_BAR_OP; } tag file_type { CRATE_FILE; SOURCE_FILE; } +tag fn_kw { + fn_kw_fn; + fn_kw_lambda; + fn_kw_block; +}; + type parse_sess = @{cm: codemap::codemap, mutable next_id: node_id}; fn next_node_id(sess: parse_sess) -> node_id { @@ -536,7 +542,14 @@ fn parse_ty(p: parser, colons_before_params: bool) -> @ast::ty { } else if eat_word(p, "block") { t = parse_ty_fn(ast::proto_block, p); } else if eat_word(p, "lambda") { - t = parse_ty_fn(ast::proto_shared(ast::sugar_sexy), p); + if p.peek() == token::LBRACE { // lambda[send](...) + expect(p, token::LBRACE); + expect_word(p, "send"); + expect(p, token::RBRACE); + t = parse_ty_fn(ast::proto_send, p); + } else { // lambda(...) + t = parse_ty_fn(ast::proto_shared(ast::sugar_sexy), p); + } } else if eat_word(p, "obj") { t = parse_ty_obj(p); } else if p.peek() == token::MOD_SEP || is_ident(p.peek()) { @@ -831,11 +844,11 @@ fn parse_bottom_expr(p: parser) -> @ast::expr { */ } else if eat_word(p, "fn") { let proto = parse_fn_anon_proto(p); - ret parse_fn_expr(p, proto); + ret parse_fn_expr(p, fn_kw_fn); } else if eat_word(p, "block") { - ret parse_fn_expr(p, ast::proto_block); + ret parse_fn_expr(p, fn_kw_block); } else if eat_word(p, "lambda") { - ret parse_fn_expr(p, ast::proto_shared(ast::sugar_sexy)); + ret parse_fn_expr(p, fn_kw_lambda); } else if eat_word(p, "unchecked") { ret parse_block_expr(p, lo, ast::unchecked_blk); } else if eat_word(p, "unsafe") { @@ -1274,12 +1287,78 @@ fn parse_if_expr(p: parser) -> @ast::expr { } } -fn parse_fn_expr(p: parser, proto: ast::proto) -> @ast::expr { +// Parses: +// +// CC := [send; copy ID*; move ID*] +// +// where any part is optional and trailing ; is permitted. +fn parse_capture_clause(p: parser) -> (bool, @ast::capture) { + fn expect_opt_trailing_semi(p: parser) { + if !eat(p, token::SEMI) { + if p.peek() != token::RBRACE { + p.fatal("expecting ; or ]"); + } + } + } + + fn eat_ident_list(p: parser) -> [ast::ident] { + let res = []; + while true { + alt p.peek() { + token::IDENT(_, _) { + res += parse_ident(p); + if !eat(p, token::COMMA) { + ret res; + } + } + + _ { ret res; } + } + } + } + + let is_send = false; + let copies = []; + let moves = []; + + if p.peek() != token::LBRACE { + ret (is_send, captures); + } + + expect(p, token::LBRACE); + while p.peek() != token::RBRACE { + if eat_word(p, "send") { + is_send = true; + expect_opt_trailing_semi(p); + } else if eat_word(p, "copy") { + copies += eat_ident_list(); + expect_opt_trailing_semi(p); + } else if eat_word(p, "move") { + moves += eat_ident_list(); + expect_opt_trailing_semi(p); + } else { + let s: str = "expecting send, copy, or move clause"; + p.fatal(s); + } + } + + ret @{is_send: is_send, copies: copies, moves: moves}; +} + +fn parse_fn_expr(p: parser, kw: fn_kw) -> @ast::expr { let lo = p.get_last_lo_pos(); + let cap = parse_capture_clause(p); let decl = parse_fn_decl(p, ast::impure_fn, ast::il_normal); let body = parse_block(p); + let proto = alt (kw, cap.is_send) { + (fn_kw_fn., true) { ast::proto_bare } + (fn_kw_lambda., true) { ast::proto_send } + (fn_kw_lambda., false) { ast::proto_shared(ast::sugar_sexy) } + (fn_kw_block., false) { ast::proto_block } + (_, true) { p.fatal("only lambda can be declared sendable"); } + } let _fn = {decl: decl, proto: proto, body: body}; - ret mk_expr(p, lo, body.span.hi, ast::expr_fn(_fn)); + ret mk_expr(p, lo, body.span.hi, ast::expr_fn(_fn, cap)); } fn parse_fn_block_expr(p: parser) -> @ast::expr {