about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-07-31 03:58:21 -0700
committerbors <bors@rust-lang.org>2013-07-31 03:58:21 -0700
commit6296dc0d73527301f18ef55b5f2d07c3241b8a00 (patch)
tree9ca1c483e62e852fb33db8aee766ba4c7e798eb9
parent8b7e241e02bb9f82d7b931033afde477d03ff4f2 (diff)
parenta696f0fecb9d11204f64d310eb66e095f64bd04a (diff)
downloadrust-6296dc0d73527301f18ef55b5f2d07c3241b8a00.tar.gz
rust-6296dc0d73527301f18ef55b5f2d07c3241b8a00.zip
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.

-rw-r--r--src/librustc/middle/cfg/construct.rs2
-rw-r--r--src/librustc/middle/dataflow.rs2
-rw-r--r--src/librustc/middle/liveness.rs4
-rw-r--r--src/librustc/middle/mem_categorization.rs2
-rw-r--r--src/librustc/middle/moves.rs2
-rw-r--r--src/librustc/middle/resolve.rs2
-rw-r--r--src/librustc/middle/trans/base.rs2
-rw-r--r--src/librustc/middle/trans/type_use.rs4
-rw-r--r--src/librustc/middle/ty.rs2
-rw-r--r--src/librustc/middle/typeck/check/mod.rs2
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs1
-rw-r--r--src/libsyntax/ast.rs1
-rw-r--r--src/libsyntax/ext/expand.rs156
-rw-r--r--src/libsyntax/fold.rs5
-rw-r--r--src/libsyntax/parse/classify.rs1
-rw-r--r--src/libsyntax/parse/parser.rs19
-rw-r--r--src/libsyntax/parse/token.rs6
-rw-r--r--src/libsyntax/print/pprust.rs9
-rw-r--r--src/libsyntax/visit.rs5
-rw-r--r--src/test/run-pass/foreach-external-iterators-break.rs21
-rw-r--r--src/test/run-pass/foreach-external-iterators-hashmap-break-restart.rs41
-rw-r--r--src/test/run-pass/foreach-external-iterators-hashmap.rs27
-rw-r--r--src/test/run-pass/foreach-external-iterators-loop.rs21
-rw-r--r--src/test/run-pass/foreach-external-iterators-nested.rs23
-rw-r--r--src/test/run-pass/foreach-external-iterators.rs18
25 files changed, 374 insertions, 4 deletions
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index 97fc49b23d0..a262aa5445e 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -239,6 +239,8 @@ impl CFGBuilder {
                 expr_exit
             }
 
+            ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
+
             ast::expr_loop(ref body, _) => {
                 //
                 //     [pred]
diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs
index 1da3b31c936..8bc2c145eb3 100644
--- a/src/librustc/middle/dataflow.rs
+++ b/src/librustc/middle/dataflow.rs
@@ -583,6 +583,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
                 copy_bits(new_loop_scope.break_bits, in_out);
             }
 
