Skip to content

Commit 6296dc0

Browse files
committed
auto merge of #8141 : graydon/rust/foreach-in-sketch, r=brson
This is a preliminary implementation of `for ... in ... { ...}` using a transitionary keyword `foreach`. Codesize seems to be a little bit down (10% or less non-opt) and otherwise it seems quite trivial to rewrite lambda-based loops to use it. Once we've rewritten the codebase away from lambda-based `for` we can retarget that word at the same production, snapshot, rewrite the keywords in one go, and expire `foreach`. Feedback welcome. It's a desugaring-based approach which is arguably something we should have been doing for other constructs before. I apologize both for the laziness associated with doing it this way and with any sense that I'm bending rules I put in place previously concerning "never doing desugarings". I put the expansion in `expand.rs` and would be amenable to the argument that the code there needs better factoring / more helpers / to move to a submodule or helper function. It does seem to work at this point, though, and I gather we'd like to get the shift done relatively quickly.
2 parents 8b7e241 + a696f0f commit 6296dc0

25 files changed

+374
-4
lines changed

src/librustc/middle/cfg/construct.rs

+2
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ impl CFGBuilder {
239239
expr_exit
240240
}
241241

242+
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
243+
242244
ast::expr_loop(ref body, _) => {
243245
//
244246
// [pred]

src/librustc/middle/dataflow.rs

+2
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
583583
copy_bits(new_loop_scope.break_bits, in_out);
584584
}
585585

586+
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
587+
586588
ast::expr_loop(ref blk, _) => {
587589
//
588590
// (expr) <--+

src/librustc/middle/liveness.rs

+4
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ fn visit_expr(expr: @expr, (this, vt): (@mut IrMaps, vt<@mut IrMaps>)) {
503503
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
504504
visit::visit_expr(expr, (this, vt));
505505
}
506+
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
506507
expr_binary(_, op, _, _) if ast_util::lazy_binop(op) => {
507508
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
508509
visit::visit_expr(expr, (this, vt));
@@ -1057,6 +1058,8 @@ impl Liveness {
10571058
self.propagate_through_loop(expr, Some(cond), blk, succ)
10581059
}
10591060

1061+
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
1062+
10601063
// Note that labels have been resolved, so we don't need to look
10611064
// at the label ident
10621065
expr_loop(ref blk, _) => {
@@ -1487,6 +1490,7 @@ fn check_expr(expr: @expr, (this, vt): (@Liveness, vt<@Liveness>)) {
14871490
expr_paren(*) | expr_fn_block(*) | expr_path(*) | expr_self(*) => {
14881491
visit::visit_expr(expr, (this, vt));
14891492
}
1493+
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
14901494
}
14911495
}
14921496

src/librustc/middle/mem_categorization.rs

+2
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ impl mem_categorization_ctxt {
435435
ast::expr_inline_asm(*) => {
436436
return self.cat_rvalue_node(expr, expr_ty);
437437
}
438+
439+
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop")
438440
}
439441
}
440442

src/librustc/middle/moves.rs

+2
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ impl VisitContext {
487487
self.consume_block(blk, visitor);
488488
}
489489

490+
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
491+
490492
expr_unary(_, _, lhs) => {
491493
if !self.use_overloaded_operator(
492494
expr, lhs, [], visitor)

src/librustc/middle/resolve.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5016,6 +5016,8 @@ impl Resolver {
50165016
}
50175017
}
50185018

5019+
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
5020+
50195021
expr_break(Some(label)) | expr_again(Some(label)) => {
50205022
match self.search_ribs(self.label_ribs, label, expr.span,
50215023
DontAllowCapturingSelf) {

src/librustc/middle/trans/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2266,7 +2266,7 @@ pub fn register_fn_fuller(ccx: @mut CrateContext,
22662266
sp: span,
22672267
sym: ~str,
22682268
node_id: ast::NodeId,
2269-
node_type: ty::t,
2269+
_node_type: ty::t,
22702270
cc: lib::llvm::CallConv,
22712271
fn_ty: Type)
22722272
-> ValueRef {

src/librustc/middle/trans/type_use.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ pub fn mark_for_expr(cx: &Context, e: &expr) {
401401
expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) |
402402
expr_break(_) | expr_again(_) | expr_unary(*) | expr_lit(_) |
403403
expr_mac(_) | expr_addr_of(*) | expr_ret(_) | expr_loop(*) |
404-
expr_loop_body(_) | expr_do_body(_) => ()
404+
expr_loop_body(_) | expr_do_body(_) => (),
405+
406+
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
405407
}
406408
}
407409

src/librustc/middle/ty.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3240,6 +3240,8 @@ pub fn expr_kind(tcx: ctxt,
32403240
RvalueStmtExpr
32413241
}
32423242

3243+
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
3244+
32433245
ast::expr_lit(_) | // Note: lit_str is carved out above
32443246
ast::expr_unary(*) |
32453247
ast::expr_addr_of(*) |

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

+2
Original file line numberDiff line numberDiff line change
@@ -2559,6 +2559,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
25592559
fcx.write_nil(id);
25602560
}
25612561
}
2562+
ast::expr_for_loop(*) =>
2563+
fail!("non-desugared expr_for_loop"),
25622564
ast::expr_loop(ref body, _) => {
25632565
check_block_no_value(fcx, (body));
25642566
if !may_break(tcx, expr.id, body) {

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

+1
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,7 @@ pub mod guarantor {
10411041
rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr));
10421042
None
10431043
}
1044+
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
10441045
}
10451046
}
10461047

