diff --git a/src/main.rs b/src/main.rs index 62b8ccd..5b3695a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,15 +5,12 @@ #![allow(non_shorthand_field_patterns)] #[generator] -fn gen<'a, T>(items: &'a [T]) -> &'a T { - for item in items.iter() { - yield_!(item); - }; +fn gen(item: String) -> String { + yield_!(moved!(item)); } fn main() { - let items = &[1, 2, 3]; - for value in gen(items) { + for value in gen(String::from("wee")) { println!("{}", value); } } diff --git a/src/mar/build/block.rs b/src/mar/build/block.rs index 7feb406..ba73149 100644 --- a/src/mar/build/block.rs +++ b/src/mar/build/block.rs @@ -7,7 +7,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { extent: CodeExtent, block: BasicBlock, ast_block: &ast::Block) -> BasicBlock { - self.in_scope(extent, block, |this| { + self.in_scope(extent, ast_block.span, block, |this| { // FIXME: handle trailing exprs this.stmts(extent, block, &ast_block.stmts[..]) }) diff --git a/src/mar/build/cfg.rs b/src/mar/build/cfg.rs index 83818c7..d5b507a 100644 --- a/src/mar/build/cfg.rs +++ b/src/mar/build/cfg.rs @@ -13,10 +13,11 @@ impl CFG { } pub fn start_new_block(&mut self, + span: Span, name: Option<&'static str>, decls: Vec<(VarDecl, ast::Ident)>) -> BasicBlock { let node_index = self.basic_blocks.len(); - self.basic_blocks.push(BasicBlockData::new(name, decls, None)); + self.basic_blocks.push(BasicBlockData::new(span, name, decls, None)); BasicBlock::new(node_index) } @@ -37,11 +38,16 @@ impl CFG { }); } - pub fn terminate(&mut self, block: BasicBlock, kind: TerminatorKind) { + pub fn terminate(&mut self, + span: Span, + block: BasicBlock, + kind: TerminatorKind) { assert!(self.block_data(block).terminator.is_none(), "terminate: block {:?} already has a terminator set", block); + let block_data = self.block_data_mut(block); block_data.terminator = Some(Terminator { + span: span, kind: kind, }); } diff --git a/src/mar/build/expr.rs b/src/mar/build/expr.rs index ef68eb9..13dbafb 100644 --- a/src/mar/build/expr.rs +++ b/src/mar/build/expr.rs @@ -11,10 +11,11 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { extent: CodeExtent, block: BasicBlock, expr: &P) -> BasicBlock { + let expr = self.expand_moved(expr); // There's no reason for us to transform expressions if they don't contain any transitions. - if !self.contains_transition(expr) { - return self.into(extent, block, expr.clone()); + if !self.contains_transition(&expr) { + return self.into(extent, block, expr); } match expr.node { @@ -38,15 +39,15 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { } ExprKind::Ret(None) => { self.exit_scope(expr.span, extent, block, END_BLOCK); - self.start_new_block(Some("AfterReturn")) + self.start_new_block(expr.span, Some("AfterReturn")) } ExprKind::If(ref cond_expr, ref then_expr, ref else_expr) => { // FIXME: This does not handle the `cond_expr` containing a transition yet. - let mut then_block = self.start_new_block(Some("Then")); - let mut else_block = self.start_new_block(Some("Else")); + let mut then_block = self.start_new_block(expr.span, Some("Then")); + let mut else_block = self.start_new_block(expr.span, Some("Else")); - self.terminate(block, TerminatorKind::If { + self.terminate(expr.span, block, TerminatorKind::If { cond: cond_expr.clone(), targets: (then_block, else_block), }); @@ -54,14 +55,26 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { then_block = self.into(extent, then_block, then_expr); else_block = self.into(extent, else_block, else_expr); - let join_block = self.start_new_block(Some("IfJoin")); - self.terminate(then_block, TerminatorKind::Goto { target: join_block }); - self.terminate(else_block, TerminatorKind::Goto { target: join_block }); + let join_block = self.start_new_block(expr.span, Some("IfJoin")); + + self.terminate( + then_expr.span, + then_block, + TerminatorKind::Goto { target: join_block }); + + self.terminate( + match *else_expr { + Some(ref expr) => expr.span, + None => expr.span, + }, + else_block, + TerminatorKind::Goto { target: join_block }); join_block } ExprKind::Match(ref discriminant, ref arms) => { self.match_expr(extent, expr.span, block, discriminant.clone(), &arms) + } ExprKind::Loop(ref body, label) => { self.expr_loop(extent, block, None, body, label) @@ -81,7 +94,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { // } // } // } - let builder = AstBuilder::new(); + let builder = AstBuilder::new().span(expr.span); // ::std::iter::IntoIterator::into_iter($expr) let into_iter = builder.expr().call() @@ -161,7 +174,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { // $pat => $then_block, // _ => $else_block, // } - let builder = AstBuilder::new(); + let builder = AstBuilder::new().span(expr.span); // $then_pat => $then_block let then_arm = builder.arm() @@ -195,7 +208,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { // _ => break, // } // } - let builder = AstBuilder::new(); + let builder = AstBuilder::new().span(expr.span); // $pat => $then_block let then_arm = builder.arm() @@ -257,11 +270,14 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { // | | // +--------------------------+ - let loop_block = self.start_new_block(Some("Loop")); - let exit_block = self.start_new_block(Some("LoopExit")); + let loop_block = self.start_new_block(body.span, Some("Loop")); + let exit_block = self.start_new_block(body.span, Some("LoopExit")); // start the loop - self.terminate(block, TerminatorKind::Goto { target: loop_block }); + self.terminate( + body.span, + block, + TerminatorKind::Goto { target: loop_block }); self.in_loop_scope(extent, label, loop_block, exit_block, |this| { // conduct the test, if necessary @@ -269,19 +285,25 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { if let Some(cond_expr) = condition { // FIXME: This does not yet handle the expr having a transition. - body_block = this.start_new_block(Some("LoopBody")); + body_block = this.start_new_block(cond_expr.span, Some("LoopBody")); - this.terminate(loop_block, TerminatorKind::If { - cond: cond_expr.clone(), - targets: (body_block, exit_block), - }); + this.terminate( + cond_expr.span, + loop_block, + TerminatorKind::If { + cond: cond_expr.clone(), + targets: (body_block, exit_block), + }); } else { body_block = loop_block; } // execute the body, branching back to the test let body_block_end = this.into(extent, body_block, body); - this.terminate(body_block_end, TerminatorKind::Goto { target: loop_block }); + this.terminate( + body.span, + body_block_end, + TerminatorKind::Goto { target: loop_block }); // final point is exit_block exit_block @@ -303,6 +325,6 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { // Even though we've exited `block`, there could be code following the break/continue. To // keep rust happy, we'll create a new block that has an edge to `block`, even though // control will never actually flow into this block. - self.start_new_block(Some("AfterBreakOrContinue")) + self.start_new_block(span, Some("AfterBreakOrContinue")) } } diff --git a/src/mar/build/mac.rs b/src/mar/build/mac.rs index b8ac00c..afd840b 100644 --- a/src/mar/build/mac.rs +++ b/src/mar/build/mac.rs @@ -23,9 +23,11 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { let (expr, idents) = parse_mac_yield(self.cx, mac); assert!(idents.is_empty()); - let next_block = self.start_new_block(Some("AfterYield")); + let expr = self.expand_moved(&expr); - self.terminate(block, TerminatorKind::Yield { + let next_block = self.start_new_block(mac.span, Some("AfterYield")); + + self.terminate(mac.span, block, TerminatorKind::Yield { expr: expr.clone(), target: next_block, }); diff --git a/src/mar/build/matches.rs b/src/mar/build/matches.rs index 16c81a6..258a5fe 100644 --- a/src/mar/build/matches.rs +++ b/src/mar/build/matches.rs @@ -17,26 +17,25 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { Arm { pats: arm.pats.clone(), guard: arm.guard.clone(), - block: self.start_new_block(Some("Arm")), + block: self.start_new_block(span, Some("Arm")), } }) .collect::>(); - let join_block = self.start_new_block(Some("MatchJoin")); + let join_block = self.start_new_block(span, Some("MatchJoin")); for (arm, target) in arms.iter().zip(targets.iter()) { - let arm_block = self.in_scope(extent, block, |this| { - this.add_decls_from_pats(span, - extent, + let arm_block = self.in_scope(extent, span, block, |this| { + this.add_decls_from_pats(extent, target.block, arm.pats.iter()); this.expr(extent, target.block, &arm.body) }); - self.terminate(arm_block, TerminatorKind::Goto { target: join_block }); + self.terminate(span, arm_block, TerminatorKind::Goto { target: join_block }); } - self.terminate(block, TerminatorKind::Match { + self.terminate(span, block, TerminatorKind::Match { discr: discriminant.clone(), targets: targets, }); diff --git a/src/mar/build/mod.rs b/src/mar/build/mod.rs index 104465c..94b8db8 100644 --- a/src/mar/build/mod.rs +++ b/src/mar/build/mod.rs @@ -1,5 +1,6 @@ use mar::repr::*; use syntax::ast::{self, ItemKind}; +use syntax::codemap::Span; use syntax::ext::base::ExtCtxt; use syntax::fold; use syntax::ptr::P; @@ -50,8 +51,8 @@ pub fn construct(cx: &ExtCtxt, item: P) -> Result { let extent = builder.start_new_extent(); - assert_eq!(builder.start_new_block(Some("Start")), START_BLOCK); - assert_eq!(builder.start_new_block(Some("End")), END_BLOCK); + assert_eq!(builder.start_new_block(item.span, Some("Start")), START_BLOCK); + assert_eq!(builder.start_new_block(item.span, Some("End")), END_BLOCK); let mut block = START_BLOCK; @@ -59,7 +60,6 @@ pub fn construct(cx: &ExtCtxt, item: P) -> Result { // Register the arguments as declarations. builder.add_decls_from_pats( - item.span, extent, block, fn_decl.inputs.iter().map(|arg| &arg.pat)); @@ -70,8 +70,8 @@ pub fn construct(cx: &ExtCtxt, item: P) -> Result { builder.pop_scope(extent, block); - builder.terminate(block, TerminatorKind::Goto { target: END_BLOCK }); - builder.terminate(END_BLOCK, TerminatorKind::Return); + builder.terminate(item.span, block, TerminatorKind::Goto { target: END_BLOCK }); + builder.terminate(item.span, END_BLOCK, TerminatorKind::Return); // The drops seem redundant, we are always moving values. for bb in builder.cfg.basic_blocks.iter_mut() { @@ -129,9 +129,9 @@ fn assign_node_ids(item: P) -> P { } impl<'a, 'b: 'a> Builder<'a, 'b> { - pub fn start_new_block(&mut self, name: Option<&'static str>) -> BasicBlock { + pub fn start_new_block(&mut self, span: Span, name: Option<&'static str>) -> BasicBlock { let decls = self.find_live_decls(); - self.cfg.start_new_block(name, decls) + self.cfg.start_new_block(span, name, decls) } pub fn start_new_extent(&mut self) -> CodeExtent { @@ -156,6 +156,7 @@ mod expr; mod into; mod mac; mod matches; +mod moved; mod scope; mod stmt; mod transition; diff --git a/src/mar/build/moved.rs b/src/mar/build/moved.rs new file mode 100644 index 0000000..4ca949c --- /dev/null +++ b/src/mar/build/moved.rs @@ -0,0 +1,86 @@ +use aster::AstBuilder; +use mar::build::Builder; +use syntax::ast; +use syntax::ext::base::ExtCtxt; +use syntax::ext::tt::transcribe::new_tt_reader; +use syntax::fold::{self, Folder}; +use syntax::parse::parser::Parser; +use syntax::ptr::P; + +impl<'a, 'b: 'a> Builder<'a, 'b> { + pub fn expand_moved(&mut self, expr: &P) -> P { + let mut expander = ExpandMac { + cx: self.cx, + moved_idents: vec![], + }; + + let expr = expander.fold_expr(expr.clone()); + + for moved_ident in expander.moved_idents { + if let Some(decl) = self.find_decl(moved_ident) { + self.schedule_move(decl); + } else { + self.cx.span_bug( + expr.span, + &format!("ident {:?} not in scope", moved_ident)); + } + } + + expr + } +} + +struct ExpandMac<'a, 'b: 'a> { + cx: &'a ExtCtxt<'b>, + moved_idents: Vec, +} + +impl<'a, 'b> ExpandMac<'a, 'b> { + fn parse_mac_moved(&mut self, mac: &ast::Mac) -> P { + let rdr = new_tt_reader( + &self.cx.parse_sess().span_diagnostic, + None, + None, + mac.node.tts.clone()); + + let mut parser = Parser::new( + self.cx.parse_sess(), + self.cx.cfg(), + Box::new(rdr)); + + let ident = panictry!(parser.parse_ident()); + self.moved_idents.push(ident); + + let expr = AstBuilder::new().expr() + .span(mac.span) + .id(ident); + + expr + } +} + +impl<'a, 'b> Folder for ExpandMac<'a, 'b> { + fn fold_expr(&mut self, expr: P) -> P { + match expr.node { + ast::ExprKind::Mac(ref mac) if is_moved_path(&mac.node.path) => { + return self.parse_mac_moved(&mac); + } + _ => {} + } + + expr.map(|expr| fold::noop_fold_expr(expr, self)) + } + + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + fold::noop_fold_mac(mac, self) + } +} + +fn is_moved_path(path: &ast::Path) -> bool { + let builder = AstBuilder::new(); + let yield_ = builder.path() + .id("moved") + .build(); + + !path.global && path.segments == yield_.segments +} diff --git a/src/mar/build/scope.rs b/src/mar/build/scope.rs index af6901e..0cb6a3c 100644 --- a/src/mar/build/scope.rs +++ b/src/mar/build/scope.rs @@ -28,6 +28,7 @@ use syntax::visit; pub struct Scope { extent: CodeExtent, drops: Vec<(Span, VarDecl, Option)>, + moved_decls: HashSet, } #[derive(Clone)] @@ -65,6 +66,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { /// to build its contents, popping the scope afterwards. pub fn in_scope(&mut self, extent: CodeExtent, + span: Span, mut block: BasicBlock, f: F) -> BasicBlock where F: FnOnce(&mut Builder) -> BasicBlock @@ -75,8 +77,8 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { // At this point, we can't tell that variables are not being accessed. So we'll create a // new block to make sure variables are properly not referenced. - let end_scope_block = self.start_new_block(Some("EndScope")); - self.terminate(block, TerminatorKind::Goto { target: end_scope_block }); + let end_scope_block = self.start_new_block(span, Some("EndScope")); + self.terminate(span, block, TerminatorKind::Goto { target: end_scope_block }); end_scope_block } @@ -88,6 +90,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { self.scopes.push(Scope { extent: extent, drops: vec![], + moved_decls: HashSet::new(), }); } @@ -95,6 +98,8 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { /// drops onto the end of `block` that are needed. This must /// match 1-to-1 with `push_scope`. pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) { + debug!("pop_scope"); + let scope = self.scopes.pop().unwrap(); assert_eq!(scope.extent, extent); @@ -162,7 +167,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { } } - self.terminate(block, TerminatorKind::Goto { target: target }); + self.terminate(span, block, TerminatorKind::Goto { target: target }); } pub fn find_decl(&self, lvalue: ast::Ident) -> Option { @@ -179,8 +184,11 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { None } - pub fn terminate(&mut self, block: BasicBlock, kind: TerminatorKind) { - self.cfg.terminate(block, kind) + pub fn terminate(&mut self, + span: Span, + block: BasicBlock, + kind: TerminatorKind) { + self.cfg.terminate(span, block, kind) } /// This function constructs a vector of all of the variables in scope, and returns if the @@ -189,28 +197,37 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { let mut decls = vec![]; let mut visited_decls = HashSet::new(); + debug!("find_live_decls: scopes: {:?}", self.scopes); + // We build up the list of declarations by walking up the scopes, and walking through each // scope backwards. If this is the first time we've seen a variable with this name, we just // add it to our list. However, if we've seen it before, then we need to rename it so that // it'll be accessible once the shading variable goes out of scope. for scope in self.scopes.iter().rev() { - for &(_, decl, _) in scope.drops.iter().rev() { - let ident = self.cfg.var_decl_data(decl).ident; + debug!("find_live_decls: scope: {:?}", scope.moved_decls); - if visited_decls.insert(ident) { - // We haven't seen this decl before, so keep it's name. - decls.push((decl, ident)); + for &(_, decl, _) in scope.drops.iter().rev() { + if scope.moved_decls.contains(&decl) { + debug!("find_live_decls: decl moved {:?}", decl); } else { - // Otherwise, we need to rename it. - let shadowed_ident = AstBuilder::new().id( - format!("{}_shadowed_{}", ident, decl.index())); + let ident = self.cfg.var_decl_data(decl).ident; + + if visited_decls.insert(ident) { + // We haven't seen this decl before, so keep it's name. + decls.push((decl, ident)); + } else { + // Otherwise, we need to rename it. + let shadowed_ident = AstBuilder::new().id( + format!("{}_shadowed_{}", ident, decl.index())); - decls.push((decl, shadowed_ident)); + decls.push((decl, shadowed_ident)); + } } } } decls.reverse(); + debug!("find_live_decls: live decls: {:?}", decls); decls } @@ -231,15 +248,20 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { &format!("extent {:?} not in scope to drop {:?}", extent, decl)); } + pub fn schedule_move(&mut self, decl: VarDecl) { + for scope in self.scopes.iter_mut().rev() { + scope.moved_decls.insert(decl); + } + } + pub fn add_decls_from_pats<'c, I>(&mut self, - span: Span, extent: CodeExtent, block: BasicBlock, pats: I) where I: Iterator>, { for pat in pats { - let decls = self.add_decls_from_pat(span, extent, pat); + let decls = self.add_decls_from_pat(pat.span, extent, pat); self.cfg.block_data_mut(block).decls.extend(decls); } } diff --git a/src/mar/build/stmt.rs b/src/mar/build/stmt.rs index 89bda1f..3f6743d 100644 --- a/src/mar/build/stmt.rs +++ b/src/mar/build/stmt.rs @@ -124,7 +124,7 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { decl: VarDecl) -> Alias { let lvalue = self.cfg.var_decl_data(decl).ident; - let ast_builder = AstBuilder::new(); + let ast_builder = AstBuilder::new().span(span); let alias = ast_builder.id(format!("{}_shadowed_{}", lvalue, decl.index())); self.cfg.push(block, Statement::Let { diff --git a/src/mar/build/transition.rs b/src/mar/build/transition.rs index c1991ba..f77b868 100644 --- a/src/mar/build/transition.rs +++ b/src/mar/build/transition.rs @@ -106,11 +106,7 @@ impl visit::Visitor for ContainsTransitionVisitor { } pub fn is_transition_path(path: &ast::Path) -> bool { - if is_yield_path(path) { - true - } else { - false - } + is_yield_path(path) } pub fn is_yield_path(path: &ast::Path) -> bool { diff --git a/src/mar/repr.rs b/src/mar/repr.rs index 71b1a2c..37537bd 100644 --- a/src/mar/repr.rs +++ b/src/mar/repr.rs @@ -127,6 +127,7 @@ impl fmt::Debug for BasicBlock { #[derive(Debug)] pub struct BasicBlockData { + pub span: Span, pub name: Option<&'static str>, pub decls: Vec<(VarDecl, ast::Ident)>, pub statements: Vec, @@ -134,10 +135,12 @@ pub struct BasicBlockData { } impl BasicBlockData { - pub fn new(name: Option<&'static str>, + pub fn new(span: Span, + name: Option<&'static str>, decls: Vec<(VarDecl, ast::Ident)>, terminator: Option) -> Self { BasicBlockData { + span: span, name: name, decls: decls, statements: vec![], @@ -168,6 +171,7 @@ impl BasicBlockData { #[derive(Debug)] pub struct Terminator { + pub span: Span, pub kind: TerminatorKind, } @@ -256,6 +260,16 @@ pub enum Statement { }, } +impl Statement { + pub fn span(&self) -> Span { + match *self { + Statement::Expr(ref stmt) => stmt.span, + Statement::Let { span, .. } => span, + Statement::Drop { span, .. } => span, + } + } +} + #[derive(Clone, Copy, Debug)] pub struct Alias { pub lvalue: ast::Ident, diff --git a/src/mar/translate/block.rs b/src/mar/translate/block.rs index 03142f4..a7f96b5 100644 --- a/src/mar/translate/block.rs +++ b/src/mar/translate/block.rs @@ -1,6 +1,7 @@ use mar::repr::*; use mar::translate::Builder; use syntax::ast; +use syntax::codemap::Span; impl<'a, 'b: 'a> Builder<'a, 'b> { pub fn block(&self, block: BasicBlock) -> Vec { @@ -19,21 +20,27 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { } fn terminator(&self, terminator: &Terminator) -> Vec { + let ast_builder = self.ast_builder.span(terminator.span); + match terminator.kind { TerminatorKind::Goto { target } => { - self.goto(target) + self.goto(terminator.span, target) } TerminatorKind::If { ref cond, targets: (then_block, else_block) } => { - let then_block = self.ast_builder.block() - .with_stmts(self.goto(then_block)) + let then_block = ast_builder + .span(self.block_span(then_block)) + .block() + .with_stmts(self.goto(terminator.span, then_block)) .build(); - let else_block = self.ast_builder.block() - .with_stmts(self.goto(else_block)) + let else_block = ast_builder + .span(self.block_span(else_block)) + .block() + .with_stmts(self.goto(terminator.span, else_block)) .build(); vec![ - self.ast_builder.stmt().expr().if_() + ast_builder.stmt().expr().if_() .build(cond.clone()) .build_then(then_block) .build_else(else_block), @@ -42,55 +49,76 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { TerminatorKind::Match { ref discr, ref targets } => { let arms = targets.iter() .map(|target| { - let block = self.ast_builder.block() - .with_stmts(self.goto(target.block)) + let ast_builder = ast_builder.span(self.block_span(target.block)); + + let block = ast_builder.block() + .span(self.mar.span) + .with_stmts(self.goto(terminator.span, target.block)) .build(); - self.ast_builder.arm() + ast_builder.arm() .with_pats(target.pats.iter().cloned()) .with_guard(target.guard.clone()) .body().build_block(block) }); vec![ - self.ast_builder.stmt().expr().match_() + ast_builder.stmt().expr().match_() .build(discr.clone()) .with_arms(arms) .build() ] } TerminatorKind::Yield { ref expr, target } => { - let next_state = self.state_expr(target); + let ast_builder = ast_builder.span(expr.span); + let yielded_expr = ast_builder.expr() + .some() + .build(expr.clone()); + let next_state = self.state_expr(terminator.span, target); + + let tuple = ast_builder.expr().tuple() + .expr().build(yielded_expr) + .expr().build(next_state) + .build(); vec![ - self.ast_builder.stmt().semi().return_expr() - .tuple() - .expr().some().build(expr.clone()) - .expr().build(next_state) - .build() + ast_builder.stmt().semi() + .return_expr() + .build(tuple) ] } TerminatorKind::Return => { - let next_state = self.state_expr(END_BLOCK); + let return_expr = ast_builder.expr().none(); + let next_state = self.state_expr(terminator.span, END_BLOCK); + let tuple = ast_builder.expr().tuple() + .expr().build(return_expr) + .expr().build(next_state) + .build(); vec![ - self.ast_builder.stmt().semi().return_expr().tuple() - .expr().none() - .expr().build(next_state) - .build() + ast_builder.stmt().semi() + .return_expr() + .build(tuple) ] } } } - fn goto(&self, target: BasicBlock) -> Vec { - let next_state = self.state_expr(target); + fn goto(&self, span: Span, target: BasicBlock) -> Vec { + let ast_builder = self.ast_builder.span(span); + let next_state = self.state_expr(span, target); + let next_expr = ast_builder.expr() + .assign() + .id("state") + .build(next_state); vec![ - self.ast_builder.stmt().expr().assign() - .id("state") - .build(next_state), - self.ast_builder.stmt().expr().continue_(), + ast_builder.stmt().expr().build(next_expr), + ast_builder.stmt().expr().continue_(), ] } + + pub fn block_span(&self, block: BasicBlock) -> Span { + self.mar.basic_block_data(block).span + } } diff --git a/src/mar/translate/mod.rs b/src/mar/translate/mod.rs index b2b1fcd..3eedf78 100644 --- a/src/mar/translate/mod.rs +++ b/src/mar/translate/mod.rs @@ -6,7 +6,7 @@ use syntax::fold; use syntax::ptr::P; pub fn translate(cx: &ExtCtxt, mar: &Mar) -> Option> { - let ast_builder = AstBuilder::new(); + let ast_builder = AstBuilder::new().span(mar.span); let item_builder = ast_builder.item().fn_(mar.ident) .with_args(mar.fn_decl.inputs.iter().cloned()); @@ -14,25 +14,25 @@ pub fn translate(cx: &ExtCtxt, mar: &Mar) -> Option> { let generics = &mar.generics; let item_builder = match mar.fn_decl.output { - FunctionRetTy::None(_) => item_builder.no_return(), - FunctionRetTy::Default(_) => { - let iter_ty = ast_builder.ty().object_sum() + FunctionRetTy::None(span) => item_builder.span(span).no_return(), + FunctionRetTy::Default(span) => { + let iter_ty = ast_builder.span(span).ty().object_sum() .iterator().unit() .with_generics(generics.clone()) .build(); - let ty = ast_builder.ty().box_() + let ty = ast_builder.span(span).ty().box_() .build(iter_ty); item_builder.build_return(ty) } FunctionRetTy::Ty(ref ty) => { - let iter_ty = ast_builder.ty().object_sum() + let iter_ty = ast_builder.span(ty.span).ty().object_sum() .iterator().build(ty.clone()) .with_generics(generics.clone()) .build(); - let ty = ast_builder.ty().box_() + let ty = ast_builder.span(ty.span).ty().box_() .build(iter_ty); item_builder.build_return(ty) @@ -48,7 +48,7 @@ pub fn translate(cx: &ExtCtxt, mar: &Mar) -> Option> { mar: mar, }; - let start_state_expr = builder.state_expr(START_BLOCK); + let start_state_expr = builder.state_expr(mar.span, START_BLOCK); let (state_enum, state_default, state_arms) = builder.state_enum_default_and_arms(); diff --git a/src/mar/translate/state.rs b/src/mar/translate/state.rs index 6c05c00..dcb1ade 100644 --- a/src/mar/translate/state.rs +++ b/src/mar/translate/state.rs @@ -2,6 +2,7 @@ use mar::repr::*; use mar::translate::Builder; use std::collections::HashSet; use syntax::ast::{self, Mutability}; +use syntax::codemap::Span; use syntax::ptr::P; impl<'a, 'b: 'a> Builder<'a, 'b> { @@ -18,29 +19,33 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { pub fn state_path(&self, block: BasicBlock) -> ast::Path { self.ast_builder.path() + .span(self.mar.span) .id("State") .id(self.state_id(block)) .build() + } fn get_incoming_decls(&self, block: BasicBlock) -> Vec<(VarDecl, ast::Ident)> { self.mar.basic_block_data(block).decls().to_vec() } - pub fn state_expr(&self, block: BasicBlock) -> P { + pub fn state_expr(&self, span: Span, block: BasicBlock) -> P { + let ast_builder = self.ast_builder.span(span); + let state_path = self.state_path(block); let incoming_decls = self.get_incoming_decls(block); if incoming_decls.is_empty() { - self.ast_builder.expr().path() + ast_builder.expr().path() .build(state_path) } else { let id_exprs = incoming_decls.iter() .map(|&(_, ident)| { - (ident, self.ast_builder.expr().id(ident)) + (ident, ast_builder.expr().span(self.mar.span).id(ident)) }); - self.ast_builder.expr().struct_path(state_path) + ast_builder.expr().struct_path(state_path) .with_id_exprs(id_exprs) .build() } @@ -92,14 +97,12 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { .build() .build(); - let end_expr = self.ast_builder.block() - .expr().build(self.state_expr(END_BLOCK)); + let end_block = self.ast_builder.block() + .expr().span(self.mar.span).build(self.state_expr(self.mar.span, END_BLOCK)); let state_default = quote_item!(self.cx, impl $generics Default for $state_path { - fn default() -> Self { - $end_expr - } + fn default() -> Self $end_block } ).expect("state default item"); @@ -134,11 +137,14 @@ impl<'a, 'b: 'a> Builder<'a, 'b> { } fn state_arm(&self, block: BasicBlock) -> ast::Arm { - let body = self.ast_builder.block() + let span = self.block_span(block); + let ast_builder = self.ast_builder.span(span); + + let body = ast_builder.block() .with_stmts(self.block(block)) .build(); - self.ast_builder.arm() + ast_builder.arm() .with_pat(self.state_pat(block)) .body().build_block(body) } diff --git a/src/mar/translate/stmt.rs b/src/mar/translate/stmt.rs index 316db55..1579098 100644 --- a/src/mar/translate/stmt.rs +++ b/src/mar/translate/stmt.rs @@ -4,6 +4,7 @@ use syntax::ast; impl<'a, 'b: 'a> Builder<'a, 'b> { pub fn stmt(&self, _block: BasicBlock, stmt: &Statement) -> Vec { + println!("stmt: {} => {:?}", self.cx.codemap().span_to_string(stmt.span()), stmt); match *stmt { Statement::Expr(ref stmt) => vec![stmt.clone()], Statement::Let { span, ref pat, ref ty, ref init } => { diff --git a/tests/test_generator.rs b/tests/test_generator.rs index 739e9cd..468e851 100644 --- a/tests/test_generator.rs +++ b/tests/test_generator.rs @@ -4,19 +4,19 @@ #![allow(unused_mut)] #![allow(non_shorthand_field_patterns)] -#[generator] -fn gen_ints() -> usize { - let x = { - yield_!(1); - let y = 3; - yield_!(2); - y - }; - yield_!(x); -} - #[test] fn test_ints() { + #[generator] + fn gen_ints() -> usize { + let x = { + yield_!(1); + let y = 3; + yield_!(2); + y + }; + yield_!(x); + } + let mut gen = gen_ints(); assert_eq!(gen.next(), Some(1)); assert_eq!(gen.next(), Some(2)); @@ -24,15 +24,15 @@ fn test_ints() { assert_eq!(gen.next(), None); } -#[generator] -fn gen_item_slice<'a, T>(items: &'a [T]) -> &'a T { - for item in items.iter() { - yield_!(item); - } -} - #[test] fn test_item_slice() { + #[generator] + fn gen_item_slice<'a, T>(items: &'a [T]) -> &'a T { + for item in items.iter() { + yield_!(item); + } + } + let items = &[1, 2, 3]; let mut gen = gen_item_slice(items); assert_eq!(gen.next(), Some(&1)); @@ -40,3 +40,19 @@ fn test_item_slice() { assert_eq!(gen.next(), Some(&3)); assert_eq!(gen.next(), None); } + +#[test] +fn test_moved() { + #[generator] + fn gen(items: Vec) -> T { + for item in moved!(items) { + yield_!(moved!(item)); + } + } + + let mut gen = gen(vec![1, 2, 3]); + assert_eq!(gen.next(), Some(1)); + assert_eq!(gen.next(), Some(2)); + assert_eq!(gen.next(), Some(3)); + assert_eq!(gen.next(), None); +}