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 for [T] { #[inline] fn map(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(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/parse/parser.rs b/src/librustsyntax/parse/parser.rs index 0c681900dd5..7eb537d8cfe 100644 --- a/src/librustsyntax/parse/parser.rs +++ b/src/librustsyntax/parse/parser.rs @@ -1206,7 +1206,7 @@ fn parse_capture_clause(p: parser) -> @ast::capture_clause { 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 decl = parse_fn_decl(p, ast::impure_fn, parse_fn_block_arg); let body = parse_block(p); ret mk_expr(p, lo, body.span.hi, ast::expr_fn(proto, decl, body, capture_clause)); @@ -1699,11 +1699,12 @@ fn parse_ty_params(p: parser) -> [ast::ty_param] { } else { [] } } -fn parse_fn_decl(p: parser, purity: ast::purity) +fn parse_fn_decl(p: parser, purity: ast::purity, + parse_arg_fn: fn(parser) -> ast::arg) -> ast::fn_decl { let inputs: ast::spanned<[ast::arg]> = parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), - parse_arg, p); + parse_arg_fn, p); // 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. @@ -1760,7 +1761,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 +1786,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 +1970,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 +2049,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..c6417ab01f4 100644 --- a/src/librustsyntax/print/pprust.rs +++ b/src/librustsyntax/print/pprust.rs @@ -1351,13 +1351,6 @@ fn print_cap_clause(s: ps, cap_clause: ast::capture_clause) { fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl) { 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); pclose(s); word(s.s, constrs_str(decl.constraints, {|c| @@ -1374,16 +1367,6 @@ fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl) { fn print_fn_block_args(s: ps, decl: ast::fn_decl) { 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); word(s.s, "|"); if decl.output.node != ast::ty_infer { @@ -1541,6 +1524,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, decl: ast::fn_decl, id: option, tps: option<[ast::ty_param]>) { @@ -1550,13 +1550,6 @@ fn print_ty_fn(s: ps, opt_proto: option, 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/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index d8db45d8919..5ca8ac04342 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( 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( 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( - self: AC, rscope: RS, a: ast::arg) -> ty::arg { + self: AC, rscope: RS, a: ast::arg, + expected_ty: option) -> ty::arg { - fn arg_mode(tcx: ty::ctxt, m: ast::mode, ty: ty::t) -> ast::mode { - alt m { + 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)} + }; + + 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( // 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( 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, @@ -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, @@ -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(fcx: @fn_ctxt, expected: option, unpack: fn(ty::sty) -> option) -> option { @@ -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) { + 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; @@ -3511,8 +3564,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, 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); + check_expr_fn(fcx, expr, proto, decl, body, false, expected); capture::check_capture_clause(tcx, expr.id, proto, *captures); } ast::expr_fn_block(decl, body) { @@ -3520,10 +3572,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, 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); } 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) { @@ -3545,9 +3602,8 @@ 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)); - } + check_expr_fn(fcx, b, proto, decl, body, true, some(inner_ty)); + demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); } } 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/omitted-arg-in-item-fn.rs b/src/test/compile-fail/omitted-arg-in-item-fn.rs new file mode 100644 index 00000000000..8478a925eb6 --- /dev/null +++ b/src/test/compile-fail/omitted-arg-in-item-fn.rs @@ -0,0 +1,2 @@ +fn foo(x) { //! ERROR expecting ':' but found ')' +} \ No newline at end of file diff --git a/src/test/compile-fail/omitted-arg-wrong-types.rs b/src/test/compile-fail/omitted-arg-wrong-types.rs new file mode 100644 index 00000000000..604fed1bc61 --- /dev/null +++ b/src/test/compile-fail/omitted-arg-wrong-types.rs @@ -0,0 +1,9 @@ +fn let_in(x: T, f: fn(T)) {} + +fn main() { + let_in(3u, fn&(i) { assert i == 3; }); + //!^ ERROR expected `uint` but found `int` + + let_in(3, fn&(i) { assert i == 3u; }); + //!^ ERROR expected `int` but found `uint` +} \ No newline at end of file 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; + +fn bar(_t: foo) {} + +fn main() { + // we used to print foo: + 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(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/run-pass/omitted-arg-type.rs b/src/test/run-pass/omitted-arg-type.rs new file mode 100644 index 00000000000..3ae04461bcd --- /dev/null +++ b/src/test/run-pass/omitted-arg-type.rs @@ -0,0 +1,8 @@ +fn let_in(x: T, f: fn(T)) {} + +fn main() { + let_in(3u) { |i| assert i == 3u; }; + let_in(3) { |i| assert i == 3; }; + let_in(3u, fn&(i) { assert i == 3u; }); + let_in(3, fn&(i) { assert i == 3; }); +} \ No newline at end of file