diff --git a/src/Makefile b/src/Makefile index 56a98b984f5..ac7dfcbbb71 100644 --- a/src/Makefile +++ b/src/Makefile @@ -447,6 +447,7 @@ TEST_XFAILS_RUSTC := $(filter-out \ div-mod.rs \ drop-bind-thunk-args.rs \ drop-on-ret.rs \ + else-if.rs \ fact.rs \ fn-lval.rs \ fun-call-variants.rs \ diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index 9fddb66f397..be47615a502 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -149,7 +149,7 @@ tag expr_ { expr_unary(unop, @expr, ann); expr_lit(@lit, ann); expr_cast(@expr, @ty, ann); - expr_if(@expr, block, option.t[block], ann); + expr_if(@expr, block, vec[tup(@expr, block)], option.t[block], ann); expr_while(@expr, block, ann); expr_for(@decl, @expr, block, ann); expr_do_while(block, @expr, ann); diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs index bef37a3cc7e..0c90df5c783 100644 --- a/src/comp/front/parser.rs +++ b/src/comp/front/parser.rs @@ -891,18 +891,40 @@ impure fn parse_if_expr(parser p) -> @ast.expr { auto cond = parse_expr(p); expect(p, token.RPAREN); auto thn = parse_block(p); - let option.t[ast.block] els = none[ast.block]; hi = thn.span; - alt (p.peek()) { - case (token.ELSE) { - p.bump(); - auto eblk = parse_block(p); - els = some(eblk); - hi = eblk.span; + + let vec[tup(@ast.expr, ast.block)] elifs = vec(); + let option.t[ast.block] els = none[ast.block]; + let bool parsing_elses = true; + while (parsing_elses) { + alt (p.peek()) { + case (token.ELSE) { + expect(p, token.ELSE); + alt (p.peek()) { + case (token.IF) { + expect(p, token.IF); + expect(p, token.LPAREN); + auto elifcond = parse_expr(p); + expect(p, token.RPAREN); + auto elifthn = parse_block(p); + elifs += tup(elifcond, elifthn); + hi = elifthn.span; + } + case (_) { + auto eblk = parse_block(p); + els = some(eblk); + hi = eblk.span; + parsing_elses = false; + } + } + } + case (_) { + parsing_elses = false; + } } - case (_) { /* fall through */ } } - ret @spanned(lo, hi, ast.expr_if(cond, thn, els, ast.ann_none)); + + ret @spanned(lo, hi, ast.expr_if(cond, thn, elifs, els, ast.ann_none)); } impure fn parse_head_local(parser p) -> @ast.decl { @@ -1331,7 +1353,7 @@ fn stmt_ends_with_semi(@ast.stmt stmt) -> bool { case (ast.expr_unary(_,_,_)) { ret true; } case (ast.expr_lit(_,_)) { ret true; } case (ast.expr_cast(_,_,_)) { ret true; } - case (ast.expr_if(_,_,_,_)) { ret false; } + case (ast.expr_if(_,_,_,_,_)) { ret false; } case (ast.expr_for(_,_,_,_)) { ret false; } case (ast.expr_while(_,_,_)) { ret false; } case (ast.expr_do_while(_,_,_)) { ret false; } diff --git a/src/comp/middle/fold.rs b/src/comp/middle/fold.rs index 67c260140b7..7e6839a831e 100644 --- a/src/comp/middle/fold.rs +++ b/src/comp/middle/fold.rs @@ -28,6 +28,7 @@ import front.ast.def; import front.ast.def_id; import front.ast.ann; +import std._uint; import std._vec; type ast_fold[ENV] = @@ -100,6 +101,7 @@ type ast_fold[ENV] = (fn(&ENV e, &span sp, @expr cond, &block thn, + &vec[tup(@expr, block)] elifs, &option.t[block] els, ann a) -> @expr) fold_expr_if, @@ -501,9 +503,19 @@ fn fold_expr[ENV](&ENV env, ast_fold[ENV] fld, &@expr e) -> @expr { ret fld.fold_expr_cast(env_, e.span, ee, tt, at); } - case (ast.expr_if(?cnd, ?thn, ?els, ?t)) { + case (ast.expr_if(?cnd, ?thn, ?elifs, ?els, ?t)) { auto ccnd = fold_expr(env_, fld, cnd); auto tthn = fold_block(env_, fld, thn); + + let vec[tup(@ast.expr, ast.block)] eelifs = vec(); + for (tup(@expr, block) elif in elifs) { + auto elifcnd = elif._0; + auto elifthn = elif._1; + auto elifccnd = fold_expr(env_, fld, elifcnd); + auto eliftthn = fold_block(env_, fld, elifthn); + eelifs += tup(elifccnd, eliftthn); + } + auto eels = none[block]; alt (els) { case (some[block](?b)) { @@ -511,7 +523,7 @@ fn fold_expr[ENV](&ENV env, ast_fold[ENV] fld, &@expr e) -> @expr { } case (_) { /* fall through */ } } - ret fld.fold_expr_if(env_, e.span, ccnd, tthn, eels, t); + ret fld.fold_expr_if(env_, e.span, ccnd, tthn, eelifs, eels, t); } case (ast.expr_for(?decl, ?seq, ?body, ?t)) { @@ -961,8 +973,9 @@ fn identity_fold_expr_cast[ENV](&ENV env, &span sp, @ast.expr e, fn identity_fold_expr_if[ENV](&ENV env, &span sp, @expr cond, &block thn, + &vec[tup(@expr, block)] elifs, &option.t[block] els, ann a) -> @expr { - ret @respan(sp, ast.expr_if(cond, thn, els, a)); + ret @respan(sp, ast.expr_if(cond, thn, elifs, els, a)); } fn identity_fold_expr_for[ENV](&ENV env, &span sp, @@ -1237,7 +1250,7 @@ fn new_identity_fold[ENV]() -> ast_fold[ENV] { fold_expr_unary = bind identity_fold_expr_unary[ENV](_,_,_,_,_), fold_expr_lit = bind identity_fold_expr_lit[ENV](_,_,_,_), fold_expr_cast = bind identity_fold_expr_cast[ENV](_,_,_,_,_), - fold_expr_if = bind identity_fold_expr_if[ENV](_,_,_,_,_,_), + fold_expr_if = bind identity_fold_expr_if[ENV](_,_,_,_,_,_,_), fold_expr_for = bind identity_fold_expr_for[ENV](_,_,_,_,_,_), fold_expr_while = bind identity_fold_expr_while[ENV](_,_,_,_,_), fold_expr_do_while diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 446b4f10f08..c08147fec5a 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -1957,8 +1957,9 @@ fn join_results(@block_ctxt parent_cx, ret res(join_cx, phi); } -fn trans_if(@block_ctxt cx, @ast.expr cond, - &ast.block thn, &option.t[ast.block] els) -> result { +fn trans_if(@block_ctxt cx, @ast.expr cond, &ast.block thn, + &vec[tup(@ast.expr, ast.block)] elifs, + &option.t[ast.block] els) -> result { auto cond_res = trans_expr(cx, cond); @@ -1968,11 +1969,25 @@ fn trans_if(@block_ctxt cx, @ast.expr cond, auto else_cx = new_scope_block_ctxt(cx, "else"); auto else_res = res(else_cx, C_nil()); - alt (els) { - case (some[ast.block](?eblk)) { - else_res = trans_block(else_cx, eblk); + auto num_elifs = _vec.len[tup(@ast.expr, ast.block)](elifs); + if (num_elifs > 0u) { + auto next_elif = elifs.(0u); + auto next_elifthn = next_elif._0; + auto next_elifcnd = next_elif._1; + auto rest_elifs = _vec.shift[tup(@ast.expr, ast.block)](elifs); + else_res = trans_if(else_cx, next_elifthn, next_elifcnd, + rest_elifs, els); + } + + /* else: FIXME: rustboot has a problem here + with preconditions inside an else block */ + if (num_elifs == 0u) { + alt (els) { + case (some[ast.block](?eblk)) { + else_res = trans_block(else_cx, eblk); + } + case (_) { /* fall through */ } } - case (_) { /* fall through */ } } cond_res.bcx.build.CondBr(cond_res.val, @@ -2901,8 +2916,8 @@ fn trans_expr(@block_ctxt cx, @ast.expr e) -> result { ret trans_binary(cx, op, x, y); } - case (ast.expr_if(?cond, ?thn, ?els, _)) { - ret trans_if(cx, cond, thn, els); + case (ast.expr_if(?cond, ?thn, ?elifs, ?els, _)) { + ret trans_if(cx, cond, thn, elifs, els); } case (ast.expr_for(?decl, ?seq, ?body, _)) { diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index b840be91384..2f14aae6259 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -653,7 +653,7 @@ fn expr_ty(@ast.expr expr) -> @t { case (ast.expr_unary(_, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_lit(_, ?ann)) { ret ann_to_type(ann); } case (ast.expr_cast(_, _, ?ann)) { ret ann_to_type(ann); } - case (ast.expr_if(_, _, _, ?ann)) { ret ann_to_type(ann); } + case (ast.expr_if(_, _, _, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_for(_, _, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_while(_, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_do_while(_, _, ?ann)) { ret ann_to_type(ann); } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index ea9002aeccb..7112e982743 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -864,10 +864,19 @@ fn demand_expr_full(&@fn_ctxt fcx, @ty.t expected, @ast.expr e, auto t = demand(fcx, e.span, expected, ann_to_type(ann)); e_1 = ast.expr_cast(sube, ast_ty, ast.ann_type(t)); } - case (ast.expr_if(?cond, ?then_0, ?else_0, ?ann)) { + case (ast.expr_if(?cond, ?then_0, ?elifs_0, ?else_0, ?ann)) { auto t = demand_full(fcx, e.span, expected, ann_to_type(ann), adk); auto then_1 = demand_block(fcx, expected, then_0); + + let vec[tup(@ast.expr, ast.block)] elifs_1 = vec(); + for (tup(@ast.expr, ast.block) elif in elifs_0) { + auto elifcond = elif._0; + auto elifthn_0 = elif._1; + auto elifthn_1 = demand_block(fcx, expected, elifthn_0); + elifs_1 += tup(elifcond, elifthn_1); + } + auto else_1; alt (else_0) { case (none[ast.block]) { else_1 = none[ast.block]; } @@ -876,7 +885,7 @@ fn demand_expr_full(&@fn_ctxt fcx, @ty.t expected, @ast.expr e, else_1 = some[ast.block](b_1); } } - e_1 = ast.expr_if(cond, then_1, else_1, ast.ann_type(t)); + e_1 = ast.expr_if(cond, then_1, elifs_1, else_1, ast.ann_type(t)); } case (ast.expr_for(?decl, ?seq, ?bloc, ?ann)) { auto t = demand(fcx, e.span, expected, ann_to_type(ann)); @@ -1195,13 +1204,28 @@ fn check_expr(&@fn_ctxt fcx, @ast.expr expr) -> @ast.expr { ann)); } - case (ast.expr_if(?cond, ?thn, ?elsopt, _)) { + case (ast.expr_if(?cond, ?thn, ?elifs, ?elsopt, _)) { auto cond_0 = check_expr(fcx, cond); auto cond_1 = demand_expr(fcx, plain_ty(ty.ty_bool), cond_0); auto thn_0 = check_block(fcx, thn); auto thn_t = block_ty(thn_0); + auto num_elifs = _vec.len[tup(@ast.expr, ast.block)](elifs); + let vec[tup(@ast.expr, ast.block)] elifs_1 = vec(); + for each (uint i in _uint.range(0u, num_elifs)) { + auto elif = elifs.(i); + auto elifcond = elif._0; + auto elifcond_0 = check_expr(fcx, cond); + auto elifcond_1 = demand_expr(fcx, + plain_ty(ty.ty_bool), + elifcond_0); + auto elifthn = elif._1; + auto elifthn_0 = check_block(fcx, elifthn); + auto elifthn_1 = demand_block(fcx, thn_t, elifthn_0); + elifs_1 += tup(elifcond_1, elifthn_1); + } + auto elsopt_1; auto elsopt_t; alt (elsopt) { @@ -1220,7 +1244,8 @@ fn check_expr(&@fn_ctxt fcx, @ast.expr expr) -> @ast.expr { auto thn_1 = demand_block(fcx, elsopt_t, thn_0); ret @fold.respan[ast.expr_](expr.span, - ast.expr_if(cond_1, thn_1, elsopt_1, + ast.expr_if(cond_1, thn_1, + elifs_1, elsopt_1, ast.ann_type(elsopt_t))); }