+            ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
+
             ast::expr_loop(ref blk, _) => {
                 //
                 //     (expr) <--+
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 385f23a73d6..e595e7c0694 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -503,6 +503,7 @@ fn visit_expr(expr: @expr, (this, vt): (@mut IrMaps, vt<@mut IrMaps>)) {
         this.add_live_node_for_node(expr.id, ExprNode(expr.span));
         visit::visit_expr(expr, (this, vt));
       }
+      expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
       expr_binary(_, op, _, _) if ast_util::lazy_binop(op) => {
         this.add_live_node_for_node(expr.id, ExprNode(expr.span));
         visit::visit_expr(expr, (this, vt));
@@ -1057,6 +1058,8 @@ impl Liveness {
             self.propagate_through_loop(expr, Some(cond), blk, succ)
           }
 
+          expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
+
           // Note that labels have been resolved, so we don't need to look
           // at the label ident
           expr_loop(ref blk, _) => {
@@ -1487,6 +1490,7 @@ fn check_expr(expr: @expr, (this, vt): (@Liveness, vt<@Liveness>)) {
       expr_paren(*) | expr_fn_block(*) | expr_path(*) | expr_self(*) => {
         visit::visit_expr(expr, (this, vt));
       }
+      expr_for_loop(*) => fail!("non-desugared expr_for_loop")
     }
 }
 
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 1c5f3b9bfdf..a875381cc43 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -435,6 +435,8 @@ impl mem_categorization_ctxt {
           ast::expr_inline_asm(*) => {
             return self.cat_rvalue_node(expr, expr_ty);
           }
+
+          ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop")
         }
     }
 
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
index 1cf0162d784..eaa7970cfe4 100644
--- a/src/librustc/middle/moves.rs
+++ b/src/librustc/middle/moves.rs
@@ -487,6 +487,8 @@ impl VisitContext {
                 self.consume_block(blk, visitor);
             }
 
+            expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
+
             expr_unary(_, _, lhs) => {
                 if !self.use_overloaded_operator(
                     expr, lhs, [], visitor)
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 4f7a2f67b3c..ba11476204a 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -5016,6 +5016,8 @@ impl Resolver {
                 }
             }
 
+            expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
+
             expr_break(Some(label)) | expr_again(Some(label)) => {
                 match self.search_ribs(self.label_ribs, label, expr.span,
                                        DontAllowCapturingSelf) {
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 7d7f78379a4..42221cb08b6 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -2266,7 +2266,7 @@ pub fn register_fn_fuller(ccx: @mut CrateContext,
                           sp: span,
                           sym: ~str,
                           node_id: ast::NodeId,
-                          node_type: ty::t,
+                          _node_type: ty::t,
                           cc: lib::llvm::CallConv,
                           fn_ty: Type)
                           -> ValueRef {
diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs
index 3df91844dda..a599c7c798e 100644
--- a/src/librustc/middle/trans/type_use.rs
+++ b/src/librustc/middle/trans/type_use.rs
@@ -401,7 +401,9 @@ pub fn mark_for_expr(cx: &Context, e: &expr) {
       expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) |
       expr_break(_) | expr_again(_) | expr_unary(*) | expr_lit(_) |
       expr_mac(_) | expr_addr_of(*) | expr_ret(_) | expr_loop(*) |
-      expr_loop_body(_) | expr_do_body(_) => ()
+      expr_loop_body(_) | expr_do_body(_) => (),
+
+      expr_for_loop(*) => fail!("non-desugared expr_for_loop")
     }
 }
 
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 050b6d3fdde..3ec66aff176 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -3240,6 +3240,8 @@ pub fn expr_kind(tcx: ctxt,
             RvalueStmtExpr
         }
 
+        ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
+
         ast::expr_lit(_) | // Note: lit_str is carved out above
         ast::expr_unary(*) |
         ast::expr_addr_of(*) |
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 97469937340..4a3f72a2276 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -2559,6 +2559,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
             fcx.write_nil(id);
         }
       }
+      ast::expr_for_loop(*) =>
+          fail!("non-desugared expr_for_loop"),
       ast::expr_loop(ref body, _) => {
         check_block_no_value(fcx, (body));
         if !may_break(tcx, expr.id, body) {
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index 1f4c37a783f..422f59c14ea 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -1041,6 +1041,7 @@ pub mod guarantor {
                     rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr));
                 None
             }
+            ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
         }
     }
 
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 97e69cd6918..d6bee4fe19f 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -465,6 +465,7 @@ pub enum expr_ {
     expr_cast(@expr, Ty),
     expr_if(@expr, Block, Option<@expr>),
     expr_while(@expr, Block),
+    expr_for_loop(@pat, @expr, Block),
     /* Conditionless loop (can be exited with break, cont, or ret)
        Same semantics as while(true) { body }, but typestate knows that the
        (implicit) condition is always true. */
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index af05f726860..72bbc4a96c5 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -16,11 +16,12 @@ use ast_util::{new_rename, new_mark, resolve};
 use attr;
 use attr::AttrMetaMethods;
 use codemap;
-use codemap::{span, ExpnInfo, NameAndSpan};
+use codemap::{span, spanned, ExpnInfo, NameAndSpan};
 use ext::base::*;
 use fold::*;
 use parse;
 use parse::{parse_item_from_source_str};
+use parse::token;
 use parse::token::{ident_to_str, intern};
 use visit;
 use visit::Visitor;
@@ -99,6 +100,159 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
                 }
             }
         }
