auto merge of #15809 : pcwalton/rust/dedesugar-for, r=pnkfelix
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<int> { ... } } for x in MyStruct { ... } { ... } Change ad-hoc `next` methods like the above to implementations of the `Iterator` trait. For example: impl Iterator<int> for MyStruct { fn next(&mut self) -> Option<int> { ... } } Closes #15392. [breaking-change]
This commit is contained in:
commit
b9035c26e2
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -95,6 +95,7 @@ pub trait Extendable<A>: FromIterator<A> {
|
||||
/// 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<A> {
|
||||
/// Advance the iterator and return the next value. Return `None` when the end is reached.
|
||||
fn next(&mut self) -> Option<A>;
|
||||
|
@ -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<T> {
|
||||
/// No value
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
@ -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()) {
|
||||
|
@ -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 {
|
||||
|
@ -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, _) => {
|
||||
//
|
||||
|
@ -42,6 +42,10 @@ impl<'a> Visitor<Context> 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) => {
|
||||
|
@ -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 ]);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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, []) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Expr>),
|
||||
/// 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<Gc<Expr>>,
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 <pat> in <expr> { ... }`.
|
||||
pub fn store_for_loop_binding<'a>(
|
||||
bcx: &'a Block<'a>,
|
||||
pat: Gc<ast::Pat>,
|
||||
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,
|
||||
|
@ -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<ast::Pat>,
|
||||
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)
|
||||
|
@ -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(_) => {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(..) |
|
||||
|
@ -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;
|
||||
@ -486,7 +487,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),
|
||||
|
@ -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<A>` that the method returns.
|
||||
fn lookup_method_for_for_loop(fcx: &FnCtxt,
|
||||
iterator_expr: Gc<ast::Expr>,
|
||||
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()) {
|
||||
|
@ -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, ());
|
||||
}
|
||||
|
@ -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 {}",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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, ())
|
||||
}
|
||||
}
|
||||
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -62,108 +62,18 @@ fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
|
||||
}
|
||||
}
|
||||
|
||||
// Desugar expr_for_loop
|
||||
// From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
|
||||
// FIXME #6993: change type of opt_ident to Option<Name>
|
||||
ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
|
||||
|
||||
let span = e.span;
|
||||
|
||||
// to:
|
||||
//
|
||||
// match &mut <src_expr> {
|
||||
// i => {
|
||||
// ['<ident>:] loop {
|
||||
// match i.next() {
|
||||
// None => break ['<ident>],
|
||||
// Some(mut value) => {
|
||||
// let <src_pat> = value;
|
||||
// <src_loop_block>
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// (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 ['<ident>],`
|
||||
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 <src_pat> = 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 ...; <src_loop_block> }
|
||||
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 <src_expr> { 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);
|
||||
|
@ -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
|
||||
|
@ -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<Vec<Vec<u64>>>) -> 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);
|
||||
|
31
src/test/compile-fail/for-loop-bogosity.rs
Normal file
31
src/test/compile-fail/for-loop-bogosity.rs
Normal file
@ -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 <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.
|
||||
|
||||
struct MyStruct {
|
||||
x: int,
|
||||
y: int,
|
||||
}
|
||||
|
||||
impl MyStruct {
|
||||
fn next(&mut self) -> Option<int> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,9 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
let mut xs = vec!(1i, 2, 3, 4);
|
||||
let mut xs: Vec<int> = vec!();
|
||||
|
||||
for x in xs.mut_iter() {
|
||||
xs.push(1) //~ ERROR cannot borrow `xs`
|
||||
xs.push(1i) //~ ERROR cannot borrow `xs`
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
24
src/test/run-pass/for-loop-goofiness.rs
Normal file
24
src/test/run-pass/for-loop-goofiness.rs
Normal file
@ -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 <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.
|
||||
|
||||
enum BogusOption<T> {
|
||||
None,
|
||||
Some(T),
|
||||
}
|
||||
|
||||
type Iterator = int;
|
||||
|
||||
pub fn main() {
|
||||
let x = [ 3i, 3, 3 ];
|
||||
for i in x.iter() {
|
||||
assert_eq!(*i, 3);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user