Skip to content

Commit 42f8a33

Browse files
committed
Print out a more helpful type error message for do-blocks/for-loops
If a do-block body has the wrong type, or a for-loop body has a non-() type, suggest that the user might have meant the other one. Closes #2817 r=brson
1 parent 6630d75 commit 42f8a33

File tree

5 files changed

+103
-30
lines changed

5 files changed

+103
-30
lines changed

src/librustc/middle/typeck/check/demand.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ use check::fn_ctxt;
1414
// don't.
1515
fn suptype(fcx: @fn_ctxt, sp: span,
1616
expected: ty::t, actual: ty::t) {
17+
suptype_with_fn(fcx, sp, expected, actual,
18+
|sp, e, a, s| { fcx.report_mismatched_types(sp, e, a, s) })
19+
}
20+
21+
fn suptype_with_fn(fcx: @fn_ctxt, sp: span,
22+
expected: ty::t, actual: ty::t,
23+
handle_err: fn(span, ty::t, ty::t, &ty::type_err)) {
1724

1825
// n.b.: order of actual, expected is reversed
1926
match infer::mk_subty(fcx.infcx(), false, sp,
2027
actual, expected) {
2128
result::Ok(()) => { /* ok */ }
2229
result::Err(ref err) => {
23-
fcx.report_mismatched_types(sp, expected, actual, err);
30+
handle_err(sp, expected, actual, err);
2431
}
2532
}
2633
}

src/librustc/middle/typeck/check/mod.rs

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ struct inherited {
133133
adjustments: HashMap<ast::node_id, @ty::AutoAdjustment>
134134
}
135135