+
+        // Desugar expr_for_loop
+        // From: `foreach <src_pat> in <src_expr> <src_loop_block>`
+        ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
+            let src_pat = src_pat.clone();
+            let src_expr = src_expr.clone();
+
+            // Expand any interior macros etc.
+            // NB: we don't fold pats yet. Curious.
+            let src_expr = fld.fold_expr(src_expr).clone();
+            let src_loop_block = fld.fold_block(src_loop_block).clone();
+
+            let span = s;
+            let lo = s.lo;
+            let hi = s.hi;
+
+            pub fn mk_expr(cx: @ExtCtxt, span: span,
+                           node: expr_) -> @ast::expr {
+                @ast::expr {
+                    id: cx.next_id(),
+                    node: node,
+                    span: span,
+                }
+            }
+
+            fn mk_block(cx: @ExtCtxt,
+                        stmts: &[@ast::stmt],
+                        expr: Option<@ast::expr>,
+                        span: span) -> ast::Block {
+                ast::Block {
+                    view_items: ~[],
+                    stmts: stmts.to_owned(),
+                    expr: expr,
+                    id: cx.next_id(),
+                    rules: ast::DefaultBlock,
+                    span: span,
+                }
+            }
+
+            fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
+                ast::Path {
+                    span: span,
+                    global: false,
+                    idents: ~[ident],
+                    rp: None,
+                    types: ~[]
+                }
+            }
+
+            // to:
+            //
+            // {
+            //   let _i = &mut <src_expr>;
+            //   loop {
+            //       match i.next() {
+            //           None => break,
+            //           Some(<src_pat>) => <src_loop_block>
+            //       }
+            //   }
+            // }
+
+            let local_ident = token::gensym_ident("i");
+            let some_ident = token::str_to_ident("Some");
+            let none_ident = token::str_to_ident("None");
+            let next_ident = token::str_to_ident("next");
+
+            let local_path_1 = mk_simple_path(local_ident, span);
+            let local_path_2 = mk_simple_path(local_ident, span);
+            let some_path = mk_simple_path(some_ident, span);
+            let none_path = mk_simple_path(none_ident, span);
+
+            // `let i = &mut <src_expr>`
+            let iter_decl_stmt = {
+                let ty = ast::Ty {
+                    id: cx.next_id(),
+                    node: ast::ty_infer,
+                    span: span
+                };
+                let local = @ast::Local {
+                    is_mutbl: false,
+                    ty: ty,
+                    pat: @ast::pat {
+                        id: cx.next_id(),
+                        node: ast::pat_ident(ast::bind_infer, local_path_1, None),
+                        span: src_expr.span
+                    },
+                    init: Some(mk_expr(cx, src_expr.span,
+                                       ast::expr_addr_of(ast::m_mutbl, src_expr))),
+                    id: cx.next_id(),
+                    span: src_expr.span,
+                };
+                let e = @spanned(src_expr.span.lo,
+                                 src_expr.span.hi,
+                                 ast::decl_local(local));
+                @spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
+            };
+
+            // `None => break;`
+            let none_arm = {
+                let break_expr = mk_expr(cx, span, ast::expr_break(None));
+                let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
+                let none_block = mk_block(cx, [break_stmt], None, span);
+                let none_pat = @ast::pat {
+                    id: cx.next_id(),
+                    node: ast::pat_ident(ast::bind_infer, none_path, None),
+                    span: span
+                };
+                ast::arm {
+                    pats: ~[none_pat],
+                    guard: None,
+                    body: none_block
+                }
+            };
+
+            // `Some(<src_pat>) => <src_loop_block>`
+            let some_arm = {
+                let pat = @ast::pat {
+                    id: cx.next_id(),
+                    node: ast::pat_enum(some_path, Some(~[src_pat])),
+                    span: src_pat.span
+                };
+                ast::arm {
+                    pats: ~[pat],
+                    guard: None,
+                    body: src_loop_block
+                }
+            };
+
+            // `match i.next() { ... }`
+            let match_stmt = {
+                let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
+                let next_call_expr = mk_expr(cx, span,
+                                             ast::expr_method_call(cx.next_id(),
+                                                                   local_expr, next_ident,
+                                                                   ~[], ~[], ast::NoSugar));
+                let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
+                                                                   ~[none_arm, some_arm]));
+                @spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
+            };
+
+            // `loop { ... }`
+            let loop_block = {
+                let loop_body_block = mk_block(cx, [match_stmt], None, span);
+                let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
+                let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
+                mk_block(cx, [iter_decl_stmt,
+                              loop_body_stmt],
+                         None, span)
+            };
+
+            (ast::expr_block(loop_block), span)
+        }
+
         _ => orig(e, s, fld)
     }
 }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 51475528174..3016ee660dc 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -559,6 +559,11 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ {
         expr_while(cond, ref body) => {
             expr_while(fld.fold_expr(cond), fld.fold_block(body))
         }
+        expr_for_loop(pat, iter, ref body) => {
+            expr_for_loop(fld.fold_pat(pat),
+                          fld.fold_expr(iter),
+                          fld.fold_block(body))
+        }
         expr_loop(ref body, opt_ident) => {
             expr_loop(
                 fld.fold_block(body),
diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs
index c7c556c9728..0bf87f10597 100644
--- a/src/libsyntax/parse/classify.rs
+++ b/src/libsyntax/parse/classify.rs
@@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
       | ast::expr_block(_)
       | ast::expr_while(*)
       | ast::expr_loop(*)
+      | ast::expr_for_loop(*)
       | ast::expr_call(_, _, ast::DoSugar)
       | ast::expr_call(_, _, ast::ForSugar)
       | ast::expr_method_call(_, _, _, _, _, ast::DoSugar)
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 61dc6f47549..6ff4b91ec97 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -29,7 +29,7 @@ use ast::{expr_method_call, expr_paren, expr_path, expr_repeat};
 use ast::{expr_ret, expr_self, expr_struct, expr_tup, expr_unary};
 use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
 use ast::{expr_vstore_slice, expr_vstore_box};
-use ast::{expr_vstore_mut_slice, expr_while, extern_fn, Field, fn_decl};
+use ast::{expr_vstore_mut_slice, expr_while, expr_for_loop, extern_fn, Field, fn_decl};
 use ast::{expr_vstore_uniq, Onceness, Once, Many};
 use ast::{foreign_item, foreign_item_static, foreign_item_fn, foreign_mod};
 use ast::{ident, impure_fn, inherited, item, item_, item_static};
@@ -1622,6 +1622,8 @@ impl Parser {
             hi = self.span.hi;
         } else if self.eat_keyword(keywords::If) {
             return self.parse_if_expr();
+        } else if self.eat_keyword(keywords::ForEach) {
+            return self.parse_for_expr();
         } else if self.eat_keyword(keywords::For) {
             return self.parse_sugary_call_expr(lo, ~"for", ForSugar,
                                                expr_loop_body);
@@ -2323,6 +2325,21 @@ impl Parser {
         }
     }
 
+    // parse a 'foreach' .. 'in' expression ('foreach' token already eaten)
+    pub fn parse_for_expr(&self) -> @expr {
+        // Parse: `foreach <src_pat> in <src_expr> <src_loop_block>`
+
+        let lo = self.last_span.lo;
+        let pat = self.parse_pat();
+        self.expect_keyword(keywords::In);
+        let expr = self.parse_expr();
+        let loop_block = self.parse_block();
+        let hi = self.span.hi;
+
+        self.mk_expr(lo, hi, expr_for_loop(pat, expr, loop_block))
+    }
+
+
     // parse a 'for' or 'do'.
     // the 'for' and 'do' expressions parse as calls, but look like
     // function calls followed by a closure expression.
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 52b6d4459bf..a9f0db32d3e 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -474,6 +474,8 @@ fn mk_fresh_ident_interner() -> @ident_interner {
         "while",              // 64
 
         "be",                 // 65
+        "in",                 // 66
+        "foreach",            // 67
     ];
 
     @ident_interner {
@@ -570,8 +572,10 @@ pub mod keywords {
         False,
         Fn,
         For,
+        ForEach,
         If,
         Impl,
+        In,
         Let,
         __Log,
         Loop,
@@ -612,8 +616,10 @@ pub mod keywords {
                 False => ident { name: 40, ctxt: 0 },
                 Fn => ident { name: 41, ctxt: 0 },
                 For => ident { name: 42, ctxt: 0 },
+                ForEach => ident { name: 67, ctxt: 0 },
                 If => ident { name: 43, ctxt: 0 },
                 Impl => ident { name: 44, ctxt: 0 },
+                In => ident { name: 66, ctxt: 0 },
                 Let => ident { name: 45, ctxt: 0 },
                 __Log => ident { name: 46, ctxt: 0 },
                 Loop => ident { name: 47, ctxt: 0 },
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 7083d6655f1..4fab3c5779e 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1228,6 +1228,15 @@ pub fn print_expr(s: @ps, expr: &ast::expr) {
         space(s.s);
         print_block(s, blk);
       }
+      ast::expr_for_loop(pat, iter, ref blk) => {
+        head(s, "foreach");
+        print_pat(s, pat);
+        space(s.s);
+        word_space(s, "in");
+        print_expr(s, iter);
+        space(s.s);
+        print_block(s, blk);
+      }
       ast::expr_loop(ref blk, opt_ident) => {
         for opt_ident.iter().advance |ident| {
             word(s.s, "'");
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 1d264bcc20e..990aaa99b78 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -512,6 +512,11 @@ pub fn visit_expr<E:Clone>(ex: @expr, (e, v): (E, vt<E>)) {
             (v.visit_expr)(x, (e.clone(), v));
             (v.visit_block)(b, (e.clone(), v));
         }
+        expr_for_loop(pat, iter, ref b) => {
+            (v.visit_pat)(pat, (e.clone(), v));
+            (v.visit_expr)(iter, (e.clone(), v));
+            (v.visit_block)(b, (e.clone(), v));
+        }
         expr_loop(ref b, _) => (v.visit_block)(b, (e.clone(), v)),
         expr_match(x, ref arms) => {
             (v.visit_expr)(x, (e.clone(), v));
diff --git a/src/test/run-pass/foreach-external-iterators-break.rs b/src/test/run-pass/foreach-external-iterators-break.rs
new file mode 100644
index 00000000000..74ff344b735
--- /dev/null
+++ b/src/test/run-pass/foreach-external-iterators-break.rs
@@ -0,0 +1,21 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let x = [1,..100];
+    let mut y = 0;
+    foreach i in x.iter() {
+        if y > 10 {
+            break;
+        }
+        y += *i;
+    }
+    assert!(y == 11);
+}
diff --git a/src/test/run-pass/foreach-external-iterators-hashmap-break-restart.rs b/src/test/run-pass/foreach-external-iterators-hashmap-break-restart.rs
new file mode 100644
index 00000000000..4b6421cb0d3
--- /dev/null
+++ b/src/test/run-pass/foreach-external-iterators-hashmap-break-restart.rs
@@ -0,0 +1,41 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::hashmap::HashMap;
+
+// This is a fancy one: it uses an external iterator established
+// outside the loop, breaks, then _picks back up_ and continues
+// iterating with it.
+
+fn main() {
+    let mut h = HashMap::new();
+    let kvs = [(1, 10), (2, 20), (3, 30)];
+    foreach &(k,v) in kvs.iter() {
+        h.insert(k,v);
+    }
+    let mut x = 0;
+    let mut y = 0;
+
+    let mut i = h.iter();
+
+    foreach (&k,&v) in i {
+        x += k;
+        y += v;
+        break;
+    }
+
+    foreach (&k,&v) in i {
+        x += k;
+        y += v;
+    }
+
+    assert_eq!(x, 6);
+    assert_eq!(y, 60);
+}
\ No newline at end of file
diff --git a/src/test/run-pass/foreach-external-iterators-hashmap.rs b/src/test/run-pass/foreach-external-iterators-hashmap.rs
new file mode 100644
index 00000000000..bc6508c4a5f
--- /dev/null
+++ b/src/test/run-pass/foreach-external-iterators-hashmap.rs
@@ -0,0 +1,27 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::hashmap::HashMap;
+
+fn main() {
+    let mut h = HashMap::new();
+    let kvs = [(1, 10), (2, 20), (3, 30)];
+    foreach &(k,v) in kvs.iter() {
+        h.insert(k,v);
+    }
+    let mut x = 0;
+    let mut y = 0;
+    foreach (&k,&v) in h.iter() {
+        x += k;
+        y += v;
+    }
+    assert_eq!(x, 6);
+    assert_eq!(y, 60);
+}
\ No newline at end of file
diff --git a/src/test/run-pass/foreach-external-iterators-loop.rs b/src/test/run-pass/foreach-external-iterators-loop.rs
new file mode 100644
index 00000000000..8db6b50f8cd
--- /dev/null
+++ b/src/test/run-pass/foreach-external-iterators-loop.rs
@@ -0,0 +1,21 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let x = [1,..100];
+    let mut y = 0;
+    foreach (n,i) in x.iter().enumerate() {
+        if n < 10 {
+            loop;
+        }
+        y += *i;
+    }
+    assert_eq!(y, 90);
+}
diff --git a/src/test/run-pass/foreach-external-iterators-nested.rs b/src/test/run-pass/foreach-external-iterators-nested.rs
new file mode 100644
index 00000000000..ffc01ebff19
--- /dev/null
+++ b/src/test/run-pass/foreach-external-iterators-nested.rs
@@ -0,0 +1,23 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let x = [1,..100];
+    let y = [2,..100];
+    let mut p = 0;
+    let mut q = 0;
+    foreach i in x.iter() {
+        foreach j in y.iter() {
+            p += *j;
+        }
+        q += *i + p;
+    }
+    assert!(q == 1010100);
+}
diff --git a/src/test/run-pass/foreach-external-iterators.rs b/src/test/run-pass/foreach-external-iterators.rs
new file mode 100644
index 00000000000..55884cee4ec
--- /dev/null
+++ b/src/test/run-pass/foreach-external-iterators.rs
@@ -0,0 +1,18 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let x = [1,..100];
+    let mut y = 0;
+    foreach i in x.iter() {
+        y += *i
+    }
+    assert!(y == 100);
+}
\ No newline at end of file