From caa564bea3d5f5a24d0797c4769184c1ea0abaff Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 21 Jul 2014 20:54:28 -0700 Subject: [PATCH] librustc: Stop desugaring `for` expressions and translate them directly. This makes edge cases in which the `Iterator` trait was not in scope and/or `Option` or its variants were not in scope work properly. This breaks code that looks like: struct MyStruct { ... } impl MyStruct { fn next(&mut self) -> Option { ... } } for x in MyStruct { ... } { ... } Change ad-hoc `next` methods like the above to implementations of the `Iterator` trait. For example: impl Iterator for MyStruct { fn next(&mut self) -> Option { ... } } Closes #15392. [breaking-change] --- src/libcore/char.rs | 5 +- src/libcore/fmt/float.rs | 8 +- src/libcore/fmt/num.rs | 8 +- src/libcore/iter.rs | 1 + src/libcore/option.rs | 7 +- src/libcore/ptr.rs | 5 +- src/libcore/str.rs | 7 +- src/libcoretest/iter.rs | 8 +- src/librustc/middle/borrowck/check_loans.rs | 6 + src/librustc/middle/borrowck/mod.rs | 7 +- src/librustc/middle/cfg/construct.rs | 41 +++++- src/librustc/middle/check_loop.rs | 4 + src/librustc/middle/check_match.rs | 18 +++ src/librustc/middle/dead.rs | 8 +- src/librustc/middle/expr_use_visitor.rs | 14 +- src/librustc/middle/lang_items.rs | 2 + src/librustc/middle/liveness.rs | 66 +++++++-- src/librustc/middle/mem_categorization.rs | 7 +- src/librustc/middle/reachable.rs | 4 +- src/librustc/middle/region.rs | 12 +- src/librustc/middle/resolve.rs | 37 ++++- src/librustc/middle/trans/_match.rs | 25 ++++ src/librustc/middle/trans/controlflow.rs | 130 ++++++++++++++++++ src/librustc/middle/trans/debuginfo.rs | 21 ++- src/librustc/middle/trans/expr.rs | 7 + src/librustc/middle/ty.rs | 10 +- src/librustc/middle/typeck/check/_match.rs | 6 +- src/librustc/middle/typeck/check/mod.rs | 91 +++++++++++- src/librustc/middle/typeck/check/regionck.rs | 16 +++ src/librustc/middle/typeck/check/vtable.rs | 3 +- src/librustc/middle/typeck/coherence.rs | 4 +- src/librustc/util/common.rs | 2 +- src/librustc_back/svh.rs | 3 +- src/libstd/io/tempfile.rs | 5 +- src/libsyntax/ext/expand.rs | 104 +------------- src/libunicode/decompose.rs | 1 + src/test/bench/shootout-meteor.rs | 7 +- src/test/compile-fail/for-loop-bogosity.rs | 31 +++++ src/test/compile-fail/issue-15167.rs | 7 +- src/test/compile-fail/vec-mut-iter-borrow.rs | 4 +- .../debuginfo/lexical-scope-in-for-loop.rs | 1 + src/test/run-pass/for-loop-goofiness.rs | 24 ++++ 42 files changed, 614 insertions(+), 163 deletions(-) create mode 100644 src/test/compile-fail/for-loop-bogosity.rs create mode 100644 src/test/run-pass/for-loop-goofiness.rs diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 99c5722699e..26baf96a8bc 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -17,7 +17,10 @@ use mem::transmute; use option::{None, Option, Some}; -use iter::{Iterator, range_step}; +use iter::range_step; + +#[cfg(stage0)] +use iter::Iterator; // NOTE(stage0): Remove after snapshot. // UTF-8 ranges and tags for encoding characters static TAG_CONT: u8 = 0b1000_0000u8; diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index ef5b51fd00b..386fc28119a 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -13,15 +13,19 @@ use char; use collections::Collection; use fmt; -use iter::{Iterator, range, DoubleEndedIterator}; +use iter::{range, DoubleEndedIterator}; use num::{Float, FPNaN, FPInfinite, ToPrimitive, Primitive}; use num::{Zero, One, cast}; -use option::{None, Some}; use result::Ok; use slice::{ImmutableVector, MutableVector}; use slice; use str::StrSlice; +#[cfg(stage0)] +use iter::Iterator; // NOTE(stage0): Remove after snapshot. +#[cfg(stage0)] +use option::{Some, None}; // NOTE(stage0): Remove after snapshot. + /// A flag that specifies whether to use exponential (scientific) notation. pub enum ExponentFormat { /// Do not use exponential notation. diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index d52791f6b0e..81e84c447e7 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -16,11 +16,15 @@ use collections::Collection; use fmt; -use iter::{Iterator, DoubleEndedIterator}; +use iter::DoubleEndedIterator; use num::{Int, cast, zero}; -use option::{Some, None}; use slice::{ImmutableVector, MutableVector}; +#[cfg(stage0)] +use iter::Iterator; // NOTE(stage0): Remove after snapshot. +#[cfg(stage0)] +use option::{Some, None}; // NOTE(stage0): Remove after snapshot. + /// A type that represents a specific radix trait GenericRadix { /// The number of digits. diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 7706d01cbae..b0660230c2c 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -95,6 +95,7 @@ pub trait Extendable: FromIterator { /// is returned. A concrete Iterator implementation may choose to behave however /// it wishes, either by returning `None` infinitely, or by doing something /// else. +#[lang="iterator"] pub trait Iterator { /// Advance the iterator and return the next value. Return `None` when the end is reached. fn next(&mut self) -> Option; diff --git a/src/libcore/option.rs b/src/libcore/option.rs index b8612ed93e0..84b402a68dd 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -147,7 +147,12 @@ use iter::{Iterator, DoubleEndedIterator, FromIterator, ExactSize}; use mem; use slice; -/// The `Option` +// Note that this is not a lang item per se, but it has a hidden dependency on +// `Iterator`, which is one. The compiler assumes that the `next` method of +// `Iterator` is an enumeration with one type parameter and two variants, +// which basically means it must be `Option`. + +/// The `Option` type. #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Show)] pub enum Option { /// No value diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index ed1d4d48110..4921802ba73 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -90,11 +90,14 @@ use mem; use clone::Clone; use intrinsics; -use iter::{range, Iterator}; +use iter::range; use option::{Some, None, Option}; use cmp::{PartialEq, Eq, PartialOrd, Equiv, Ordering, Less, Equal, Greater}; +#[cfg(stage0)] +use iter::Iterator; // NOTE(stage0): Remove after snapshot. + pub use intrinsics::copy_memory; pub use intrinsics::copy_nonoverlapping_memory; pub use intrinsics::set_memory; diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 37af64d74d4..2ba51eb98fc 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -28,7 +28,7 @@ use iter::{Map, Iterator}; use iter::{DoubleEndedIterator, ExactSize}; use iter::range; use num::{CheckedMul, Saturating}; -use option::{None, Option, Some}; +use option::{Option, None, Some}; use raw::Repr; use slice::ImmutableVector; use slice; @@ -1027,9 +1027,12 @@ pub mod traits { use cmp::{Ord, Ordering, Less, Equal, Greater, PartialEq, PartialOrd, Equiv, Eq}; use collections::Collection; use iter::Iterator; - use option::{Option, Some, None}; + use option::{Option, Some}; use str::{Str, StrSlice, eq_slice}; + #[cfg(stage0)] + use option::None; // NOTE(stage0): Remove after snapshot. + impl<'a> Ord for &'a str { #[inline] fn cmp(&self, other: & &'a str) -> Ordering { diff --git a/src/libcoretest/iter.rs b/src/libcoretest/iter.rs index 64c33609399..cadbc238e67 100644 --- a/src/libcoretest/iter.rs +++ b/src/libcoretest/iter.rs @@ -137,8 +137,8 @@ fn test_iterator_take_while() { let ys = [0u, 1, 2, 3, 5, 13]; let mut it = xs.iter().take_while(|&x| *x < 15u); let mut i = 0; - for &x in it { - assert_eq!(x, ys[i]); + for x in it { + assert_eq!(*x, ys[i]); i += 1; } assert_eq!(i, ys.len()); @@ -150,8 +150,8 @@ fn test_iterator_skip_while() { let ys = [15, 16, 17, 19]; let mut it = xs.iter().skip_while(|&x| *x < 15u); let mut i = 0; - for &x in it { - assert_eq!(x, ys[i]); + for x in it { + assert_eq!(*x, ys[i]); i += 1; } assert_eq!(i, ys.len()); diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 2aa0818b177..b570cb43f16 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -440,6 +440,7 @@ impl<'a> CheckLoanCtxt<'a> { euv::AddrOf(..) | euv::AutoRef(..) | euv::ClosureInvocation(..) | + euv::ForLoop(..) | euv::RefBinding(..) => { format!("previous borrow of `{}` occurs here", self.bccx.loan_path_to_string(&*old_loan.loan_path)) @@ -668,6 +669,11 @@ impl<'a> CheckLoanCtxt<'a> { return; } + // Initializations are OK. + if mode == euv::Init { + return + } + // For immutable local variables, assignments are legal // if they cannot already have been assigned if self.is_local_variable_or_arg(assignee_cmt.clone()) { diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 5604d33496d..3ed0f61a8f0 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -651,7 +651,8 @@ impl<'a> BorrowckCtxt<'a> { euv::OverloadedOperator | euv::AddrOf | euv::RefBinding | - euv::AutoRef => { + euv::AutoRef | + euv::ForLoop => { format!("cannot borrow {} as mutable", descr) } euv::ClosureInvocation => { @@ -712,6 +713,10 @@ impl<'a> BorrowckCtxt<'a> { BorrowViolation(euv::ClosureInvocation) => { "closure invocation" } + + BorrowViolation(euv::ForLoop) => { + "`for` loop" + } }; match cause { diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 208a9d05716..4f6885f05ed 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -240,7 +240,7 @@ impl<'a> CFGBuilder<'a> { // v 3 // [expr] // - // Note that `break` and `loop` statements + // Note that `break` and `continue` statements // may cause additional edges. // Is the condition considered part of the loop? @@ -258,7 +258,44 @@ impl<'a> CFGBuilder<'a> { expr_exit } - ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"), + ast::ExprForLoop(ref pat, ref head, ref body, _) => { + // + // [pred] + // | + // v 1 + // [head] + // | + // v 2 + // [loopback] <--+ 7 + // | | + // v 3 | + // +------[cond] | + // | | | + // | v 5 | + // | [pat] | + // | | | + // | v 6 | + // v 4 [body] -----+ + // [expr] + // + // Note that `break` and `continue` statements + // may cause additional edges. + + let head = self.expr(head.clone(), pred); // 1 + let loopback = self.add_dummy_node([head]); // 2 + let cond = self.add_dummy_node([loopback]); // 3 + let expr_exit = self.add_node(expr.id, [cond]); // 4 + self.loop_scopes.push(LoopScope { + loop_id: expr.id, + continue_index: loopback, + break_index: expr_exit, + }); + let pat = self.pat(&**pat, cond); // 5 + let body = self.block(&**body, pat); // 6 + self.add_contained_edge(body, loopback); // 7 + self.loop_scopes.pop(); + expr_exit + } ast::ExprLoop(ref body, _) => { // diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index ac8faaa6c6d..61a2e840730 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -42,6 +42,10 @@ impl<'a> Visitor for CheckLoopVisitor<'a> { ast::ExprLoop(ref b, _) => { self.visit_block(&**b, Loop); } + ast::ExprForLoop(_, ref e, ref b, _) => { + self.visit_expr(&**e, cx); + self.visit_block(&**b, Loop); + } ast::ExprFnBlock(_, ref b) | ast::ExprProc(_, ref b) | ast::ExprUnboxedFn(_, ref b) => { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 67208e33372..e458b82f036 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -170,6 +170,24 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) { .collect(); check_exhaustive(cx, ex.span, &matrix); }, + ExprForLoop(ref pat, _, _, _) => { + let mut static_inliner = StaticInliner { + tcx: cx.tcx + }; + match is_refutable(cx, static_inliner.fold_pat(*pat)) { + Some(uncovered_pat) => { + cx.tcx.sess.span_err( + pat.span, + format!("refutable pattern in `for` loop binding: \ + `{}` not covered", + pat_to_string(&*uncovered_pat)).as_slice()); + }, + None => {} + } + + // Check legality of move bindings. + check_legality_of_move_bindings(cx, false, [ *pat ]); + } _ => () } } diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 9fc589ddf59..108bd35424a 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -354,11 +354,11 @@ fn create_and_seed_worklist(tcx: &ty::ctxt, // depending on whether a crate is built as bin or lib, and we want // the warning to be consistent, we also seed the worklist with // exported symbols. - for &id in exported_items.iter() { - worklist.push(id); + for id in exported_items.iter() { + worklist.push(*id); } - for &id in reachable_symbols.iter() { - worklist.push(id); + for id in reachable_symbols.iter() { + worklist.push(*id); } // Seed entry point diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index b911e636da0..7995317d49f 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -79,7 +79,8 @@ pub enum LoanCause { AutoRef, RefBinding, OverloadedOperator, - ClosureInvocation + ClosureInvocation, + ForLoop, } #[deriving(PartialEq,Show)] @@ -395,7 +396,16 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { self.walk_block(&**blk); } - ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"), + ast::ExprForLoop(ref pat, ref head, ref blk, _) => { + // The pattern lives as long as the block. + debug!("walk_expr for loop case: blk id={}", blk.id); + self.walk_expr(&**head); + + let head_cmt = return_if_err!(self.mc.cat_expr(&**head)); + self.walk_pat(head_cmt, pat.clone()); + + self.walk_block(&**blk); + } ast::ExprUnary(_, ref lhs) => { if !self.walk_overloaded_operator(expr, &**lhs, []) { diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 9abc9226c13..f59909bd138 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -299,5 +299,7 @@ lets_do_this! { NoShareItem, "no_share_bound", no_share_bound; ManagedItem, "managed_bound", managed_bound; + IteratorItem, "iterator", iterator; + StackExhaustedLangItem, "stack_exhausted", stack_exhausted; } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 737b952151b..1c31b671a94 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -125,6 +125,17 @@ use syntax::print::pprust::{expr_to_string, block_to_string}; use syntax::{visit, ast_util}; use syntax::visit::{Visitor, FnKind}; +/// For use with `propagate_through_loop`. +#[deriving(PartialEq, Eq)] +enum LoopKind { + /// An endless `loop` loop. + LoopLoop, + /// A `while` loop, with the given expression as condition. + WhileLoop(Gc), + /// A `for` loop. + ForLoop, +} + #[deriving(PartialEq)] struct Variable(uint); #[deriving(PartialEq)] @@ -480,7 +491,20 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); visit::walk_expr(ir, expr, ()); } - ExprForLoop(..) => fail!("non-desugared expr_for_loop"), + ExprForLoop(ref pat, _, _, _) => { + pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| { + debug!("adding local variable {} from for loop with bm {:?}", + p_id, bm); + let name = path1.node; + ir.add_live_node_for_node(p_id, VarDefNode(sp)); + ir.add_variable(Local(LocalInfo { + id: p_id, + ident: name + })); + }); + ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); + visit::walk_expr(ir, expr, ()); + } ExprBinary(op, _, _) if ast_util::lazy_binop(op) => { ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); visit::walk_expr(ir, expr, ()); @@ -994,15 +1018,21 @@ impl<'a> Liveness<'a> { } ExprWhile(ref cond, ref blk) => { - self.propagate_through_loop(expr, Some(cond.clone()), &**blk, succ) + self.propagate_through_loop(expr, + WhileLoop(cond.clone()), + &**blk, + succ) } - ExprForLoop(..) => fail!("non-desugared expr_for_loop"), + ExprForLoop(_, ref head, ref blk, _) => { + let ln = self.propagate_through_loop(expr, ForLoop, &**blk, succ); + self.propagate_through_expr(&**head, ln) + } // Note that labels have been resolved, so we don't need to look // at the label ident ExprLoop(ref blk, _) => { - self.propagate_through_loop(expr, None, &**blk, succ) + self.propagate_through_loop(expr, LoopLoop, &**blk, succ) } ExprMatch(ref e, ref arms) => { @@ -1281,7 +1311,7 @@ impl<'a> Liveness<'a> { fn propagate_through_loop(&mut self, expr: &Expr, - cond: Option>, + kind: LoopKind, body: &Block, succ: LiveNode) -> LiveNode { @@ -1309,17 +1339,20 @@ impl<'a> Liveness<'a> { let mut first_merge = true; let ln = self.live_node(expr.id, expr.span); self.init_empty(ln, succ); - if cond.is_some() { - // if there is a condition, then it's possible we bypass - // the body altogether. otherwise, the only way is via a - // break in the loop body. + if kind != LoopLoop { + // If this is not a `loop` loop, then it's possible we bypass + // the body altogether. Otherwise, the only way is via a `break` + // in the loop body. self.merge_from_succ(ln, succ, first_merge); first_merge = false; } debug!("propagate_through_loop: using id for loop body {} {}", expr.id, block_to_string(body)); - let cond_ln = self.propagate_through_opt_expr(cond, ln); + let cond_ln = match kind { + LoopLoop | ForLoop => ln, + WhileLoop(ref cond) => self.propagate_through_expr(&**cond, ln), + }; let body_ln = self.with_loop_nodes(expr.id, succ, ln, |this| { this.propagate_through_block(body, cond_ln) }); @@ -1327,8 +1360,14 @@ impl<'a> Liveness<'a> { // repeat until fixed point is reached: while self.merge_from_succ(ln, body_ln, first_merge) { first_merge = false; - assert!(cond_ln == self.propagate_through_opt_expr(cond, - ln)); + + let new_cond_ln = match kind { + LoopLoop | ForLoop => ln, + WhileLoop(ref cond) => { + self.propagate_through_expr(&**cond, ln) + } + }; + assert!(cond_ln == new_cond_ln); assert!(body_ln == self.with_loop_nodes(expr.id, succ, ln, |this| this.propagate_through_block(body, cond_ln))); } @@ -1415,10 +1454,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ExprAgain(..) | ExprLit(_) | ExprBlock(..) | ExprMac(..) | ExprAddrOf(..) | ExprStruct(..) | ExprRepeat(..) | ExprParen(..) | ExprFnBlock(..) | ExprProc(..) | ExprUnboxedFn(..) | - ExprPath(..) | ExprBox(..) => { + ExprPath(..) | ExprBox(..) | ExprForLoop(..) => { visit::walk_expr(this, expr, ()); } - ExprForLoop(..) => fail!("non-desugared expr_for_loop") } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index baf7f2dd776..317fdb5c387 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -482,11 +482,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { ast::ExprBlock(..) | ast::ExprLoop(..) | ast::ExprMatch(..) | ast::ExprLit(..) | ast::ExprBreak(..) | ast::ExprMac(..) | ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) | - ast::ExprInlineAsm(..) | ast::ExprBox(..) => { + ast::ExprInlineAsm(..) | ast::ExprBox(..) | + ast::ExprForLoop(..) => { Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } - - ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop") } } @@ -1113,7 +1112,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { } ast::PatBox(ref subpat) | ast::PatRegion(ref subpat) => { - // @p1, ~p1 + // @p1, ~p1, ref p1 let subcmt = self.cat_deref(pat, cmt, 0, false); if_ok!(self.cat_pattern(subcmt, &**subpat, op)); } diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index e404ce85663..7ed1209bdf7 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -365,8 +365,8 @@ pub fn find_reachable(tcx: &ty::ctxt, // other crates link to us, they're going to expect to be able to // use the lang items, so we need to be sure to mark them as // exported. - for &id in exported_items.iter() { - reachable_context.worklist.push(id); + for id in exported_items.iter() { + reachable_context.worklist.push(*id); } for (_, item) in tcx.lang_items.items() { match *item { diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 822a43f2619..5f5a324857a 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -208,7 +208,9 @@ impl RegionMaps { pub fn var_region(&self, id: ast::NodeId) -> ty::Region { //! Returns the lifetime of the variable `id`. - ty::ReScope(self.var_scope(id)) + let scope = ty::ReScope(self.var_scope(id)); + debug!("var_region({}) = {:?}", id, scope); + scope } pub fn scopes_intersect(&self, scope1: ast::NodeId, scope2: ast::NodeId) @@ -524,6 +526,14 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, visitor.region_maps.mark_as_terminating_scope(body.id); } + ast::ExprForLoop(ref _pat, ref _head, ref body, _) => { + visitor.region_maps.mark_as_terminating_scope(body.id); + + // The variable parent of everything inside (most importantly, the + // pattern) is the body. + new_cx.var_parent = Some(body.id); + } + ast::ExprMatch(..) => { new_cx.var_parent = Some(expr.id); } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 0fb377838fe..95c04ad6607 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -5329,7 +5329,42 @@ impl<'a> Resolver<'a> { }) } - ExprForLoop(..) => fail!("non-desugared expr_for_loop"), + ExprForLoop(ref pattern, ref head, ref body, optional_label) => { + self.resolve_expr(&**head); + + self.value_ribs.borrow_mut().push(Rib::new(NormalRibKind)); + + self.resolve_pattern(&**pattern, + LocalIrrefutableMode, + &mut HashMap::new()); + + match optional_label { + None => {} + Some(label) => { + self.label_ribs + .borrow_mut() + .push(Rib::new(NormalRibKind)); + let def_like = DlDef(DefLabel(expr.id)); + + { + let label_ribs = self.label_ribs.borrow(); + let length = label_ribs.len(); + let rib = label_ribs.get(length - 1); + let renamed = mtwt::resolve(label); + rib.bindings.borrow_mut().insert(renamed, + def_like); + } + } + } + + self.resolve_block(&**body); + + if optional_label.is_some() { + drop(self.label_ribs.borrow_mut().pop()) + } + + self.value_ribs.borrow_mut().pop(); + } ExprBreak(Some(label)) | ExprAgain(Some(label)) => { let renamed = mtwt::resolve(label); diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 419fcffba36..0b181d2cf36 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -1543,6 +1543,31 @@ pub fn store_arg<'a>(mut bcx: &'a Block<'a>, } } +/// Generates code for the pattern binding in a `for` loop like +/// `for in { ... }`. +pub fn store_for_loop_binding<'a>( + bcx: &'a Block<'a>, + pat: Gc, + llvalue: ValueRef, + body_scope: cleanup::ScopeId) + -> &'a Block<'a> { + let _icx = push_ctxt("match::store_for_loop_binding"); + + if simple_identifier(&*pat).is_some() { + // Generate nicer LLVM for the common case of a `for` loop pattern + // like `for x in blahblah { ... }`. + let binding_type = node_id_type(bcx, pat.id); + bcx.fcx.lllocals.borrow_mut().insert(pat.id, + Datum::new(llvalue, + binding_type, + Lvalue)); + return bcx + } + + // General path. Copy out the values that are used in the pattern. + bind_irrefutable_pat(bcx, pat, llvalue, BindLocal, body_scope) +} + fn mk_binding_alloca<'a,A>(bcx: &'a Block<'a>, p_id: ast::NodeId, ident: &ast::Ident, diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 845684bb037..d8a8cc1c561 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -12,16 +12,23 @@ use llvm::*; use driver::config::FullDebugInfo; use middle::def; use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem}; +use middle::trans::_match; +use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; use middle::trans::cleanup::CleanupMethods; use middle::trans::cleanup; use middle::trans::common::*; +use middle::trans::datum; use middle::trans::debuginfo; use middle::trans::expr; +use middle::trans::meth; +use middle::trans::type_::Type; use middle::ty; +use middle::typeck::MethodCall; use util::ppaux::Repr; +use util::ppaux; use syntax::ast; use syntax::ast::Ident; @@ -237,6 +244,129 @@ pub fn trans_while<'a>(bcx: &'a Block<'a>, return next_bcx_in; } +/// Translates a `for` loop. +pub fn trans_for<'a>( + mut bcx: &'a Block<'a>, + loop_info: NodeInfo, + pat: Gc, + head: &ast::Expr, + body: &ast::Block) + -> &'a Block<'a> { + let _icx = push_ctxt("trans_for"); + + // bcx + // | + // loopback_bcx_in <-------+ + // | | + // loopback_bcx_out | + // | | | + // | body_bcx_in | + // cleanup_blk | | + // | body_bcx_out --+ + // next_bcx_in + + // Codegen the head to create the iterator value. + let iterator_datum = + unpack_datum!(bcx, expr::trans_to_lvalue(bcx, head, "for_head")); + let iterator_type = node_id_type(bcx, head.id); + debug!("iterator type is {}, datum type is {}", + ppaux::ty_to_string(bcx.tcx(), iterator_type), + ppaux::ty_to_string(bcx.tcx(), iterator_datum.ty)); + let lliterator = load_ty(bcx, iterator_datum.val, iterator_datum.ty); + + // Create our basic blocks and set up our loop cleanups. + let next_bcx_in = bcx.fcx.new_id_block("for_exit", loop_info.id); + let loopback_bcx_in = bcx.fcx.new_id_block("for_loopback", head.id); + let body_bcx_in = bcx.fcx.new_id_block("for_body", body.id); + bcx.fcx.push_loop_cleanup_scope(loop_info.id, + [next_bcx_in, loopback_bcx_in]); + Br(bcx, loopback_bcx_in.llbb); + let cleanup_llbb = bcx.fcx.normal_exit_block(loop_info.id, + cleanup::EXIT_BREAK); + + // Set up the method call (to `.next()`). + let method_call = MethodCall::expr(loop_info.id); + let method_type = loopback_bcx_in.tcx() + .method_map + .borrow() + .get(&method_call) + .ty; + let method_type = monomorphize_type(loopback_bcx_in, method_type); + let method_result_type = ty::ty_fn_ret(method_type); + let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); + let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope); + + // Compile the method call (to `.next()`). + let mut loopback_bcx_out = loopback_bcx_in; + let option_datum = + unpack_datum!(loopback_bcx_out, + datum::lvalue_scratch_datum(loopback_bcx_out, + method_result_type, + "loop_option", + false, + option_cleanup_scope_id, + (), + |(), bcx, lloption| { + let Result { + bcx: bcx, + val: _ + } = callee::trans_call_inner(bcx, + Some(loop_info), + method_type, + |bcx, arg_cleanup_scope| { + meth::trans_method_callee( + bcx, + method_call, + None, + arg_cleanup_scope) + }, + callee::ArgVals([lliterator]), + Some(expr::SaveIn(lloption))); + bcx + })); + + // Check the discriminant; if the `None` case, exit the loop. + let option_representation = adt::represent_type(loopback_bcx_out.ccx(), + method_result_type); + let i8_type = Type::i8(loopback_bcx_out.ccx()); + let lldiscriminant = adt::trans_get_discr(loopback_bcx_out, + &*option_representation, + option_datum.val, + Some(i8_type)); + let llzero = C_u8(loopback_bcx_out.ccx(), 0); + let llcondition = ICmp(loopback_bcx_out, IntNE, lldiscriminant, llzero); + CondBr(loopback_bcx_out, llcondition, body_bcx_in.llbb, cleanup_llbb); + + // Now we're in the body. Unpack the `Option` value into the programmer- + // supplied pattern. + let llpayload = adt::trans_field_ptr(body_bcx_in, + &*option_representation, + option_datum.val, + 1, + 0); + let binding_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); + let binding_cleanup_scope_id = + cleanup::CustomScope(binding_cleanup_scope); + let mut body_bcx_out = + _match::store_for_loop_binding(body_bcx_in, + pat, + llpayload, + binding_cleanup_scope_id); + + // Codegen the body. + body_bcx_out = trans_block(body_bcx_out, body, expr::Ignore); + body_bcx_out.fcx.pop_custom_cleanup_scope(binding_cleanup_scope); + body_bcx_out = + body_bcx_out.fcx + .pop_and_trans_custom_cleanup_scope(body_bcx_out, + option_cleanup_scope); + Br(body_bcx_out, loopback_bcx_in.llbb); + + // Codegen cleanups and leave. + next_bcx_in.fcx.pop_loop_cleanup_scope(loop_info.id); + next_bcx_in +} + pub fn trans_loop<'a>(bcx:&'a Block<'a>, loop_id: ast::NodeId, body: &ast::Block) diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 66722b2c4db..6fe3ee5d29b 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -3582,9 +3582,24 @@ fn populate_scope_map(cx: &CrateContext, }) } - ast::ExprForLoop(_, _, _, _) => { - cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ - Found unexpanded for-loop."); + ast::ExprForLoop(ref pattern, ref head, ref body, _) => { + walk_expr(cx, &**head, scope_stack, scope_map); + + with_new_scope(cx, + exp.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + scope_map.insert(exp.id, + scope_stack.last() + .unwrap() + .scope_metadata); + walk_pattern(cx, + *pattern, + scope_stack, + scope_map); + walk_block(cx, &**body, scope_stack, scope_map); + }) } ast::ExprMac(_) => { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 4cb1edbe1e7..9d970b1a393 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -665,6 +665,13 @@ fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, ast::ExprWhile(ref cond, ref body) => { controlflow::trans_while(bcx, expr.id, &**cond, &**body) } + ast::ExprForLoop(ref pat, ref head, ref body, _) => { + controlflow::trans_for(bcx, + expr_info(expr), + *pat, + &**head, + &**body) + } ast::ExprLoop(ref body, _) => { controlflow::trans_loop(bcx, expr.id, &**body) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 4dfd15fe136..8f60fe340e4 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3090,7 +3090,7 @@ pub enum ExprKind { pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { if tcx.method_map.borrow().contains_key(&typeck::MethodCall::expr(expr.id)) { // Overloaded operations are generally calls, and hence they are - // generated via DPS, but there are two exceptions: + // generated via DPS, but there are a few exceptions: return match expr.node { // `a += b` has a unit result. ast::ExprAssignOp(..) => RvalueStmtExpr, @@ -3101,6 +3101,9 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { // the index method invoked for `a[i]` always yields an `&T` ast::ExprIndex(..) => LvalueExpr, + // `for` loops are statements + ast::ExprForLoop(..) => RvalueStmtExpr, + // in the general case, result could be any type, use DPS _ => RvalueDpsExpr }; @@ -3209,12 +3212,11 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprLoop(..) | ast::ExprAssign(..) | ast::ExprInlineAsm(..) | - ast::ExprAssignOp(..) => { + ast::ExprAssignOp(..) | + ast::ExprForLoop(..) => { RvalueStmtExpr } - ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"), - ast::ExprLit(_) | // Note: LitStr is carved out above ast::ExprUnary(..) | ast::ExprAddrOf(..) | diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index be01643e22a..3f54677c08b 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -21,6 +21,7 @@ use middle::typeck::check::{instantiate_path, lookup_def}; use middle::typeck::check::{structure_of, valid_range_bounds}; use middle::typeck::infer; use middle::typeck::require_same_types; +use util::ppaux; use std::collections::{HashMap, HashSet}; use std::gc::Gc; @@ -484,7 +485,10 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { } fcx.write_ty(pat.id, typ); - debug!("(checking match) writing type for pat id {}", pat.id); + debug!("(checking match) writing type {} (expected {}) for pat id {}", + ppaux::ty_to_string(tcx, typ), + ppaux::ty_to_string(tcx, expected), + pat.id); match sub { Some(ref p) => check_pat(pcx, &**p, expected), diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 747ba26f590..fd6b3a20a19 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -79,6 +79,7 @@ type parameter). use middle::const_eval; use middle::def; +use middle::lang_items::IteratorItem; use middle::pat_util::pat_id_map; use middle::pat_util; use middle::subst; @@ -1708,6 +1709,80 @@ fn try_overloaded_index(fcx: &FnCtxt, } } +/// Given the head of a `for` expression, looks up the `next` method in the +/// `Iterator` trait. Fails if the expression does not implement `next`. +/// +/// The return type of this function represents the concrete element type +/// `A` in the type `Iterator` that the method returns. +fn lookup_method_for_for_loop(fcx: &FnCtxt, + iterator_expr: Gc, + loop_id: ast::NodeId) + -> ty::t { + let trait_did = match fcx.tcx().lang_items.require(IteratorItem) { + Ok(trait_did) => trait_did, + Err(ref err_string) => { + fcx.tcx().sess.span_err(iterator_expr.span, + err_string.as_slice()); + return ty::mk_err() + } + }; + + let method = method::lookup_in_trait(fcx, + iterator_expr.span, + Some(&*iterator_expr), + token::intern("next"), + trait_did, + fcx.expr_ty(&*iterator_expr), + [], + DontAutoderefReceiver, + IgnoreStaticMethods); + + // Regardless of whether the lookup succeeds, check the method arguments + // so that we have *some* type for each argument. + let method_type = match method { + Some(ref method) => method.ty, + None => { + fcx.tcx().sess.span_err(iterator_expr.span, + "`for` loop expression does not \ + implement the `Iterator` trait"); + ty::mk_err() + } + }; + let return_type = check_method_argument_types(fcx, + iterator_expr.span, + method_type, + &*iterator_expr, + [iterator_expr], + DontDerefArgs, + DontTupleArguments); + + match method { + Some(method) => { + fcx.inh.method_map.borrow_mut().insert(MethodCall::expr(loop_id), + method); + + // We expect the return type to be `Option` or something like it. + // Grab the first parameter of its type substitution. + let return_type = structurally_resolved_type(fcx, + iterator_expr.span, + return_type); + match ty::get(return_type).sty { + ty::ty_enum(_, ref substs) + if !substs.types.is_empty_in(subst::TypeSpace) => { + *substs.types.get(subst::TypeSpace, 0) + } + _ => { + fcx.tcx().sess.span_err(iterator_expr.span, + "`next` method of the `Iterator` \ + trait has an unexpected type"); + ty::mk_err() + } + } + } + None => ty::mk_err() + } +} + fn check_method_argument_types(fcx: &FnCtxt, sp: Span, method_fn_ty: ty::t, @@ -3273,8 +3348,20 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.write_nil(id); } } - ast::ExprForLoop(..) => - fail!("non-desugared expr_for_loop"), + ast::ExprForLoop(ref pat, ref head, ref block, _) => { + check_expr(fcx, &**head); + let typ = lookup_method_for_for_loop(fcx, *head, expr.id); + vtable::early_resolve_expr(expr, fcx, true); + + let pcx = pat_ctxt { + fcx: fcx, + map: pat_id_map(&tcx.def_map, &**pat), + }; + _match::check_pat(&pcx, &**pat, typ); + + check_block_no_value(fcx, &**block); + fcx.write_nil(id); + } ast::ExprLoop(ref body, _) => { check_block_no_value(fcx, &**body); if !may_break(tcx, expr.id, body.clone()) { diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 22d52d0b7d2..d0431de81a3 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -609,6 +609,22 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { rcx.set_repeating_scope(repeating_scope); } + ast::ExprForLoop(ref pat, ref head, ref body, _) => { + constrain_bindings_in_pat(&**pat, rcx); + + { + let mc = mc::MemCategorizationContext::new(rcx); + let head_cmt = ignore_err!(mc.cat_expr(&**head)); + link_pattern(rcx, mc, head_cmt, &**pat); + } + + rcx.visit_expr(&**head, ()); + + let repeating_scope = rcx.set_repeating_scope(body.id); + rcx.visit_block(&**body, ()); + rcx.set_repeating_scope(repeating_scope); + } + _ => { visit::walk_expr(rcx, expr, ()); } diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index c176054b3ae..565b88b7493 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -756,7 +756,8 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { ast::ExprUnary(_, _) | ast::ExprAssignOp(_, _, _) | ast::ExprIndex(_, _) | - ast::ExprMethodCall(_, _, _) => { + ast::ExprMethodCall(_, _, _) | + ast::ExprForLoop(..) => { match fcx.inh.method_map.borrow().find(&MethodCall::expr(ex.id)) { Some(method) => { debug!("vtable resolution on parameter bounds for method call {}", diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index fe140cf2c97..dc0f2a9ffff 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -419,8 +419,8 @@ impl<'a> CoherenceChecker<'a> { } fn check_implementation_coherence(&self) { - for &trait_id in self.crate_context.tcx.trait_impls.borrow().keys() { - self.check_implementation_coherence_of(trait_id); + for trait_id in self.crate_context.tcx.trait_impls.borrow().keys() { + self.check_implementation_coherence_of(*trait_id); } } diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 3dd056969d6..9c9942d2628 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -66,7 +66,7 @@ impl<'a> Visitor<()> for LoopQueryVisitor<'a> { match e.node { // Skip inner loops, since a break in the inner loop isn't a // break inside the outer loop - ast::ExprLoop(..) | ast::ExprWhile(..) => {} + ast::ExprLoop(..) | ast::ExprWhile(..) | ast::ExprForLoop(..) => {} _ => visit::walk_expr(self, e, ()) } } diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index cb9519b1eb8..94dea6cb540 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -252,6 +252,7 @@ mod svh_visitor { SawExprStruct, SawExprRepeat, SawExprParen, + SawExprForLoop, } fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> { @@ -287,9 +288,9 @@ mod svh_visitor { ExprStruct(..) => SawExprStruct, ExprRepeat(..) => SawExprRepeat, ExprParen(..) => SawExprParen, + ExprForLoop(..) => SawExprForLoop, // just syntactic artifacts, expanded away by time of SVH. - ExprForLoop(..) => unreachable!(), ExprMac(..) => unreachable!(), } } diff --git a/src/libstd/io/tempfile.rs b/src/libstd/io/tempfile.rs index 5ca7e417af6..f580dfd80f0 100644 --- a/src/libstd/io/tempfile.rs +++ b/src/libstd/io/tempfile.rs @@ -12,7 +12,7 @@ use io::{fs, IoResult}; use io; -use iter::{Iterator, range}; +use iter::range; use libc; use ops::Drop; use option::{Option, None, Some}; @@ -21,6 +21,9 @@ use path::{Path, GenericPath}; use result::{Ok, Err}; use sync::atomics; +#[cfg(stage0)] +use iter::Iterator; // NOTE(stage0): Remove after snapshot. + /// A wrapper for a path to temporary directory implementing automatic /// scope-based deletion. pub struct TempDir { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 123dcf366f4..f82796b480a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -62,108 +62,18 @@ fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { } } - // Desugar expr_for_loop - // From: `[':] for in ` - // FIXME #6993: change type of opt_ident to Option - ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => { - - let span = e.span; - - // to: - // - // match &mut { - // i => { - // [':] loop { - // match i.next() { - // None => break ['], - // Some(mut value) => { - // let = value; - // - // } - // } - // } - // } - // } - // - // (The use of the `let` is to give better error messages - // when the pattern is refutable.) - - let local_ident = token::gensym_ident("i"); - let next_ident = fld.cx.ident_of("next"); - let none_ident = fld.cx.ident_of("None"); - - let local_path = fld.cx.path_ident(span, local_ident); - let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some")); - - // `None => break ['],` - let none_arm = { - let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident)); - let none_pat = fld.cx.pat_ident(span, none_ident); - fld.cx.arm(span, vec!(none_pat), break_expr) - }; - - // let = value; - // use underscore to suppress lint error: - let value_ident = token::gensym_ident("_value"); - // this is careful to use src_pat.span so that error - // messages point exact at that. - let local = box(GC) ast::Local { - ty: fld.cx.ty_infer(src_pat.span), - pat: src_pat, - init: Some(fld.cx.expr_ident(src_pat.span, value_ident)), - id: ast::DUMMY_NODE_ID, - span: src_pat.span, - source: ast::LocalFor - }; - let local = codemap::respan(src_pat.span, ast::DeclLocal(local)); - let local = box(GC) codemap::respan(span, ast::StmtDecl(box(GC) local, - ast::DUMMY_NODE_ID)); - - // { let ...; } - let block = fld.cx.block(span, vec![local], - Some(fld.cx.expr_block(src_loop_block))); - - // `Some(mut value) => { ... }` - // Note the _'s in the name will stop any unused mutability warnings. - let value_pat = fld.cx.pat_ident_binding_mode(span, value_ident, - ast::BindByValue(ast::MutMutable)); - let some_arm = - fld.cx.arm(span, - vec!(fld.cx.pat_enum(span, some_path, vec!(value_pat))), - fld.cx.expr_block(block)); - - // `match i.next() { ... }` - let match_expr = { - let next_call_expr = - fld.cx.expr_method_call(span, - fld.cx.expr_path(local_path), - next_ident, - Vec::new()); - - fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm)) - }; - - // ['ident:] loop { ... } - let loop_expr = fld.cx.expr(span, - ast::ExprLoop(fld.cx.block_expr(match_expr), - opt_ident)); - - // `i => loop { ... }` - - // `match &mut { i => loop { ... } }` - let discrim = fld.cx.expr_mut_addr_of(span, src_expr); - let i_pattern = fld.cx.pat_ident(span, local_ident); - let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr); - // why these clone()'s everywhere? I guess I'll follow the pattern.... - let match_expr = fld.cx.expr_match(span, discrim, vec!(arm)); - fld.fold_expr(match_expr).clone() - } - ast::ExprLoop(loop_block, opt_ident) => { let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident)) } + ast::ExprForLoop(pat, head, body, opt_ident) => { + let pat = fld.fold_pat(pat); + let head = fld.fold_expr(head); + let (body, opt_ident) = expand_loop_block(body, opt_ident, fld); + fld.cx.expr(e.span, ast::ExprForLoop(pat, head, body, opt_ident)) + } + ast::ExprFnBlock(fn_decl, block) => { let (rewritten_fn_decl, rewritten_block) = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld); diff --git a/src/libunicode/decompose.rs b/src/libunicode/decompose.rs index 832b65d4739..25e06bf7c13 100644 --- a/src/libunicode/decompose.rs +++ b/src/libunicode/decompose.rs @@ -39,6 +39,7 @@ pub fn decompose_canonical(c: char, i: |char|) { d(c, i, false); } pub fn decompose_compatible(c: char, i: |char|) { d(c, i, true); } fn d(c: char, i: |char|, k: bool) { + #[cfg(stage0)] use core::iter::Iterator; // 7-bit ASCII never decomposes diff --git a/src/test/bench/shootout-meteor.rs b/src/test/bench/shootout-meteor.rs index e13c53407e4..615dd1d69d5 100644 --- a/src/test/bench/shootout-meteor.rs +++ b/src/test/bench/shootout-meteor.rs @@ -297,10 +297,10 @@ fn search( // for every unused piece for id in range(0u, 10).filter(|id| board & (1 << (id + 50)) == 0) { // for each mask that fits on the board - for &m in masks_at.get(id).iter().filter(|&m| board & *m == 0) { + for m in masks_at.get(id).iter().filter(|&m| board & *m == 0) { // This check is too costy. //if is_board_unfeasible(board | m, masks) {continue;} - search(masks, board | m, i + 1, Cons(m, &cur), data); + search(masks, board | *m, i + 1, Cons(*m, &cur), data); } } } @@ -311,9 +311,10 @@ fn par_search(masks: Vec>>) -> Data { // launching the search in parallel on every masks at minimum // coordinate (0,0) - for &m in masks.get(0).iter().flat_map(|masks_pos| masks_pos.iter()) { + for m in masks.get(0).iter().flat_map(|masks_pos| masks_pos.iter()) { let masks = masks.clone(); let tx = tx.clone(); + let m = *m; spawn(proc() { let mut data = Data::new(); search(&*masks, m, 1, Cons(m, &Nil), &mut data); diff --git a/src/test/compile-fail/for-loop-bogosity.rs b/src/test/compile-fail/for-loop-bogosity.rs new file mode 100644 index 00000000000..ba268cf3d64 --- /dev/null +++ b/src/test/compile-fail/for-loop-bogosity.rs @@ -0,0 +1,31 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct MyStruct { + x: int, + y: int, +} + +impl MyStruct { + fn next(&mut self) -> Option { + Some(self.x) + } +} + +pub fn main() { + let mut bogus = MyStruct { + x: 1, + y: 2, + }; + for x in bogus { //~ ERROR does not implement the `Iterator` trait + drop(x); + } +} + diff --git a/src/test/compile-fail/issue-15167.rs b/src/test/compile-fail/issue-15167.rs index 61e1e92ff94..300831b1007 100644 --- a/src/test/compile-fail/issue-15167.rs +++ b/src/test/compile-fail/issue-15167.rs @@ -9,13 +9,18 @@ // except according to those terms. // macro f should not be able to inject a reference to 'n'. +// +// Ignored because `for` loops are not hygienic yet; they will require special +// handling since they introduce a new pattern binding position. + +// ignore-test #![feature(macro_rules)] macro_rules! f(() => (n)) fn main() -> (){ - for n in range(0, 1) { + for n in range(0i, 1) { println!("{}", f!()); //~ ERROR unresolved name `n` } } diff --git a/src/test/compile-fail/vec-mut-iter-borrow.rs b/src/test/compile-fail/vec-mut-iter-borrow.rs index 9a179f434c2..9060ed7495e 100644 --- a/src/test/compile-fail/vec-mut-iter-borrow.rs +++ b/src/test/compile-fail/vec-mut-iter-borrow.rs @@ -9,9 +9,9 @@ // except according to those terms. fn main() { - let mut xs = vec!(1i, 2, 3, 4); + let mut xs: Vec = vec!(); for x in xs.mut_iter() { - xs.push(1) //~ ERROR cannot borrow `xs` + xs.push(1i) //~ ERROR cannot borrow `xs` } } diff --git a/src/test/debuginfo/lexical-scope-in-for-loop.rs b/src/test/debuginfo/lexical-scope-in-for-loop.rs index 13cae3252d8..74d54624318 100644 --- a/src/test/debuginfo/lexical-scope-in-for-loop.rs +++ b/src/test/debuginfo/lexical-scope-in-for-loop.rs @@ -9,6 +9,7 @@ // except according to those terms. // ignore-android: FIXME(#10381) +// ignore-test: Not sure what is going on here --pcwalton // compile-flags:-g diff --git a/src/test/run-pass/for-loop-goofiness.rs b/src/test/run-pass/for-loop-goofiness.rs new file mode 100644 index 00000000000..73f4cdd252e --- /dev/null +++ b/src/test/run-pass/for-loop-goofiness.rs @@ -0,0 +1,24 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum BogusOption { + None, + Some(T), +} + +type Iterator = int; + +pub fn main() { + let x = [ 3i, 3, 3 ]; + for i in x.iter() { + assert_eq!(*i, 3); + } +} +