allow fn exprs to omit arg types

also, avoid using type variables for fn args with omitted types
unless necessary.  This will be important for bound regions in
fn types.

fixes #2093
This commit is contained in:
Niko Matsakis 2012-05-03 09:09:55 -07:00
parent 6e5c8a7fb8
commit 1ba4ca4c4a
10 changed files with 178 additions and 103 deletions

View File

@ -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
"]

View File

@ -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,

View File

@ -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<ast::proto>,
decl: ast::fn_decl, id: option<ast::ident>,
tps: option<[ast::ty_param]>) {
@ -1550,13 +1550,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);

View File

@ -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 {
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<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,
@ -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>,
@ -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;
@ -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(

View File

@ -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:

View File

@ -0,0 +1,2 @@
fn foo(x) { //! ERROR expecting ':' but found ')'
}

View File

@ -0,0 +1,9 @@
fn let_in<T>(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`
}

View File

@ -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`
}

View File

@ -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;
}

View File

@ -0,0 +1,8 @@
fn let_in<T>(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; });
}