Skip to content

Commit fb2aeaf

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. As per rust-lang#2817
1 parent 2c2398c commit fb2aeaf

File tree

5 files changed

+74
-20
lines changed

5 files changed

+74
-20
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: 44 additions & 16 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_nil(e) && ty::type_is_bool(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_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) => {
@@ -2101,8 +2131,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
21012131
match b.node {
21022132
ast::expr_fn_block(decl, ref body, cap_clause) => {
21032133
check_expr_fn(fcx, b, None,
2104-
decl, (*body), true,
2105-
Some(inner_ty));
2134+
decl, (*body), ForLoop, Some(inner_ty));
21062135
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
21072136
capture::check_capture_clause(tcx, b.id, cap_clause);
21082137
}
@@ -2145,8 +2174,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
21452174
match b.node {
21462175
ast::expr_fn_block(decl, ref body, cap_clause) => {
21472176
check_expr_fn(fcx, b, None,
2148-
decl, (*body), true,
2149-
Some(inner_ty));
2177+
decl, (*body), DoBlock, Some(inner_ty));
21502178
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
21512179
capture::check_capture_clause(tcx, b.id, cap_clause);
21522180
}
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
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
for uint::range(0, 100000) |_i| { //~ ERROR A for-loop body must return (), but
9+
false
10+
}
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
}

0 commit comments

Comments
 (0)