src/libsyntax/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ pub enum expr_ {
465465
expr_cast(@expr, Ty),
466466
expr_if(@expr, Block, Option<@expr>),
467467
expr_while(@expr, Block),
468+
expr_for_loop(@pat, @expr, Block),
468469
/* Conditionless loop (can be exited with break, cont, or ret)
469470
Same semantics as while(true) { body }, but typestate knows that the
470471
(implicit) condition is always true. */

src/libsyntax/ext/expand.rs

+155-1
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ use ast_util::{new_rename, new_mark, resolve};
1616
use attr;
1717
use attr::AttrMetaMethods;
1818
use codemap;
19-
use codemap::{span, ExpnInfo, NameAndSpan};
19+
use codemap::{span, spanned, ExpnInfo, NameAndSpan};
2020
use ext::base::*;
2121
use fold::*;
2222
use parse;
2323
use parse::{parse_item_from_source_str};
24+
use parse::token;
2425
use parse::token::{ident_to_str, intern};
2526
use visit;
2627
use visit::Visitor;
@@ -99,6 +100,159 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
99100
}
100101
}
101102
}
103+
104+
// Desugar expr_for_loop
105+
// From: `foreach <src_pat> in <src_expr> <src_loop_block>`
106+
ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
107+
let src_pat = src_pat.clone();
108+
let src_expr = src_expr.clone();
109+
110+
// Expand any interior macros etc.
111+
// NB: we don't fold pats yet. Curious.
112+
let src_expr = fld.fold_expr(src_expr).clone();
113+
let src_loop_block = fld.fold_block(src_loop_block).clone();
114+
115+
let span = s;
116+
let lo = s.lo;
117+
let hi = s.hi;
118+
119+
pub fn mk_expr(cx: @ExtCtxt, span: span,
120+
node: expr_) -> @ast::expr {
121+
@ast::expr {
122+
id: cx.next_id(),
123+
node: node,
124+
span: span,
125+
}
126+
}
127+
128+
fn mk_block(cx: @ExtCtxt,
129+
stmts: &[@ast::stmt],
130+
expr: Option<@ast::expr>,
131+
span: span) -> ast::Block {
132+
ast::Block {
133+
view_items: ~[],
134+
stmts: stmts.to_owned(),
135+
expr: expr,
136+
id: cx.next_id(),
137+
rules: ast::DefaultBlock,
138+
span: span,
139+
}
140+
}
141+
142+
fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
143+
ast::Path {
144+
span: span,
145+
global: false,
146+
idents: ~[ident],
147+
rp: None,
148+
types: ~[]
149+
}
150+
}
151+
152+
// to:
153+
//
154+
// {
155+
// let _i = &mut <src_expr>;
156+
// loop {
157+
// match i.next() {
158+
// None => break,
159+
// Some(<src_pat>) => <src_loop_block>
160+
// }
161+
// }
162+
// }
163+
164+
let local_ident = token::gensym_ident("i");
165+
let some_ident = token::str_to_ident("Some");
166+
let none_ident = token::str_to_ident("None");
167+
let next_ident = token::str_to_ident("next");
168+
169+
let local_path_1 = mk_simple_path(local_ident, span);
170+
let local_path_2 = mk_simple_path(local_ident, span);
171+
let some_path = mk_simple_path(some_ident, span);
172+
let none_path = mk_simple_path(none_ident, span);
173+
174+
// `let i = &mut <src_expr>`
175+
let iter_decl_stmt = {
176+
let ty = ast::Ty {
177+
id: cx.next_id(),
178+
node: ast::ty_infer,
179+
span: span
180+
};
181+
let local = @ast::Local {
182+
is_mutbl: false,
183+
ty: ty,
184+
pat: @ast::pat {
185+
id: cx.next_id(),
186+
node: ast::pat_ident(ast::bind_infer, local_path_1, None),
187+
span: src_expr.span
188+
},
189+
init: Some(mk_expr(cx, src_expr.span,
190+
ast::expr_addr_of(ast::m_mutbl, src_expr))),
191+
id: cx.next_id(),
192+
span: src_expr.span,
193+
};
194+
let e = @spanned(src_expr.span.lo,
195+
src_expr.span.hi,
196+
ast::decl_local(local));
197+
@spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
198+
};
199+
200+
// `None => break;`
201+
let none_arm = {
202+
let break_expr = mk_expr(cx, span, ast::expr_break(None));
203+
let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
204+
let none_block = mk_block(cx, [break_stmt], None, span);
205+
let none_pat = @ast::pat {
206+
id: cx.next_id(),
207+
node: ast::pat_ident(ast::bind_infer, none_path, None),
208+
span: span
209+
};
210+
ast::arm {
211+
pats: ~[none_pat],
212+
guard: None,
213+
body: none_block
214+
}
215+
};
216+
217+
// `Some(<src_pat>) => <src_loop_block>`
218+
let some_arm = {
219+
let pat = @ast::pat {
220+
id: cx.next_id(),
221+
node: ast::pat_enum(some_path, Some(~[src_pat])),
222+
span: src_pat.span
223+
};
224+
ast::arm {
225+
pats: ~[pat],
226+
guard: None,
227+
body: src_loop_block
228+
}
229+
};
230+
231+
// `match i.next() { ... }`
232+
let match_stmt = {
233+
let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
234+
let next_call_expr = mk_expr(cx, span,
235+
ast::expr_method_call(cx.next_id(),
236+
local_expr, next_ident,
237+
~[], ~[], ast::NoSugar));
238+
let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
239+
~[none_arm, some_arm]));
240+
@spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
241+
};
242+
243+
// `loop { ... }`
244+
let loop_block = {
245+
let loop_body_block = mk_block(cx, [match_stmt], None, span);
246+
let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
247+
let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
248+
mk_block(cx, [iter_decl_stmt,
249+
loop_body_stmt],
250+
None, span)
251+
};
252+
253+
(ast::expr_block(loop_block), span)
254+
}
255+
102256
_ => orig(e, s, fld)
103257
}
104258
}

