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:
Patrick Walton 2014-07-21 20:54:28 -07:00
parent 7f2e63ec3f
commit caa564bea3
42 changed files with 614 additions and 163 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -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>;

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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());

View File

@ -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()) {

View File

@ -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 {

View File

@ -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, _) => {
//

View File

@ -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) => {

View File

@ -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 ]);
}
_ => ()
}
}

View File

@ -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

View File

@ -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, []) {

View File

@ -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;
}

View File

@ -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")
}
}

View File

@ -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));
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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(_) => {

View File

@ -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)
}

View File

@ -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(..) |

View File

@ -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),

View File

@ -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()) {

View File

@ -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, ());
}

View File

@ -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 {}",

View File

@ -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);
}
}

View File

@ -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, ())
}
}

View File

@ -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!(),
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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);

View 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);
}
}

View File

@ -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`
}
}

View File

@ -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`
}
}

View File

@ -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

View 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);
}
}