diff options
Diffstat (limited to 'src')
31 files changed, 444 insertions, 357 deletions
diff --git a/src/fuzzer/fuzzer.rs b/src/fuzzer/fuzzer.rs index 47c6371ad16..594748e2eb9 100644 --- a/src/fuzzer/fuzzer.rs +++ b/src/fuzzer/fuzzer.rs @@ -154,11 +154,11 @@ fn steal(crate: ast::crate, tm: test_mode) -> stolen_stuff { fn safe_to_replace_expr(e: ast::expr_, _tm: test_mode) -> bool { alt e { // https://github.com/mozilla/rust/issues/652 - ast::expr_if(_, _, _) { false } + ast::expr_if(*) { false } ast::expr_block(_) { false } // expr_call is also missing a constraint - ast::expr_fn_block(_, _) { false } + ast::expr_fn_block(*) { false } _ { true } } diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 3215f3b9f28..cddb1346c01 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1096,6 +1096,14 @@ impl extensions<T> for [T] { #[inline] fn map<U>(f: fn(T) -> U) -> [U] { map(self, f) } #[doc = " + Apply a function to the index and value of each element in the vector + and return the results + "] + fn mapi<U>(f: fn(uint, T) -> U) -> [U] { + let mut i = 0u; + self.map { |e| i += 1u; f(i - 1u, e) } + } + #[doc = " Apply a function to each element of a vector and return a concatenation of each result vector "] diff --git a/src/librustsyntax/ast.rs b/src/librustsyntax/ast.rs index 4a4b5ea4d38..12e29bcc3ec 100644 --- a/src/librustsyntax/ast.rs +++ b/src/librustsyntax/ast.rs @@ -311,8 +311,8 @@ enum expr_ { (implicit) condition is always true. */ expr_loop(blk), expr_alt(@expr, [arm], alt_mode), - expr_fn(proto, fn_decl, blk, @capture_clause), - expr_fn_block(fn_decl, blk), + expr_fn(proto, fn_decl, blk, capture_clause), + expr_fn_block(fn_decl, blk, capture_clause), // Inner expr is always an expr_fn_block. We need the wrapping node to // sanely type this (a function returning nil on the inside but bool on // the outside). @@ -356,15 +356,13 @@ enum expr_ { #[auto_serialize] type capture_item = { id: int, + is_move: bool, name: ident, // Currently, can only capture a local var. span: span }; #[auto_serialize] -type capture_clause = { - copies: [@capture_item], - moves: [@capture_item] -}; +type capture_clause = [capture_item]; /* // Says whether this is a block the user marked as diff --git a/src/librustsyntax/fold.rs b/src/librustsyntax/fold.rs index 30e8c5173b2..5ebaee71339 100644 --- a/src/librustsyntax/fold.rs +++ b/src/librustsyntax/fold.rs @@ -456,8 +456,9 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { expr_fn(proto, fold_fn_decl(decl, fld), fld.fold_block(body), captures) } - expr_fn_block(decl, body) { - expr_fn_block(fold_fn_decl(decl, fld), fld.fold_block(body)) + expr_fn_block(decl, body, captures) { + expr_fn_block(fold_fn_decl(decl, fld), fld.fold_block(body), + captures) } expr_block(blk) { expr_block(fld.fold_block(blk)) } expr_move(el, er) { diff --git a/src/librustsyntax/parse/parser.rs b/src/librustsyntax/parse/parser.rs index 0c681900dd5..0432faecba0 100644 --- a/src/librustsyntax/parse/parser.rs +++ b/src/librustsyntax/parse/parser.rs @@ -398,25 +398,51 @@ fn parse_arg_mode(p: parser) -> ast::mode { } else { ast::infer(p.get_id()) } } -fn parse_arg(p: parser) -> ast::arg { +fn parse_capture_item_or( + p: parser, + parse_arg_fn: fn() -> arg_or_capture_item) -> arg_or_capture_item { + + fn parse_capture_item(p: parser, is_move: bool) -> ast::capture_item { + let id = p.get_id(); + let sp = mk_sp(p.span.lo, p.span.hi); + let ident = parse_ident(p); + {id: id, is_move: is_move, name: ident, span: sp} + } + + if eat_keyword(p, "move") { + either::right(parse_capture_item(p, true)) + } else if eat_keyword(p, "copy") { + either::right(parse_capture_item(p, false)) + } else { + parse_arg_fn() + } +} + +fn parse_arg(p: parser) -> arg_or_capture_item { let m = parse_arg_mode(p); let i = parse_value_ident(p); expect(p, token::COLON); let t = parse_ty(p, false); - ret {mode: m, ty: t, ident: i, id: p.get_id()}; + either::left({mode: m, ty: t, ident: i, id: p.get_id()}) } -fn parse_fn_block_arg(p: parser) -> ast::arg { - let m = parse_arg_mode(p); - let i = parse_value_ident(p); - let t = if eat(p, token::COLON) { - parse_ty(p, false) - } else { - @{id: p.get_id(), - node: ast::ty_infer, - span: mk_sp(p.span.lo, p.span.hi)} - }; - ret {mode: m, ty: t, ident: i, id: p.get_id()}; +fn parse_arg_or_capture_item(p: parser) -> arg_or_capture_item { + parse_capture_item_or(p) {|| parse_arg(p) } +} + +fn parse_fn_block_arg(p: parser) -> arg_or_capture_item { + parse_capture_item_or(p) {|| + let m = parse_arg_mode(p); + let i = parse_value_ident(p); + let t = if eat(p, token::COLON) { + parse_ty(p, false) + } else { + @{id: p.get_id(), + node: ast::ty_infer, + span: mk_sp(p.span.lo, p.span.hi)} + }; + either::left({mode: m, ty: t, ident: i, id: p.get_id()}) + } } fn maybe_parse_dollar_mac(p: parser) -> option<ast::mac_> { @@ -1149,74 +1175,27 @@ fn parse_if_expr(p: parser) -> @ast::expr { } } -// Parses: -// -// CC := [copy ID*; move ID*] -// -// where any part is optional and trailing ; is permitted. -fn parse_capture_clause(p: parser) -> @ast::capture_clause { - fn expect_opt_trailing_semi(p: parser) { - if !eat(p, token::SEMI) { - if p.token != token::RBRACKET { - p.fatal("expecting ; or ]"); - } - } - } - - fn eat_ident_list(p: parser) -> [@ast::capture_item] { - let mut res = []; - loop { - alt p.token { - token::IDENT(_, _) { - let id = p.get_id(); - let sp = mk_sp(p.span.lo, p.span.hi); - let ident = parse_ident(p); - res += [@{id:id, name:ident, span:sp}]; - if !eat(p, token::COMMA) { - ret res; - } - } - - _ { ret res; } - } - }; - } - - let mut copies = []; - let mut moves = []; +fn parse_fn_expr(p: parser, proto: ast::proto) -> @ast::expr { + let lo = p.last_span.lo; - if eat(p, token::LBRACKET) { - while !eat(p, token::RBRACKET) { - if eat_keyword(p, "copy") { - copies += eat_ident_list(p); - expect_opt_trailing_semi(p); - } else if eat_keyword(p, "move") { - moves += eat_ident_list(p); - expect_opt_trailing_semi(p); - } else { - let s: str = "expecting send, copy, or move clause"; - p.fatal(s); - } - } - } + let cc_old = parse_old_skool_capture_clause(p); - ret @{copies: copies, moves: moves}; -} + // if we want to allow fn expression argument types to be inferred in the + // future, just have to change parse_arg to parse_fn_block_arg. + let (decl, capture_clause) = + parse_fn_decl(p, ast::impure_fn, parse_arg_or_capture_item); -fn parse_fn_expr(p: parser, proto: ast::proto) -> @ast::expr { - let lo = p.last_span.lo; - let capture_clause = parse_capture_clause(p); - let decl = parse_fn_decl(p, ast::impure_fn); let body = parse_block(p); ret mk_expr(p, lo, body.span.hi, - ast::expr_fn(proto, decl, body, capture_clause)); + ast::expr_fn(proto, decl, body, capture_clause + cc_old)); } fn parse_fn_block_expr(p: parser) -> @ast::expr { let lo = p.last_span.lo; - let decl = parse_fn_block_decl(p); + let (decl, captures) = parse_fn_block_decl(p); let body = parse_block_tail(p, lo, ast::default_blk); - ret mk_expr(p, lo, body.span.hi, ast::expr_fn_block(decl, body)); + ret mk_expr(p, lo, body.span.hi, + ast::expr_fn_block(decl, body, captures)); } fn parse_else_expr(p: parser) -> @ast::expr { @@ -1699,46 +1678,107 @@ fn parse_ty_params(p: parser) -> [ast::ty_param] { } else { [] } } -fn parse_fn_decl(p: parser, purity: ast::purity) - -> ast::fn_decl { - let inputs: ast::spanned<[ast::arg]> = +// FIXME Remove after snapshot +fn parse_old_skool_capture_clause(p: parser) -> ast::capture_clause { + fn expect_opt_trailing_semi(p: parser) { + if !eat(p, token::SEMI) { + if p.token != token::RBRACKET { + p.fatal("expecting ; or ]"); + } + } + } + + fn eat_ident_list(p: parser, is_move: bool) -> [ast::capture_item] { + let mut res = []; + loop { + alt p.token { + token::IDENT(_, _) { + let id = p.get_id(); + let sp = mk_sp(p.span.lo, p.span.hi); + let ident = parse_ident(p); + res += [{id:id, is_move: is_move, name:ident, span:sp}]; + if !eat(p, token::COMMA) { + ret res; + } + } + + _ { ret res; } + } + }; + } + + let mut cap_items = []; + + if eat(p, token::LBRACKET) { + while !eat(p, token::RBRACKET) { + if eat_keyword(p, "copy") { + cap_items += eat_ident_list(p, false); + expect_opt_trailing_semi(p); + } else if eat_keyword(p, "move") { + cap_items += eat_ident_list(p, true); + expect_opt_trailing_semi(p); + } else { + let s: str = "expecting send, copy, or move clause"; + p.fatal(s); + } + } + } + + ret cap_items; +} + +type arg_or_capture_item = either<ast::arg, ast::capture_item>; + + +fn parse_fn_decl(p: parser, purity: ast::purity, + parse_arg_fn: fn(parser) -> arg_or_capture_item) + -> (ast::fn_decl, ast::capture_clause) { + + let args_or_capture_items: [arg_or_capture_item] = parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), - parse_arg, p); + parse_arg_fn, p).node; + + let inputs = either::lefts(args_or_capture_items); + let capture_clause = either::rights(args_or_capture_items); + // Use the args list to translate each bound variable // mentioned in a constraint to an arg index. // Seems weird to do this in the parser, but I'm not sure how else to. let mut constrs = []; if p.token == token::COLON { p.bump(); - constrs = parse_constrs({|x| parse_ty_constr(inputs.node, x) }, p); + constrs = parse_constrs({|x| parse_ty_constr(inputs, x) }, p); } let (ret_style, ret_ty) = parse_ret_ty(p); - ret {inputs: inputs.node, - output: ret_ty, - purity: purity, - cf: ret_style, - constraints: constrs}; + ret ({inputs: inputs, + output: ret_ty, + purity: purity, + cf: ret_style, + constraints: constrs}, capture_clause); } -fn parse_fn_block_decl(p: parser) -> ast::fn_decl { - let inputs = if eat(p, token::OROR) { - [] - } else { - parse_seq(token::BINOP(token::OR), - token::BINOP(token::OR), - seq_sep(token::COMMA), - parse_fn_block_arg, p).node - }; +fn parse_fn_block_decl(p: parser) -> (ast::fn_decl, ast::capture_clause) { + let inputs_captures = { + if eat(p, token::OROR) { + [] + } else { + parse_seq(token::BINOP(token::OR), + token::BINOP(token::OR), + seq_sep(token::COMMA), + parse_fn_block_arg, p).node + } + }; let output = if eat(p, token::RARROW) { parse_ty(p, false) } else { @{id: p.get_id(), node: ast::ty_infer, span: p.span} }; - ret {inputs: inputs, - output: output, - purity: ast::impure_fn, - cf: ast::return_val, - constraints: []}; + ret ({inputs: either::lefts(inputs_captures), + output: output, + purity: ast::impure_fn, + cf: ast::return_val, + constraints: []}, + either::rights(inputs_captures)); } fn parse_fn_header(p: parser) -> {ident: ast::ident, tps: [ast::ty_param]} { @@ -1760,7 +1800,7 @@ fn parse_item_fn(p: parser, purity: ast::purity, attrs: [ast::attribute]) -> @ast::item { let lo = p.last_span.lo; let t = parse_fn_header(p); - let decl = parse_fn_decl(p, purity); + let (decl, _) = parse_fn_decl(p, purity, parse_arg); let (inner_attrs, body) = parse_inner_attrs_and_block(p, true); let attrs = attrs + inner_attrs; ret mk_item(p, lo, body.span.hi, t.ident, @@ -1785,7 +1825,7 @@ fn parse_method(p: parser, pr: ast::privacy) -> @ast::method { let lo = p.span.lo, pur = parse_fn_purity(p); let ident = parse_method_name(p); let tps = parse_ty_params(p); - let decl = parse_fn_decl(p, pur); + let (decl, _) = parse_fn_decl(p, pur, parse_arg); let (inner_attrs, body) = parse_inner_attrs_and_block(p, true); let attrs = attrs + inner_attrs; @{ident: ident, attrs: attrs, tps: tps, decl: decl, body: body, @@ -1969,7 +2009,7 @@ fn parse_class_item(p:parser, class_name_with_tps: @ast::path) let lo = p.last_span.lo; // Can ctors have attrs? // result type is always the type of the class - let decl_ = parse_fn_decl(p, ast::impure_fn); + let (decl_, _) = parse_fn_decl(p, ast::impure_fn, parse_arg); let decl = {output: @{id: p.get_id(), node: ast::ty_path(class_name_with_tps, p.get_id()), span: decl_.output.span} @@ -2048,7 +2088,7 @@ fn parse_item_native_fn(p: parser, attrs: [ast::attribute], purity: ast::purity) -> @ast::native_item { let lo = p.last_span.lo; let t = parse_fn_header(p); - let decl = parse_fn_decl(p, purity); + let (decl, _) = parse_fn_decl(p, purity, parse_arg); let mut hi = p.span.hi; expect(p, token::SEMI); ret @{ident: t.ident, diff --git a/src/librustsyntax/print/pprust.rs b/src/librustsyntax/print/pprust.rs index b50f4f341a7..6e573041265 100644 --- a/src/librustsyntax/print/pprust.rs +++ b/src/librustsyntax/print/pprust.rs @@ -519,7 +519,7 @@ fn print_item(s: ps, &&item: @ast::item) { hardbreak_if_not_bol(s); maybe_print_comment(s, ctor.span.lo); head(s, "new"); - print_fn_args_and_ret(s, ctor.node.dec); + print_fn_args_and_ret(s, ctor.node.dec, []); space(s.s); print_block(s, ctor.node.body); for items.each {|ci| @@ -1018,18 +1018,17 @@ fn print_expr(s: ps, &&expr: @ast::expr) { // head-box, will be closed by print-block at start ibox(s, 0u); word(s.s, proto_to_str(proto)); - print_cap_clause(s, *cap_clause); - print_fn_args_and_ret(s, decl); + print_fn_args_and_ret(s, decl, cap_clause); space(s.s); print_block(s, body); } - ast::expr_fn_block(decl, body) { + ast::expr_fn_block(decl, body, cap_clause) { // containing cbox, will be closed by print-block at } cbox(s, indent_unit); // head-box, will be closed by print-block at start ibox(s, 0u); word(s.s, "{"); - print_fn_block_args(s, decl); + print_fn_block_args(s, decl, cap_clause); print_possibly_embedded_block(s, body, block_block_fn, indent_unit); } ast::expr_loop_body(body) { @@ -1319,46 +1318,27 @@ fn print_fn(s: ps, decl: ast::fn_decl, name: ast::ident, } word(s.s, name); print_type_params(s, typarams); - print_fn_args_and_ret(s, decl); + print_fn_args_and_ret(s, decl, []); } -fn print_cap_clause(s: ps, cap_clause: ast::capture_clause) { - fn print_cap_item(s: ps, &&cap_item: @ast::capture_item) { - word(s.s, cap_item.name); - } - - let has_copies = vec::is_not_empty(cap_clause.copies); - let has_moves = vec::is_not_empty(cap_clause.moves); - if !has_copies && !has_moves { ret; } - - word(s.s, "["); - - if has_copies { - word_nbsp(s, "copy"); - commasep(s, inconsistent, cap_clause.copies, print_cap_item); - if has_moves { - word_space(s, ";"); +fn print_fn_args(s: ps, decl: ast::fn_decl, + cap_items: [ast::capture_item]) { + commasep(s, inconsistent, decl.inputs, print_arg); + if cap_items.is_not_empty() { + let mut first = decl.inputs.is_empty(); + for cap_items.each { |cap_item| + if first { first = false; } else { word_space(s, ","); } + if cap_item.is_move { word_nbsp(s, "move") } + else { word_nbsp(s, "copy") } + word(s.s, cap_item.name); } } - - if has_moves { - word_nbsp(s, "move"); - commasep(s, inconsistent, cap_clause.moves, print_cap_item); - } - - word(s.s, "]"); } -fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl) { +fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl, + cap_items: [ast::capture_item]) { popen(s); - fn print_arg(s: ps, x: ast::arg) { - ibox(s, indent_unit); - print_arg_mode(s, x.mode); - word_space(s, x.ident + ":"); - print_type(s, x.ty); - end(s); - } - commasep(s, inconsistent, decl.inputs, print_arg); + print_fn_args(s, decl, cap_items); pclose(s); word(s.s, constrs_str(decl.constraints, {|c| ast_fn_constr_to_str(decl, c) @@ -1372,19 +1352,10 @@ fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl) { } } -fn print_fn_block_args(s: ps, decl: ast::fn_decl) { +fn print_fn_block_args(s: ps, decl: ast::fn_decl, + cap_items: [ast::capture_item]) { word(s.s, "|"); - fn print_arg(s: ps, x: ast::arg) { - ibox(s, indent_unit); - print_arg_mode(s, x.mode); - word(s.s, x.ident); - if x.ty.node != ast::ty_infer { - word_space(s, ":"); - print_type(s, x.ty); - } - end(s); - } - commasep(s, inconsistent, decl.inputs, print_arg); + print_fn_args(s, decl, cap_items); word(s.s, "|"); if decl.output.node != ast::ty_infer { space_if_not_bol(s); @@ -1541,6 +1512,23 @@ fn print_mt(s: ps, mt: ast::mt) { print_type(s, mt.ty); } +fn print_arg(s: ps, input: ast::arg) { + ibox(s, indent_unit); + print_arg_mode(s, input.mode); + alt input.ty.node { + ast::ty_infer { + word(s.s, input.ident); + } + _ { + if str::len(input.ident) > 0u { + word_space(s, input.ident + ":"); + } + print_type(s, input.ty); + } + } + end(s); +} + fn print_ty_fn(s: ps, opt_proto: option<ast::proto>, decl: ast::fn_decl, id: option<ast::ident>, tps: option<[ast::ty_param]>) { @@ -1550,13 +1538,6 @@ fn print_ty_fn(s: ps, opt_proto: option<ast::proto>, alt tps { some(tps) { print_type_params(s, tps); } _ { } } zerobreak(s.s); popen(s); - fn print_arg(s: ps, input: ast::arg) { - print_arg_mode(s, input.mode); - if str::len(input.ident) > 0u { - word_space(s, input.ident + ":"); - } - print_type(s, input.ty); - } commasep(s, inconsistent, decl.inputs, print_arg); pclose(s); maybe_print_comment(s, decl.output.span.lo); diff --git a/src/librustsyntax/visit.rs b/src/librustsyntax/visit.rs index d2188977af0..2cac7f53e83 100644 --- a/src/librustsyntax/visit.rs +++ b/src/librustsyntax/visit.rs @@ -384,7 +384,7 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) { expr_fn(proto, decl, body, _) { v.visit_fn(fk_anon(proto), decl, body, ex.span, ex.id, e, v); } - expr_fn_block(decl, body) { + expr_fn_block(decl, body, _) { v.visit_fn(fk_fn_block, decl, body, ex.span, ex.id, e, v); } expr_block(b) { v.visit_block(b, e, v); } diff --git a/src/rustc/front/test.rs b/src/rustc/front/test.rs index 258c49daa9d..c23c6d5a997 100644 --- a/src/rustc/front/test.rs +++ b/src/rustc/front/test.rs @@ -365,15 +365,10 @@ fn mk_test_wrapper(cx: test_ctxt, rules: ast::default_blk }); - let wrapper_capture: @ast::capture_clause = @{ - copies: [], - moves: [] - }; - let wrapper_expr: ast::expr = { id: cx.sess.next_node_id(), node: ast::expr_fn(ast::proto_bare, wrapper_decl, - wrapper_body, wrapper_capture), + wrapper_body, []), span: span }; diff --git a/src/rustc/middle/alias.rs b/src/rustc/middle/alias.rs index f48a38e6a89..444947c471b 100644 --- a/src/rustc/middle/alias.rs +++ b/src/rustc/middle/alias.rs @@ -182,8 +182,8 @@ fn cant_copy(cx: ctx, b: binding) -> bool { fn local_id_for_args(cx: ctx, args: [@ast::expr]) -> uint { for vec::each(args) {|arg| alt arg.node { - ast::expr_fn_block(decl, _) | ast::expr_fn(_, decl, _, _) | - ast::expr_loop_body(@{node: ast::expr_fn_block(decl, _), _}) { + ast::expr_fn_block(decl, _, _) | ast::expr_fn(_, decl, _, _) | + ast::expr_loop_body(@{node: ast::expr_fn_block(decl, _, _), _}) { if decl.inputs.len() > 0u { ret local_id_of_node(cx, decl.inputs[0].id); } @@ -216,7 +216,7 @@ fn check_call(cx: @ctx, sc: scope, f: @ast::expr, args: [@ast::expr], ast::by_ref | ast::by_val | ast::by_move | ast::by_copy {} } alt arg.node { - ast::expr_fn_block(_, _) { blocks += [arg]; } + ast::expr_fn_block(*) { blocks += [arg]; } ast::expr_loop_body(b) { blocks += [b]; } _ { let root_var = path_def_id(*cx, root.ex); @@ -267,7 +267,7 @@ fn check_call(cx: @ctx, sc: scope, f: @ast::expr, args: [@ast::expr], let mut_alias = (ast::by_mutbl_ref == ty::arg_mode(cx.tcx, arg_t)); alt args[i].node { - ast::expr_fn_block(_, _) | ast::expr_loop_body(_) {} + ast::expr_fn_block(*) | ast::expr_loop_body(_) {} _ { if i != j && ty_can_unsafely_include( *cx, unsafe_ty, arg_t.ty, mut_alias) && @@ -306,7 +306,7 @@ fn check_call(cx: @ctx, sc: scope, f: @ast::expr, args: [@ast::expr], let inner_sc = {bs: bindings + sc.bs, invalid: sc.invalid}; for blocks.each {|blk| alt check blk.node { - ast::expr_fn_block(_, body) { + ast::expr_fn_block(_, body, _) { v.visit_block(body, inner_sc, v); } } diff --git a/src/rustc/middle/capture.rs b/src/rustc/middle/capture.rs index d7e09cce271..745469d0148 100644 --- a/src/rustc/middle/capture.rs +++ b/src/rustc/middle/capture.rs @@ -36,7 +36,7 @@ fn check_capture_clause(tcx: ty::ctxt, let freevars = freevars::get_freevars(tcx, fn_expr_id); let seen_defs = map::int_hash(); - let check_capture_item = fn@(&&cap_item: @ast::capture_item) { + let check_capture_item = fn@(cap_item: ast::capture_item) { let cap_def = tcx.def_map.get(cap_item.id); if !vec::any(*freevars, {|fv| fv.def == cap_def}) { tcx.sess.span_warn( @@ -54,36 +54,19 @@ fn check_capture_clause(tcx: ty::ctxt, } }; - let check_not_upvar = fn@(&&cap_item: @ast::capture_item) { - alt tcx.def_map.get(cap_item.id) { - ast::def_upvar(_, _, _) { - tcx.sess.span_err( - cap_item.span, - #fmt("upvars (like '%s') cannot be moved into a closure", - cap_item.name)); - } - _ {} - } - }; - - let check_block_captures = fn@(v: [@ast::capture_item]) { - if check vec::is_not_empty(v) { - let cap_item0 = vec::head(v); + alt fn_proto { + ast::proto_any | ast::proto_block { + if vec::is_not_empty(cap_clause) { + let cap_item0 = vec::head(cap_clause); tcx.sess.span_err( cap_item0.span, "cannot capture values explicitly with a block closure"); } - }; - - alt fn_proto { - ast::proto_any | ast::proto_block { - check_block_captures(cap_clause.copies); - check_block_captures(cap_clause.moves); } ast::proto_bare | ast::proto_box | ast::proto_uniq { - vec::iter(cap_clause.copies, check_capture_item); - vec::iter(cap_clause.moves, check_capture_item); - vec::iter(cap_clause.moves, check_not_upvar); + for cap_clause.each { |cap_item| + check_capture_item(cap_item); + } } } } @@ -95,24 +78,31 @@ fn compute_capture_vars(tcx: ty::ctxt, let freevars = freevars::get_freevars(tcx, fn_expr_id); let cap_map = map::int_hash(); - vec::iter(cap_clause.copies) { |cap_item| - let cap_def = tcx.def_map.get(cap_item.id); - let cap_def_id = ast_util::def_id_of_def(cap_def).node; - if vec::any(*freevars, {|fv| fv.def == cap_def}) { - cap_map.insert(cap_def_id, { def:cap_def, mode:cap_copy }); - } - } + // first add entries for anything explicitly named in the cap clause - vec::iter(cap_clause.moves) { |cap_item| + for cap_clause.each { |cap_item| let cap_def = tcx.def_map.get(cap_item.id); let cap_def_id = ast_util::def_id_of_def(cap_def).node; - if vec::any(*freevars, {|fv| fv.def == cap_def}) { - cap_map.insert(cap_def_id, { def:cap_def, mode:cap_move }); + if cap_item.is_move { + // if we are moving the value in, but it's not actually used, + // must drop it. + if vec::any(*freevars, {|fv| fv.def == cap_def}) { + cap_map.insert(cap_def_id, { def:cap_def, mode:cap_move }); + } else { + cap_map.insert(cap_def_id, { def:cap_def, mode:cap_drop }); + } } else { - cap_map.insert(cap_def_id, { def:cap_def, mode:cap_drop }); + // if we are copying the value in, but it's not actually used, + // just ignore it. + if vec::any(*freevars, {|fv| fv.def == cap_def}) { + cap_map.insert(cap_def_id, { def:cap_def, mode:cap_copy }); + } } } + // now go through anything that is referenced but was not explicitly + // named and add that + let implicit_mode = alt fn_proto { ast::proto_any | ast::proto_block { cap_ref } ast::proto_bare | ast::proto_box | ast::proto_uniq { cap_copy } diff --git a/src/rustc/middle/check_loop.rs b/src/rustc/middle/check_loop.rs index 5082aba1394..99dadb5922a 100644 --- a/src/rustc/middle/check_loop.rs +++ b/src/rustc/middle/check_loop.rs @@ -21,10 +21,10 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) { expr_fn(_, _, _, _) { visit::visit_expr(e, {in_loop: false, can_ret: true}, v); } - expr_fn_block(_, b) { + expr_fn_block(_, b, _) { v.visit_block(b, {in_loop: false, can_ret: false}, v); } - expr_loop_body(@{node: expr_fn_block(_, b), _}) { + expr_loop_body(@{node: expr_fn_block(_, b, _), _}) { let blk = is_blockish(ty::ty_fn_proto(ty::expr_ty(tcx, e))); v.visit_block(b, {in_loop: true, can_ret: blk}, v); } diff --git a/src/rustc/middle/freevars.rs b/src/rustc/middle/freevars.rs index 727cbac4f23..df6a718de1d 100644 --- a/src/rustc/middle/freevars.rs +++ b/src/rustc/middle/freevars.rs @@ -40,12 +40,12 @@ fn collect_freevars(def_map: resolve::def_map, blk: ast::blk) let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) { alt expr.node { - ast::expr_fn(proto, decl, _, captures) { + ast::expr_fn(proto, decl, _, _) { if proto != ast::proto_bare { visit::visit_expr(expr, depth + 1, v); } } - ast::expr_fn_block(_, _) { + ast::expr_fn_block(_, _, _) { visit::visit_expr(expr, depth + 1, v); } ast::expr_path(path) { diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index dee278e7ed0..672629577d9 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -117,16 +117,14 @@ fn check_fn_cap_clause(cx: ctx, }); //log("freevar_ids", freevar_ids); with_appropriate_checker(cx, id) { |checker| - let check_var = fn@(&&cap_item: @capture_item) { + for cap_clause.each { |cap_item| let cap_def = cx.tcx.def_map.get(cap_item.id); let cap_def_id = ast_util::def_id_of_def(cap_def).node; if !vec::contains(freevar_ids, cap_def_id) { let ty = ty::node_id_to_type(cx.tcx, cap_def_id); checker(cx, ty, cap_item.span); } - }; - vec::iter(cap_clause.copies, check_var); - vec::iter(cap_clause.moves, check_var); + } } } @@ -227,8 +225,8 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) { } } } - expr_fn(_, _, _, cap_clause) { - check_fn_cap_clause(cx, e.id, *cap_clause); + expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) { + check_fn_cap_clause(cx, e.id, cap_clause); } _ { } } diff --git a/src/rustc/middle/last_use.rs b/src/rustc/middle/last_use.rs index 083274c8db5..1364118730a 100644 --- a/src/rustc/middle/last_use.rs +++ b/src/rustc/middle/last_use.rs @@ -154,12 +154,15 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) { v.visit_expr(dest, cx, v); clear_if_path(cx, dest, v, true); } + expr_fn_block(_, _, cap_clause) | expr_fn(_, _, _, cap_clause) { // n.b.: safe to ignore copies, as if they are unused // then they are ignored, otherwise they will show up // as freevars in the body. - vec::iter(cap_clause.moves) {|ci| - clear_def_if_local(cx, cx.def_map.get(ci.id), false); + for cap_clause.each { |ci| + if ci.is_move { + clear_def_if_local(cx, cx.def_map.get(ci.id), false); + } } visit::visit_expr(ex, cx, v); } @@ -169,7 +172,7 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) { let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f)); vec::iter2(args, arg_ts) {|arg, arg_t| alt arg.node { - expr_fn(_, _, _, _) | expr_fn_block(_, _) + expr_fn(*) | expr_fn_block(*) if is_blockish(ty::ty_fn_proto(arg_t.ty)) { fns += [arg]; } diff --git a/src/rustc/middle/mutbl.rs b/src/rustc/middle/mutbl.rs index 35561c56e65..572d1c234de 100644 --- a/src/rustc/middle/mutbl.rs +++ b/src/rustc/middle/mutbl.rs @@ -199,14 +199,17 @@ fn visit_expr(ex: @expr, &&cx: @ctx, v: visit::vt<@ctx>) { expr_assign(dest, src) | expr_assign_op(_, dest, src) { check_lval(cx, dest, msg_assign); } - expr_fn(_, _, _, cap) { - for cap.moves.each {|moved| - let def = cx.tcx.def_map.get(moved.id); - alt is_illegal_to_modify_def(cx, def, msg_move_out) { - some(name) { mk_err(cx, moved.span, msg_move_out, moved.name); } - _ { } + expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) { + for cap_clause.each { |cap_item| + if cap_item.is_move { + let def = cx.tcx.def_map.get(cap_item.id); + alt is_illegal_to_modify_def(cx, def, msg_move_out) { + some(name) { mk_err(cx, cap_item.span, + msg_move_out, name); } + _ { } + } + cx.mutbl_map.insert(ast_util::def_id_of_def(def).node, ()); } - cx.mutbl_map.insert(ast_util::def_id_of_def(def).node, ()); } } _ { } diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs index 53c52cf9fc2..87ba22fc8bb 100644 --- a/src/rustc/middle/region.rs +++ b/src/rustc/middle/region.rs @@ -286,7 +286,7 @@ fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) { fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) { record_parent(cx, expr.id); alt expr.node { - ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) { + ast::expr_fn(*) | ast::expr_fn_block(*) { let new_cx = {parent: some(expr.id) with cx}; visit::visit_expr(expr, new_cx, visitor); } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 933a3ba48b2..379c5c66ab8 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -390,7 +390,7 @@ fn check_unused_imports(e: @env, level: lint::level) { }; } -fn resolve_capture_item(e: @env, sc: scopes, &&cap_item: @ast::capture_item) { +fn resolve_capture_item(e: @env, sc: scopes, cap_item: ast::capture_item) { let dcur = lookup_in_scope_strict( *e, sc, cap_item.span, cap_item.name, ns_val); maybe_insert(e, cap_item.id, dcur); @@ -453,10 +453,11 @@ fn resolve_names(e: @env, c: @ast::crate) { maybe_insert(e, exp.id, lookup_path_strict(*e, sc, exp.span, p, ns_val)); } - ast::expr_fn(_, _, _, cap_clause) { - let rci = bind resolve_capture_item(e, sc, _); - vec::iter(cap_clause.copies, rci); - vec::iter(cap_clause.moves, rci); + ast::expr_fn(_, _, _, cap_clause) | + ast::expr_fn_block(_, _, cap_clause) { + for cap_clause.each { |ci| + resolve_capture_item(e, sc, ci); + } } _ { } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index a06d78c24bb..6c1b81a371d 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -2544,11 +2544,11 @@ fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id, fn trans_loop_body(bcx: block, e: @ast::expr, ret_flag: option<ValueRef>, dest: dest) -> block { alt check e.node { - ast::expr_loop_body(b@@{node: ast::expr_fn_block(decl, body), _}) { + ast::expr_loop_body(b@@{node: ast::expr_fn_block(decl, body, cap), _}) { alt check ty::get(expr_ty(bcx, e)).struct { ty::ty_fn({proto, _}) { closure::trans_expr_fn(bcx, proto, decl, body, e.span, b.id, - {copies: [], moves: []}, some(ret_flag), + cap, some(ret_flag), dest) } } @@ -2800,7 +2800,7 @@ fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t, -> block { let ret_in_loop = alt args { arg_exprs(args) { args.len() > 0u && alt vec::last(args).node { - ast::expr_loop_body(@{node: ast::expr_fn_block(_, body), _}) { + ast::expr_loop_body(@{node: ast::expr_fn_block(_, body, _), _}) { body_contains_ret(body) } _ { false } @@ -3166,15 +3166,15 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); } ast::expr_fn(proto, decl, body, cap_clause) { ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id, - *cap_clause, none, dest); + cap_clause, none, dest); } - ast::expr_fn_block(decl, body) { + ast::expr_fn_block(decl, body, cap_clause) { alt check ty::get(expr_ty(bcx, e)).struct { ty::ty_fn({proto, _}) { #debug("translating fn_block %s with type %s", expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e))); ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id, - {copies: [], moves: []}, none, dest); + cap_clause, none, dest); } } } diff --git a/src/rustc/middle/trans/debuginfo.rs b/src/rustc/middle/trans/debuginfo.rs index 3176c234b7a..44f66f7a70e 100644 --- a/src/rustc/middle/trans/debuginfo.rs +++ b/src/rustc/middle/trans/debuginfo.rs @@ -772,7 +772,7 @@ fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> { ast::expr_fn(_, decl, _, _) { (dbg_cx.names("fn"), decl.output, expr.id) } - ast::expr_fn_block(decl, _) { + ast::expr_fn_block(decl, _, _) { (dbg_cx.names("fn"), decl.output, expr.id) } _ { fcx.ccx.sess.span_bug(expr.span, "create_function: \ diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs index bee159e3373..3ec11d5b0c4 100644 --- a/src/rustc/middle/trans/type_use.rs +++ b/src/rustc/middle/trans/type_use.rs @@ -160,7 +160,7 @@ fn mark_for_expr(cx: ctx, e: @expr) { } } } - expr_fn(_, _, _, _) | expr_fn_block(_, _) { + expr_fn(*) | expr_fn_block(*) { alt ty::ty_fn_proto(ty::expr_ty(cx.ccx.tcx, e)) { proto_bare | proto_any | proto_uniq {} proto_box | proto_block { diff --git a/src/rustc/middle/tstate/pre_post_conditions.rs b/src/rustc/middle/tstate/pre_post_conditions.rs index 4cc00940202..196b891850e 100644 --- a/src/rustc/middle/tstate/pre_post_conditions.rs +++ b/src/rustc/middle/tstate/pre_post_conditions.rs @@ -340,24 +340,21 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { expr_log(_, lvl, arg) { find_pre_post_exprs(fcx, [lvl, arg], e.id); } - expr_fn(_, _, _, cap_clause) { + expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) { find_pre_post_expr_fn_upvars(fcx, e); - let use_cap_item = fn@(&&cap_item: @capture_item) { + for cap_clause.each { |cap_item| let d = local_node_id_to_local_def_id(fcx, cap_item.id); option::iter(d, { |id| use_var(fcx, id) }); - }; - vec::iter(cap_clause.copies, use_cap_item); - vec::iter(cap_clause.moves, use_cap_item); + } - vec::iter(cap_clause.moves) { |cap_item| - log(debug, ("forget_in_postcond: ", cap_item)); - forget_in_postcond(fcx, e.id, cap_item.id); + for cap_clause.each { |cap_item| + if cap_item.is_move { + log(debug, ("forget_in_postcond: ", cap_item)); + forget_in_postcond(fcx, e.id, cap_item.id); + } } } - expr_fn_block(_, _) { - find_pre_post_expr_fn_upvars(fcx, e); - } expr_block(b) { find_pre_post_block(fcx, b); let p = block_pp(fcx.ccx, b); diff --git a/src/rustc/middle/tstate/states.rs b/src/rustc/middle/tstate/states.rs index efad557fbe7..8397fa879cf 100644 --- a/src/rustc/middle/tstate/states.rs +++ b/src/rustc/middle/tstate/states.rs @@ -363,8 +363,10 @@ fn find_pre_post_state_cap_clause(fcx: fn_ctxt, e_id: node_id, let ccx = fcx.ccx; let pres_changed = set_prestate_ann(ccx, e_id, pres); let post = tritv_clone(pres); - vec::iter(cap_clause.moves) { |cap_item| - forget_in_poststate(fcx, post, cap_item.id); + for cap_clause.each { |cap_item| + if cap_item.is_move { + forget_in_poststate(fcx, post, cap_item.id); + } } ret set_poststate_ann(ccx, e_id, post) || pres_changed; } @@ -418,9 +420,11 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool { expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); } expr_lit(l) { ret pure_exp(fcx.ccx, e.id, pres); } expr_fn(_, _, _, cap_clause) { - ret find_pre_post_state_cap_clause(fcx, e.id, pres, *cap_clause); + ret find_pre_post_state_cap_clause(fcx, e.id, pres, cap_clause); + } + expr_fn_block(_, _, cap_clause) { + ret find_pre_post_state_cap_clause(fcx, e.id, pres, cap_clause); } - expr_fn_block(_, _) { ret pure_exp(fcx.ccx, e.id, pres); } expr_block(b) { ret find_pre_post_state_block(fcx, pres, b) | set_prestate_ann(fcx.ccx, e.id, pres) | diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index d8db45d8919..81e32078215 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -275,7 +275,8 @@ fn instantiate_path(fcx: @fn_ctxt, fcx.write_ty_substs(id, tpt.ty, substs); } -// Type tests +// Resolves `typ` by a single level if `typ` is a type variable. If no +// resolution is possible, then an error is reported. fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t { alt infer::resolve_shallow(fcx.infcx, tp, false) { result::ok(t_s) if !ty::type_is_var(t_s) { ret t_s; } @@ -286,7 +287,6 @@ fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t { } } - // Returns the one-level-deep structure of the given type. fn structure_of(fcx: @fn_ctxt, sp: span, typ: ty::t) -> ty::sty { ty::get(structurally_resolved_type(fcx, sp, typ)).struct @@ -689,7 +689,7 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>( ty::mk_rec(tcx, flds) } ast::ty_fn(proto, decl) { - ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl)) + ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl, none)) } ast::ty_path(path, id) { let a_def = alt tcx.def_map.find(id) { @@ -777,7 +777,13 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>( ty::mk_constr(tcx, ast_ty_to_ty(self, rscope, t), out_cs) } ast::ty_infer { - self.ty_infer(ast_ty.span) + // ty_infer should only appear as the type of arguments or return + // values in a fn_expr, or as the type of local variables. Both of + // these cases are handled specially and should not descend into this + // routine. + self.tcx().sess.span_bug( + ast_ty.span, + "found `ty_infer` in unexpected place"); } ast::ty_mac(_) { tcx.sess.span_bug(ast_ty.span, @@ -850,7 +856,8 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) } ast::item_fn(decl, tps, _) { let bounds = ty_param_bounds(ccx, tps); - let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare, decl); + let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare, + decl, none); let tpt = {bounds: bounds, rp: ast::rp_none, // functions do not have a self ty: ty::mk_fn(ccx.tcx, tofd)}; @@ -884,7 +891,8 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) } ast::item_res(decl, tps, _, _, _, rp) { let {bounds, substs} = mk_substs(ccx, tps, rp); - let t_arg = ty_of_arg(ccx, type_rscope(rp), decl.inputs[0]); + let t_arg = ty_of_arg(ccx, type_rscope(rp), + decl.inputs[0], none); let t = ty::mk_res(tcx, local_def(it.id), t_arg.ty, substs); let t_res = {bounds: bounds, rp: rp, ty: t}; tcx.tcache.insert(local_def(it.id), t_res); @@ -1027,16 +1035,27 @@ fn replace_bound_regions( } fn ty_of_arg<AC: ast_conv, RS: region_scope copy>( - self: AC, rscope: RS, a: ast::arg) -> ty::arg { + self: AC, rscope: RS, a: ast::arg, + expected_ty: option<ty::arg>) -> ty::arg { + + let ty = alt a.ty.node { + ast::ty_infer if expected_ty.is_some() {expected_ty.get().ty} + ast::ty_infer {self.ty_infer(a.ty.span)} + _ {ast_ty_to_ty(self, rscope, a.ty)} + }; - fn arg_mode(tcx: ty::ctxt, m: ast::mode, ty: ty::t) -> ast::mode { - alt m { + let mode = { + alt a.mode { + ast::infer(_) if expected_ty.is_some() { + result::get(ty::unify_mode(self.tcx(), a.mode, + expected_ty.get().mode)) + } ast::infer(_) { alt ty::get(ty).struct { // If the type is not specified, then this must be a fn expr. // Leave the mode as infer(_), it will get inferred based // on constraints elsewhere. - ty::ty_var(_) { m } + ty::ty_var(_) {a.mode} // If the type is known, then use the default for that type. // Here we unify m and the default. This should update the @@ -1044,30 +1063,48 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy>( // will have been unified with m yet: _ { let m1 = ast::expl(ty::default_arg_mode_for_ty(ty)); - result::get(ty::unify_mode(tcx, m, m1)) + result::get(ty::unify_mode(self.tcx(), a.mode, m1)) } } } - ast::expl(_) { m } + ast::expl(_) {a.mode} } - } + }; - let ty = ast_ty_to_ty(self, rscope, a.ty); - let mode = arg_mode(self.tcx(), a.mode, ty); {mode: mode, ty: ty} } + +type expected_tys = option<{inputs: [ty::arg], + output: ty::t}>; + fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>( self: AC, rscope: RS, proto: ast::proto, - decl: ast::fn_decl) -> ty::fn_ty { + decl: ast::fn_decl, + expected_tys: expected_tys) -> ty::fn_ty { #debug["ty_of_fn_decl"]; indent {|| // new region names that appear inside of the fn decl are bound to // that function type let rb = in_binding_rscope(rscope); - let input_tys = vec::map(decl.inputs) { |a| ty_of_arg(self, rb, a) }; - let output_ty = ast_ty_to_ty(self, rb, decl.output); + + let input_tys = decl.inputs.mapi { |i, a| + let expected_arg_ty = expected_tys.chain { |e| + // no guarantee that the correct number of expected args + // were supplied + if i < e.inputs.len() {some(e.inputs[i])} else {none} + }; + ty_of_arg(self, rb, a, expected_arg_ty) + }; + + let expected_ret_ty = expected_tys.map { |e| e.output }; + let output_ty = alt decl.output.node { + ast::ty_infer if expected_ret_ty.is_some() {expected_ret_ty.get()} + ast::ty_infer {self.ty_infer(decl.output.span)} + _ {ast_ty_to_ty(self, rb, decl.output)} + }; + let out_constrs = vec::map(decl.constraints) {|constr| ty::ast_constr_to_constr(self.tcx(), constr) }; @@ -1083,7 +1120,7 @@ fn ty_of_native_fn_decl(ccx: @crate_ctxt, let bounds = ty_param_bounds(ccx, ty_params); let rb = in_binding_rscope(empty_rscope); - let input_tys = vec::map(decl.inputs) { |a| ty_of_arg(ccx, rb, a) }; + let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) }; let output_ty = ast_ty_to_ty(ccx, rb, decl.output); let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare, @@ -1135,7 +1172,8 @@ fn ty_of_method(ccx: @crate_ctxt, rp: ast::region_param) -> ty::method { {ident: m.ident, tps: ty_param_bounds(ccx, m.tps), - fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, m.decl), + fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, + m.decl, none), purity: m.decl.purity, privacy: m.privacy} } @@ -1145,7 +1183,8 @@ fn ty_of_ty_method(self: @crate_ctxt, rp: ast::region_param) -> ty::method { {ident: m.ident, tps: ty_param_bounds(self, m.tps), - fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, m.decl), + fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, + m.decl, none), // assume public, because this is only invoked on iface methods purity: m.decl.purity, privacy: ast::pub} } @@ -1649,7 +1688,8 @@ mod collect { ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) { let {bounds, substs} = mk_substs(ccx, tps, rp); let def_id = local_def(it.id); - let t_arg = ty_of_arg(ccx, type_rscope(rp), decl.inputs[0]); + let t_arg = ty_of_arg(ccx, type_rscope(rp), + decl.inputs[0], none); let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs); let t_ctor = ty::mk_fn(tcx, { @@ -1691,7 +1731,8 @@ mod collect { ty_of_fn_decl(ccx, empty_rscope, ast::proto_any, - ctor.node.dec)); + ctor.node.dec, + none)); write_ty_to_tcx(tcx, ctor.node.id, t_ctor); tcx.tcache.insert(local_def(ctor.node.id), {bounds: tpt.bounds, @@ -1961,7 +2002,7 @@ mod writeback { resolve_type_vars_for_node(wbcx, e.span, e.id); alt e.node { ast::expr_fn(_, decl, _, _) | - ast::expr_fn_block(decl, _) { + ast::expr_fn_block(decl, _, _) { vec::iter(decl.inputs) {|input| let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id); @@ -2902,35 +2943,6 @@ fn region_of(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region { } } -fn check_expr_fn_with_unifier(fcx: @fn_ctxt, - expr: @ast::expr, - proto: ast::proto, - decl: ast::fn_decl, - body: ast::blk, - is_loop_body: bool, - unifier: fn()) { - let tcx = fcx.ccx.tcx; - let fty = ty::mk_fn(tcx, ty_of_fn_decl(fcx, fcx, proto, decl)); - - #debug("check_expr_fn_with_unifier %s fty=%s", - expr_to_str(expr), fcx.ty_to_str(fty)); - - fcx.write_ty(expr.id, fty); - - // Unify the type of the function with the expected type before we - // typecheck the body so that we have more information about the - // argument types in the body. This is needed to make binops and - // record projection work on type inferred arguments. - unifier(); - - let ret_ty = ty::ty_fn_ret(fty); - let arg_tys = vec::map(ty::ty_fn_args(fty)) {|a| a.ty }; - - check_fn(fcx.ccx, proto, decl, body, expr.id, - ret_ty, arg_tys, is_loop_body, some(fcx), - fcx.self_ty); -} - fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, expected: option<ty::t>, @@ -2999,7 +3011,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, alt a_opt { some(a) { let is_block = alt a.node { - ast::expr_fn_block(_, _) { true } + ast::expr_fn_block(*) { true } _ { false } }; if is_block == check_blocks { @@ -3227,6 +3239,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } } } + + // Resolves `expected` by a single level if it is a variable and passes it + // through the `unpack` function. It there is no expected type or + // resolution is not possible (e.g., no constraints yet present), just + // returns `none`. fn unpack_expected<O: copy>(fcx: @fn_ctxt, expected: option<ty::t>, unpack: fn(ty::sty) -> option<O>) -> option<O> { @@ -3241,6 +3258,42 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } } + fn check_expr_fn(fcx: @fn_ctxt, + expr: @ast::expr, + proto: ast::proto, + decl: ast::fn_decl, + body: ast::blk, + is_loop_body: bool, + expected: option<ty::t>) { + let tcx = fcx.ccx.tcx; + + let expected_tys = unpack_expected(fcx, expected) { |sty| + alt sty { + ty::ty_fn(fn_ty) {some({inputs:fn_ty.inputs, + output:fn_ty.output})} + _ {none} + } + }; + + // construct the function type + let fty = ty::mk_fn(tcx, + ty_of_fn_decl(fcx, fcx, proto, decl, + expected_tys)); + + #debug("check_expr_fn_with_unifier %s fty=%s", + expr_to_str(expr), fcx.ty_to_str(fty)); + + fcx.write_ty(expr.id, fty); + + let ret_ty = ty::ty_fn_ret(fty); + let arg_tys = vec::map(ty::ty_fn_args(fty)) {|a| a.ty }; + + check_fn(fcx.ccx, proto, decl, body, expr.id, + ret_ty, arg_tys, is_loop_body, some(fcx), + fcx.self_ty); + } + + let tcx = fcx.ccx.tcx; let id = expr.id; let mut bot = false; @@ -3510,20 +3563,25 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, if !arm_non_bot { result_ty = ty::mk_bot(tcx); } fcx.write_ty(id, result_ty); } - ast::expr_fn(proto, decl, body, captures) { - check_expr_fn_with_unifier(fcx, expr, proto, decl, body, - false, unifier); - capture::check_capture_clause(tcx, expr.id, proto, *captures); + ast::expr_fn(proto, decl, body, cap_clause) { + check_expr_fn(fcx, expr, proto, decl, body, false, expected); + capture::check_capture_clause(tcx, expr.id, proto, cap_clause); } - ast::expr_fn_block(decl, body) { + ast::expr_fn_block(decl, body, cap_clause) { // Take the prototype from the expected type, but default to block: let proto = unpack_expected(fcx, expected, {|sty| alt sty { ty::ty_fn({proto, _}) { some(proto) } _ { none } } }).get_default(ast::proto_box); - check_expr_fn_with_unifier(fcx, expr, proto, decl, body, - false, unifier); + check_expr_fn(fcx, expr, proto, decl, body, false, expected); + capture::check_capture_clause(tcx, expr.id, proto, cap_clause); } ast::expr_loop_body(b) { + // a loop body is the special argument to a `for` loop. We know that + // there will be an expected type in this context because it can only + // appear in the context of a call, so we get the expected type of the + // parameter. The catch here is that we need to validate two things: + // 1. a closure that returns a bool is expected + // 2. the cloure that was given returns unit let expected_sty = unpack_expected(fcx, expected, {|x|some(x)}).get(); let (inner_ty, proto) = alt expected_sty { ty::ty_fn(fty) { @@ -3544,10 +3602,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } }; alt check b.node { - ast::expr_fn_block(decl, body) { - check_expr_fn_with_unifier(fcx, b, proto, decl, body, true) {|| - demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); - } + ast::expr_fn_block(decl, body, cap_clause) { + check_expr_fn(fcx, b, proto, decl, body, true, some(inner_ty)); + demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); + capture::check_capture_clause(tcx, b.id, proto, cap_clause); } } let block_ty = structurally_resolved_type( diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index 227dc0c8645..80af3229bc4 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -130,18 +130,9 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { } // if there is an id, print that instead of the structural type: - alt ty::type_def_id(typ) { - some(def_id) { - let cs = ast_map::path_to_str(ty::item_path(cx, def_id)); - ret alt ty::get(typ).struct { - ty_enum(_, substs) | ty_res(_, _, substs) | ty_class(_, substs) | - ty_iface(_, substs) { - parameterized(cx, cs, substs.self_r, substs.tps) - } - _ { cs } - }; - } - none { /* fallthrough */} + for ty::type_def_id(typ).each { |def_id| + // note that this typedef cannot have type parameters + ret ast_map::path_to_str(ty::item_path(cx, def_id)); } // pretty print the structural type representation: diff --git a/src/test/compile-fail/cap-clause-move-upvar.rs b/src/test/compile-fail/cap-clause-move-upvar.rs index bb0ddcd3cf7..c10fb806af4 100644 --- a/src/test/compile-fail/cap-clause-move-upvar.rs +++ b/src/test/compile-fail/cap-clause-move-upvar.rs @@ -1,8 +1,7 @@ -// error-pattern:upvars (like 'x') cannot be moved into a closure fn main() { let x = 5; - let _y = fn~[move x]() -> int { - let _z = fn~[move x]() -> int { x }; + let _y = fn~(move x) -> int { + let _z = fn~(move x) -> int { x }; //! ERROR moving out of upvar 22 }; } diff --git a/src/test/compile-fail/pptypedef.rs b/src/test/compile-fail/pptypedef.rs new file mode 100644 index 00000000000..941535ef0f1 --- /dev/null +++ b/src/test/compile-fail/pptypedef.rs @@ -0,0 +1,8 @@ +type foo = option<int>; + +fn bar(_t: foo) {} + +fn main() { + // we used to print foo<int>: + bar(some(3u)); //! ERROR mismatched types: expected `foo` +} \ No newline at end of file diff --git a/src/test/compile-fail/vec-concat-bug.rs b/src/test/compile-fail/vec-concat-bug.rs index b75623d6233..510bef1cae0 100644 --- a/src/test/compile-fail/vec-concat-bug.rs +++ b/src/test/compile-fail/vec-concat-bug.rs @@ -4,7 +4,6 @@ fn concat<T: copy>(v: [const [const T]]) -> [T] { // Earlier versions of our type checker accepted this: vec::iter(v) {|&&inner: [T]| //!^ ERROR values differ in mutability - //!^^ ERROR values differ in mutability r += inner; } diff --git a/src/test/pretty/cap-clause.rs b/src/test/pretty/cap-clause.rs index e6a78c3d762..b17ea3fa68e 100644 --- a/src/test/pretty/cap-clause.rs +++ b/src/test/pretty/cap-clause.rs @@ -4,14 +4,14 @@ fn main() { let x = 1; let y = 2; let z = 3; - let l1 = fn@[copy x]() -> int { x + y }; - let l2 = fn@[copy x; move y]() -> int { x + y }; - let l3 = fn@[move z]() -> int { z }; + let l1 = fn@(w: int, copy x) -> int { w + x + y }; + let l2 = fn@(w: int, copy x, move y) -> int { w + x + y }; + let l3 = fn@(w: int, move z) -> int { w + z }; let x = 1; let y = 2; let z = 3; - let s1 = fn~[copy x]() -> int { x + y }; - let s2 = fn~[copy x; move y]() -> int { x + y }; - let s3 = fn~[move z]() -> int { z }; + let s1 = fn~(copy x) -> int { x + y }; + let s2 = fn~(copy x, move y) -> int { x + y }; + let s3 = fn~(move z) -> int { z }; } diff --git a/src/test/run-pass/cap-clause-move.rs b/src/test/run-pass/cap-clause-move.rs index 3050d534ac8..c2e0f4b58dd 100644 --- a/src/test/run-pass/cap-clause-move.rs +++ b/src/test/run-pass/cap-clause-move.rs @@ -1,16 +1,29 @@ fn main() { let x = ~1; let y = ptr::addr_of(*x) as uint; - - let lam_copy = fn@[copy x]() -> uint { ptr::addr_of(*x) as uint }; - let lam_move = fn@[move x]() -> uint { ptr::addr_of(*x) as uint }; + let lam_copy = fn@(copy x) -> uint { ptr::addr_of(*x) as uint }; + let lam_move = fn@(move x) -> uint { ptr::addr_of(*x) as uint }; assert lam_copy() != y; assert lam_move() == y; let x = ~2; let y = ptr::addr_of(*x) as uint; - let snd_copy = fn~[copy x]() -> uint { ptr::addr_of(*x) as uint }; - let snd_move = fn~[move x]() -> uint { ptr::addr_of(*x) as uint }; + let lam_copy: fn@() -> uint = { |copy x| ptr::addr_of(*x) as uint }; + let lam_move: fn@() -> uint = { |move x| ptr::addr_of(*x) as uint }; + assert lam_copy() != y; + assert lam_move() == y; + + let x = ~3; + let y = ptr::addr_of(*x) as uint; + let snd_copy = fn~(copy x) -> uint { ptr::addr_of(*x) as uint }; + let snd_move = fn~(move x) -> uint { ptr::addr_of(*x) as uint }; assert snd_copy() != y; assert snd_move() == y; + + let x = ~4; + let y = ptr::addr_of(*x) as uint; + let lam_copy: fn~() -> uint = { |copy x| ptr::addr_of(*x) as uint }; + let lam_move: fn~() -> uint = { |move x| ptr::addr_of(*x) as uint }; + assert lam_copy() != y; + assert lam_move() == y; } diff --git a/src/test/run-pass/cap-clause-not-used.rs b/src/test/run-pass/cap-clause-not-used.rs index 742214ee2a1..08fca191abd 100644 --- a/src/test/run-pass/cap-clause-not-used.rs +++ b/src/test/run-pass/cap-clause-not-used.rs @@ -1,5 +1,5 @@ // error-pattern: warning: Captured variable 'y' not used in closure fn main() { let x = 5; - let _y = fn~[copy x]() { }; + let _y = fn~(copy x) { }; } diff --git a/src/test/run-pass/last-use-in-cap-clause.rs b/src/test/run-pass/last-use-in-cap-clause.rs index 6ac7404c3ce..fc5425f24e9 100644 --- a/src/test/run-pass/last-use-in-cap-clause.rs +++ b/src/test/run-pass/last-use-in-cap-clause.rs @@ -3,7 +3,7 @@ fn foo() -> fn@() -> int { let k = ~22; let _u = {a: k}; - ret fn@[move k]() -> int { 22 }; + ret fn@(move k) -> int { 22 }; } fn main() { |