src/libsyntax/fold.rs

+5
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,11 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ {
559559
expr_while(cond, ref body) => {
560560
expr_while(fld.fold_expr(cond), fld.fold_block(body))
561561
}
562+
expr_for_loop(pat, iter, ref body) => {
563+
expr_for_loop(fld.fold_pat(pat),
564+
fld.fold_expr(iter),
565+
fld.fold_block(body))
566+
}
562567
expr_loop(ref body, opt_ident) => {
563568
expr_loop(
564569
fld.fold_block(body),

src/libsyntax/parse/classify.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
2828
| ast::expr_block(_)
2929
| ast::expr_while(*)
3030
| ast::expr_loop(*)
31+
| ast::expr_for_loop(*)
3132
| ast::expr_call(_, _, ast::DoSugar)
3233
| ast::expr_call(_, _, ast::ForSugar)
3334
| ast::expr_method_call(_, _, _, _, _, ast::DoSugar)

src/libsyntax/parse/parser.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use ast::{expr_method_call, expr_paren, expr_path, expr_repeat};
2929
use ast::{expr_ret, expr_self, expr_struct, expr_tup, expr_unary};
3030
use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
3131
use ast::{expr_vstore_slice, expr_vstore_box};
32-
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, Field, fn_decl};
32+
use ast::{expr_vstore_mut_slice, expr_while, expr_for_loop, extern_fn, Field, fn_decl};
3333
use ast::{expr_vstore_uniq, Onceness, Once, Many};
3434
use ast::{foreign_item, foreign_item_static, foreign_item_fn, foreign_mod};
3535
use ast::{ident, impure_fn, inherited, item, item_, item_static};
@@ -1622,6 +1622,8 @@ impl Parser {
16221622
hi = self.span.hi;
16231623
} else if self.eat_keyword(keywords::If) {
16241624
return self.parse_if_expr();
1625+
} else if self.eat_keyword(keywords::ForEach) {
1626+
return self.parse_for_expr();
16251627
} else if self.eat_keyword(keywords::For) {
16261628
return self.parse_sugary_call_expr(lo, ~"for", ForSugar,
16271629
expr_loop_body);
@@ -2323,6 +2325,21 @@ impl Parser {
23232325
}
23242326
}
23252327

2328+
// parse a 'foreach' .. 'in' expression ('foreach' token already eaten)
2329+
pub fn parse_for_expr(&self) -> @expr {
2330+
// Parse: `foreach <src_pat> in <src_expr> <src_loop_block>`
2331+
2332+
let lo = self.last_span.lo;
2333+
let pat = self.parse_pat();
2334+
self.expect_keyword(keywords::In);
2335+
let expr = self.parse_expr();
2336+
let loop_block = self.parse_block();
2337+
let hi = self.span.hi;
2338+
2339+
self.mk_expr(lo, hi, expr_for_loop(pat, expr, loop_block))
2340+
}
2341+
2342+
23262343
// parse a 'for' or 'do'.
23272344
// the 'for' and 'do' expressions parse as calls, but look like
23282345
// function calls followed by a closure expression.

src/libsyntax/parse/token.rs

+6
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ fn mk_fresh_ident_interner() -> @ident_interner {
474474
"while", // 64
475475

476476
"be", // 65
477+
"in", // 66
478+
"foreach", // 67
477479
];
478480

479481
@ident_interner {
@@ -570,8 +572,10 @@ pub mod keywords {
570572
False,
571573
Fn,
572574
For,
575+
ForEach,
573576
If,
574577
Impl,
578+
In,
575579
Let,
576580
__Log,
577581
Loop,
@@ -612,8 +616,10 @@ pub mod keywords {
612616
False => ident { name: 40, ctxt: 0 },
613617
Fn => ident { name: 41, ctxt: 0 },
614618
For => ident { name: 42, ctxt: 0 },
619+
ForEach => ident { name: 67, ctxt: 0 },
615620
If => ident { name: 43, ctxt: 0 },
616621
Impl => ident { name: 44, ctxt: 0 },
622+
In => ident { name: 66, ctxt: 0 },
617623
Let => ident { name: 45, ctxt: 0 },
618624
__Log => ident { name: 46, ctxt: 0 },
619625
Loop => ident { name: 47, ctxt: 0 },

0 commit comments

Comments
 (0)