136+
enum FnKind { ForLoop, DoBlock, Vanilla }
137+
136138
struct fn_ctxt {
137139
// var_bindings, locals and next_var_id are shared
138140
// with any nested functions that capture the environment
@@ -158,6 +160,11 @@ struct fn_ctxt {
158160
// can actually be made to live as long as it needs to live.
159161
mut region_lb: ast::node_id,
160162

163+
// Says whether we're inside a for loop, in a do block
164+
// or neither. Helps with error messages involving the
165+
// function return type.
166+
fn_kind: FnKind,
167+
161168
in_scope_regions: isr_alist,
162169

163170
inh: @inherited,
@@ -187,6 +194,7 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
187194
purity: ast::pure_fn,
188195
mut region_lb: region_bnd,
189196
in_scope_regions: @Nil,
197+
fn_kind: Vanilla,
190198
inh: blank_inherited(ccx),
191199
ccx: ccx
192200
}
@@ -208,7 +216,7 @@ fn check_bare_fn(ccx: @crate_ctxt,
208216
let fty = ty::node_id_to_type(ccx.tcx, id);
209217
match ty::get(fty).sty {
210218
ty::ty_fn(ref fn_ty) => {
211-
check_fn(ccx, self_info, fn_ty, decl, body, false, None)
219+
check_fn(ccx, self_info, fn_ty, decl, body, Vanilla, None)
212220
}
213221
_ => ccx.tcx.sess.impossible_case(body.span,
214222
"check_bare_fn: function type expected")
@@ -220,10 +228,13 @@ fn check_fn(ccx: @crate_ctxt,
220228
fn_ty: &ty::FnTy,
221229
decl: ast::fn_decl,
222230
body: ast::blk,
223-
indirect_ret: bool,
231+
fn_kind: FnKind,
224232
old_fcx: Option<@fn_ctxt>) {
225233

226234
let tcx = ccx.tcx;
235+
let indirect_ret = match fn_kind {
236+
ForLoop => true, _ => false
237+
};
227238

228239
// ______________________________________________________________________
229240
// First, we have to replace any bound regions in the fn and self
@@ -276,6 +287,7 @@ fn check_fn(ccx: @crate_ctxt,
276287
purity: purity,
277288
mut region_lb: body.node.id,
278289
in_scope_regions: isr,
290+
fn_kind: fn_kind,
279291
inh: inherited,
280292
ccx: ccx
281293
}
@@ -304,7 +316,11 @@ fn check_fn(ccx: @crate_ctxt,
304316
match body.node.expr {
305317
Some(tail_expr) => {
306318
let tail_expr_ty = fcx.expr_ty(tail_expr);
307-
demand::suptype(fcx, tail_expr.span, fcx.ret_ty, tail_expr_ty);
319+
// Special case: we print a special error if there appears
320+
// to be do-block/for-loop confusion
321+
demand::suptype_with_fn(fcx, tail_expr.span, fcx.ret_ty, tail_expr_ty,
322+
|sp, e, a, s| {
323+
fcx.report_mismatched_return_types(sp, e, a, s) });
308324
}
309325
None => ()
310326
}
@@ -774,11 +790,27 @@ impl @fn_ctxt {
774790
self.infcx().type_error_message(sp, mk_msg, actual_ty, err);
775791
}
776792

777-
fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
793+
fn report_mismatched_return_types(sp: span, e: ty::t, a: ty::t,
778794
err: &ty::type_err) {
779-
self.infcx().report_mismatched_types(sp, e, a, err);
795+
match self.fn_kind {
796+
ForLoop if !ty::type_is_bool(e) && !ty::type_is_nil(a) =>
797+
self.tcx().sess.span_err(sp, fmt!("A for-loop body must \
798+
return (), but it returns %s here. \
799+
Perhaps you meant to write a `do`-block?",
800+
ty_to_str(self.tcx(), a))),
801+
DoBlock if ty::type_is_bool(e) && ty::type_is_nil(a) =>
802+
// If we expected bool and got ()...
803+
self.tcx().sess.span_err(sp, fmt!("Do-block body must \
804+
return %s, but returns () here. Perhaps you meant \
805+
to write a `for`-loop?", ty_to_str(self.tcx(), e))),
806+
_ => self.infcx().report_mismatched_types(sp, e, a, err)
807+
}
780808
}
781809

810+
fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
811+
err: &ty::type_err) {
812+
self.infcx().report_mismatched_types(sp, e, a, err)
813+
}
782814
}
783815

784816
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> (ty::t, uint) {
@@ -1087,9 +1119,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
10871119
DontDerefArgs => {}
10881120
}
10891121

1122+
// mismatch error happens in here
10901123
bot |= check_expr_with_assignability(fcx,
10911124
*arg, formal_ty);
1092-
fcx.write_ty(arg.id, fcx.expr_ty(*arg));
10931125

10941126
}
10951127
}
@@ -1414,7 +1446,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
14141446
ast_proto_opt: Option<ast::Proto>,
14151447
decl: ast::fn_decl,
14161448
body: ast::blk,
1417-
is_loop_body: bool,
1449+
fn_kind: FnKind,
14181450
expected: Option<ty::t>) {
14191451
let tcx = fcx.ccx.tcx;
14201452

@@ -1473,7 +1505,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
14731505
fcx.write_ty(expr.id, fty);
14741506

14751507
check_fn(fcx.ccx, None, &fn_ty, decl, body,
1476-
is_loop_body, Some(fcx));
1508+
fn_kind, Some(fcx));
14771509
}
14781510

14791511

@@ -2041,14 +2073,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
20412073
}
20422074
ast::expr_fn(proto, decl, ref body, cap_clause) => {
20432075
check_expr_fn(fcx, expr, Some(proto),
2044-
decl, (*body), false,
2045-
expected);
2076+
decl, (*body), Vanilla, expected);
20462077
capture::check_capture_clause(tcx, expr.id, cap_clause);
20472078
}
20482079
ast::expr_fn_block(decl, ref body, cap_clause) => {
20492080
check_expr_fn(fcx, expr, None,
2050-
decl, (*body), false,
2051-
expected);
2081+
decl, (*body), Vanilla, expected);
20522082
capture::check_capture_clause(tcx, expr.id, cap_clause);
20532083
}
20542084
ast::expr_loop_body(b) => {
@@ -2058,6 +2088,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
20582088
// parameter. The catch here is that we need to validate two things:
20592089
// 1. a closure that returns a bool is expected
20602090
// 2. the closure that was given returns unit
2091+
let mut err_happened = false;
20612092
let expected_sty = unpack_expected(fcx, expected, |x| Some(x));
20622093
let inner_ty = match expected_sty {
20632094
Some(ty::ty_fn(ref fty)) => {
@@ -2071,8 +2102,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
20712102
should return `bool`, not `%s`", actual)
20722103
},
20732104
(*fty).sig.output, None);
2105+
err_happened = true;
20742106
fcx.write_ty(id, ty::mk_err(tcx));
2075-
return true;
20762107
}
20772108
}
20782109
ty::mk_fn(tcx, FnTyBase {
@@ -2091,8 +2122,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
20912122
actual)
20922123
},
20932124
expected_t, None);
2094-
fcx.write_ty(id, ty::mk_err(tcx));
2095-
return true;
2125+
let err_ty = ty::mk_err(tcx);
2126+
fcx.write_ty(id, err_ty);
2127+
err_happened = true;
2128+
err_ty
20962129
}
20972130
None => fcx.tcx().sess.impossible_case(expr.span,
20982131
~"loop body must have an expected type")
@@ -2101,8 +2134,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
21012134
match b.node {
21022135
ast::expr_fn_block(decl, ref body, cap_clause) => {
21032136
check_expr_fn(fcx, b, None,
2104-
decl, (*body), true,
2105-
Some(inner_ty));
2137+
decl, (*body), ForLoop, Some(inner_ty));
21062138
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
21072139
capture::check_capture_clause(tcx, b.id, cap_clause);
21082140
}
@@ -2113,11 +2145,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
21132145
fcx, expr.span, fcx.node_ty(b.id));
21142146
match ty::get(block_ty).sty {
21152147
ty::ty_fn(ref fty) => {
2116-
fcx.write_ty(expr.id, ty::mk_fn(tcx, FnTyBase {
2117-
meta: (*fty).meta,
2118-
sig: FnSig {output: ty::mk_bool(tcx),
2119-
..(*fty).sig}
2120-
}))
2148+
if !err_happened {
2149+
fcx.write_ty(expr.id, ty::mk_fn(tcx, FnTyBase {
2150+
meta: (*fty).meta,
2151+
sig: FnSig {output: ty::mk_bool(tcx),
2152+
..(*fty).sig}
2153+
}));
2154+
}
2155+
else {
2156+
fcx.write_ty(expr.id, ty::mk_err(fcx.tcx()));
2157+
}
21212158
}
21222159
_ => fail ~"expected fn type"
21232160
}
@@ -2135,8 +2172,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
21352172
function as its last argument, or wrong number \
21362173
of arguments passed to a `do` function"
21372174
}, expected_t, None);
2138-
fcx.write_ty(id, ty::mk_err(tcx));
2139-
return true;
2175+
let err_ty = ty::mk_err(tcx);
2176+
fcx.write_ty(id, err_ty);
2177+
err_ty
21402178
}
21412179
None => fcx.tcx().sess.impossible_case(expr.span,
21422180
~"do body must have expected type")
@@ -2145,8 +2183,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
21452183
match b.node {
21462184
ast::expr_fn_block(decl, ref body, cap_clause) => {
21472185
check_expr_fn(fcx, b, None,
2148-
decl, (*body), true,
2149-
Some(inner_ty));
2186+
decl, (*body), DoBlock, Some(inner_ty));
21502187
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
21512188
capture::check_capture_clause(tcx, b.id, cap_clause);
21522189
}
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
// error-pattern:mismatched types: expected `()` but found `bool`
2-
31
fn main() {
4-
for vec::each(~[0]) |_i| {
2+
for vec::each(~[0]) |_i| { //~ ERROR A for-loop body must return (), but
53
true
64
}
75
}

src/test/compile-fail/issue-2817-2.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn not_bool(f: fn(int) -> ~str) {}
2+
3+
fn main() {
4+
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
5+
false
6+
};
7+
for not_bool |_i| { //~ ERROR a `loop` function's last argument should return `bool`
8+
//~^ ERROR A for-loop body must return (), but
9+
~"hi"
10+
};
11+
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
12+
~"hi"
13+
};
14+
for not_bool() |_i| { //~ ERROR a `loop` function's last argument
15+
};
16+
}

src/test/compile-fail/issue-2817.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn uuid() -> uint { fail; }
2+
3+
fn from_str(s: ~str) -> uint { fail; }
4+
fn to_str(u: uint) -> ~str { fail; }
5+
fn uuid_random() -> uint { fail; }
6+
7+
fn main() {
8+
do uint::range(0, 100000) |_i| { //~ ERROR Do-block body must return bool, but
9+
}
10+
// should get a more general message if the callback
11+
// doesn't return nil
12+
do uint::range(0, 100000) |_i| { //~ ERROR mismatched types
13+
~"str"
14+
}
15+
}

0 commit comments

Comments
 (0)