From 419ac4a1b899ba88fb360b4c71c08f3610564cd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jan 2014 14:39:08 -0500 Subject: [PATCH 01/17] Issue #3511 - Rationalize temporary lifetimes. Major changes: - Define temporary scopes in a syntax-based way that basically defaults to the innermost statement or conditional block, except for in a `let` initializer, where we default to the innermost block. Rules are documented in the code, but not in the manual (yet). See new test run-pass/cleanup-value-scopes.rs for examples. - Refactors Datum to better define cleanup roles. - Refactor cleanup scopes to not be tied to basic blocks, permitting us to have a very large number of scopes (one per AST node). - Introduce nascent documentation in trans/doc.rs covering datums and cleanup in a more comprehensive way. --- src/librustc/lib/llvm.rs | 7 +- src/librustc/middle/astencode.rs | 6 +- .../middle/borrowck/gather_loans/lifetime.rs | 19 +- .../middle/borrowck/gather_loans/mod.rs | 7 +- src/librustc/middle/borrowck/move_data.rs | 4 +- src/librustc/middle/mem_categorization.rs | 19 +- src/librustc/middle/pat_util.rs | 11 + src/librustc/middle/region.rs | 539 +++++- src/librustc/middle/resolve.rs | 2 +- src/librustc/middle/trans/_match.rs | 473 +++-- src/librustc/middle/trans/adt.rs | 19 + src/librustc/middle/trans/asm.rs | 26 +- src/librustc/middle/trans/base.rs | 881 ++------- src/librustc/middle/trans/build.rs | 11 +- src/librustc/middle/trans/callee.rs | 404 ++-- src/librustc/middle/trans/cleanup.rs | 948 ++++++++++ src/librustc/middle/trans/closure.rs | 17 +- src/librustc/middle/trans/common.rs | 447 +---- src/librustc/middle/trans/controlflow.rs | 382 ++-- src/librustc/middle/trans/datum.rs | 1120 ++++++----- src/librustc/middle/trans/debuginfo.rs | 4 +- src/librustc/middle/trans/doc.rs | 227 +++ src/librustc/middle/trans/expr.rs | 1647 +++++++++-------- src/librustc/middle/trans/foreign.rs | 11 +- src/librustc/middle/trans/glue.rs | 89 +- src/librustc/middle/trans/inline.rs | 1 - src/librustc/middle/trans/intrinsic.rs | 37 +- src/librustc/middle/trans/meth.rs | 96 +- src/librustc/middle/trans/mod.rs | 2 + src/librustc/middle/trans/monomorphize.rs | 1 - src/librustc/middle/trans/reflect.rs | 25 +- src/librustc/middle/trans/tvec.rs | 196 +- src/librustc/middle/trans/write_guard.rs | 41 +- src/librustc/middle/typeck/check/mod.rs | 29 +- src/librustc/middle/typeck/check/regionck.rs | 73 +- src/librustc/util/ppaux.rs | 8 + src/librustdoc/html/render.rs | 3 +- src/libstd/ascii.rs | 13 +- src/libstd/io/mem.rs | 6 +- src/libstd/io/process.rs | 14 +- src/libstd/option.rs | 19 + src/libstd/path/posix.rs | 13 +- src/libstd/path/windows.rs | 14 +- src/libstd/rt/crate_map.rs | 18 +- src/libstd/unstable/intrinsics.rs | 6 - src/libstd/util.rs | 2 +- src/libstd/vec.rs | 31 +- src/libsyntax/ast_util.rs | 6 +- src/libsyntax/ext/format.rs | 15 +- src/libsyntax/opt_vec.rs | 47 +- src/libsyntax/visit.rs | 11 + src/rustllvm/RustWrapper.cpp | 11 + ...owck-reborrow-from-shorter-lived-andmut.rs | 31 + .../borrowck-rvalues-mutable-bad.rs | 38 - .../compile-fail/cleanup-rvalue-scopes-cf.rs | 45 + src/test/run-pass/borrowck-rvalues-mutable.rs | 12 + src/test/run-pass/cleanup-arm-conditional.rs | 43 + src/test/run-pass/cleanup-rvalue-scopes.rs | 138 ++ ...nup-rvalue-temp-during-incomplete-alloc.rs | 39 + src/test/run-pass/cleanup-shortcircuit.rs | 30 + src/test/run-pass/intrinsic-move-val.rs | 12 +- src/test/run-pass/issue-10626.rs | 4 +- src/test/run-pass/issue-9382.rs | 8 +- src/test/run-pass/move-1.rs | 1 + 64 files changed, 4797 insertions(+), 3662 deletions(-) create mode 100644 src/librustc/middle/trans/cleanup.rs create mode 100644 src/librustc/middle/trans/doc.rs create mode 100644 src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs delete mode 100644 src/test/compile-fail/borrowck-rvalues-mutable-bad.rs create mode 100644 src/test/compile-fail/cleanup-rvalue-scopes-cf.rs create mode 100644 src/test/run-pass/cleanup-arm-conditional.rs create mode 100644 src/test/run-pass/cleanup-rvalue-scopes.rs create mode 100644 src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs create mode 100644 src/test/run-pass/cleanup-shortcircuit.rs diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index 98f67b6442b5a..c259fa6a61831 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -1698,6 +1698,7 @@ pub mod llvm { pub fn LLVMDICompositeTypeSetTypeArray(CompositeType: ValueRef, TypeArray: ValueRef); pub fn LLVMTypeToString(Type: TypeRef) -> *c_char; + pub fn LLVMValueToString(value_ref: ValueRef) -> *c_char; pub fn LLVMIsAArgument(value_ref: ValueRef) -> ValueRef; @@ -1847,8 +1848,10 @@ impl TypeNames { pub fn val_to_str(&self, val: ValueRef) -> ~str { unsafe { - let ty = Type::from_ref(llvm::LLVMTypeOf(val)); - self.type_to_str(ty) + let s = llvm::LLVMValueToString(val); + let ret = from_c_str(s); + free(s as *c_void); + ret } } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 6a37324e05a69..4ca6f5f370a5d 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -12,13 +12,14 @@ use c = metadata::common; use cstore = metadata::cstore; use driver::session::Session; -use e = metadata::encoder; use metadata::decoder; +use e = metadata::encoder; +use middle::freevars::freevar_entry; +use middle::region; use metadata::tydecode; use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter, RegionParameter}; use metadata::tyencode; -use middle::freevars::freevar_entry; use middle::typeck::{method_origin, method_map_entry}; use middle::{ty, typeck, moves}; use middle; @@ -146,6 +147,7 @@ pub fn decode_inlined_item(cdata: @cstore::crate_metadata, debug!("< Decoded inlined fn: {}::{}", ast_map::path_to_str(path, token::get_ident_interner()), tcx.sess.str_of(ident)); + region::resolve_inlined_item(tcx.sess, &tcx.region_maps, &ii); decode_side_tables(xcx, ast_doc); match ii { ast::IIItem(i) => { diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index 16a7f4c3e7eea..ea222839ffe16 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -220,7 +220,7 @@ impl<'a> GuaranteeLifetimeContext<'a> { // If inside of a match arm, expand the rooting to the entire // match. See the detailed discussion in `check()` above. - let mut root_scope = match discr_scope { + let root_scope = match discr_scope { None => root_scope, Some(id) => { if self.bccx.is_subscope_of(root_scope, id) { @@ -231,17 +231,6 @@ impl<'a> GuaranteeLifetimeContext<'a> { } }; - // FIXME(#3511) grow to the nearest cleanup scope---this can - // cause observable errors if freezing! - if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) { - debug!("{:?} is not a cleanup scope, adjusting", root_scope); - - let cleanup_scope = - self.bccx.tcx.region_maps.cleanup_scope(root_scope); - - root_scope = cleanup_scope; - } - // Add a record of what is required let rm_key = root_map_key {id: cmt_deref.id, derefs: derefs}; let root_info = RootInfo {scope: root_scope}; @@ -301,8 +290,8 @@ impl<'a> GuaranteeLifetimeContext<'a> { // See the SCOPE(LV) function in doc.rs match cmt.cat { - mc::cat_rvalue(cleanup_scope_id) => { - ty::ReScope(cleanup_scope_id) + mc::cat_rvalue(temp_scope) => { + temp_scope } mc::cat_copied_upvar(_) => { ty::ReScope(self.item_scope_id) @@ -313,7 +302,7 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_local(local_id) | mc::cat_arg(local_id) | mc::cat_self(local_id) => { - self.bccx.tcx.region_maps.encl_region(local_id) + ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id)) } mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { ty::ReStatic diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index b1337fca0c85d..00e648db732e7 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -662,8 +662,9 @@ impl<'a> GatherLoanCtxt<'a> { //! with immutable `&` pointers, because borrows of such pointers //! do not require restrictions and hence do not cause a loan. - let lexical_scope = self.bccx.tcx.region_maps.encl_scope(lp.node_id()); - if self.bccx.tcx.region_maps.is_subscope_of(lexical_scope, loan_scope) { + let rm = &self.bccx.tcx.region_maps; + let lexical_scope = rm.var_scope(lp.node_id()); + if rm.is_subscope_of(lexical_scope, loan_scope) { lexical_scope } else { assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope)); @@ -688,7 +689,7 @@ impl<'a> GatherLoanCtxt<'a> { let arg_cmt = mc_ctxt.cat_rvalue( arg.id, arg.pat.span, - body.id, // Arguments live only as long as the fn body. + ty::ReScope(body.id), // Args live only as long as the fn body. arg_ty); self.gather_pat(arg_cmt, arg.pat, None); diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index e61ebd39ca766..53cf5646cfb54 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -471,7 +471,7 @@ impl MoveData { for path in paths.get().iter() { match *path.loan_path { LpVar(id) => { - let kill_id = tcx.region_maps.encl_scope(id); + let kill_id = tcx.region_maps.var_scope(id); let path = { let path_map = self.path_map.borrow(); *path_map.get().get(&path.loan_path) @@ -490,7 +490,7 @@ impl MoveData { var_assignments.get().iter().enumerate() { match *self.path_loan_path(assignment.path) { LpVar(id) => { - let kill_id = tcx.region_maps.encl_scope(id); + let kill_id = tcx.region_maps.var_scope(id); dfcx_assign.add_kill(kill_id, assignment_index); } LpExtend(..) => { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 8cf39d14763b3..70d4f63a16449 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -60,7 +60,7 @@ use syntax::parse::token; #[deriving(Eq)] pub enum categorization { - cat_rvalue(ast::NodeId), // temporary val, argument is its scope + cat_rvalue(ty::Region), // temporary val, argument is its scope cat_static_item, cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env cat_stack_upvar(cmt), // by ref upvar from || @@ -585,21 +585,26 @@ impl mem_categorization_ctxt { pub fn cat_rvalue_node(&self, node: &N, expr_ty: ty::t) -> cmt { - self.cat_rvalue(node.id(), - node.span(), - self.tcx.region_maps.cleanup_scope(node.id()), - expr_ty) + match self.tcx.region_maps.temporary_scope(node.id()) { + Some(scope) => { + self.cat_rvalue(node.id(), node.span(), + ty::ReScope(scope), expr_ty) + } + None => { + self.cat_rvalue(node.id(), node.span(), ty::ReStatic, expr_ty) + } + } } pub fn cat_rvalue(&self, cmt_id: ast::NodeId, span: Span, - cleanup_scope_id: ast::NodeId, + temp_scope: ty::Region, expr_ty: ty::t) -> cmt { @cmt_ { id:cmt_id, span:span, - cat:cat_rvalue(cleanup_scope_id), + cat:cat_rvalue(temp_scope), mutbl:McDeclared, ty:expr_ty } diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index 85217a7d55ece..50096c012be3d 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -108,3 +108,14 @@ pub fn pat_contains_bindings(dm: resolve::DefMap, pat: &Pat) -> bool { }); contains_bindings } + +pub fn simple_identifier<'a>(pat: &'a Pat) -> Option<&'a Path> { + match pat.node { + PatIdent(BindByValue(_), ref path, None) => { + Some(path) + } + _ => { + None + } + } +} diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index ef755b12d15d1..3f4e5a4ef89c0 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -31,6 +31,7 @@ use syntax::codemap::Span; use syntax::{ast, visit}; use syntax::visit::{Visitor, FnKind}; use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local}; +use syntax::ast_util::{stmt_id}; /** The region maps encode information about region relationships. @@ -46,30 +47,30 @@ The region maps encode information about region relationships. - the free region map is populated during type check as we check each function. See the function `relate_free_regions` for more information. -- `cleanup_scopes` includes scopes where trans cleanups occur - - this is intended to reflect the current state of trans, not - necessarily how I think things ought to work +- `temporary_scopes` includes scopes where cleanups for temporaries occur. + These are statements and loop/fn bodies. */ pub struct RegionMaps { priv scope_map: RefCell>, + priv var_map: RefCell>, priv free_region_map: RefCell>, - priv cleanup_scopes: RefCell>, + priv rvalue_scopes: RefCell>, + priv terminating_scopes: RefCell>, } #[deriving(Clone)] pub struct Context { - // Scope where variables should be parented to var_parent: Option, // Innermost enclosing expression parent: Option, } -struct RegionResolutionVisitor { +struct RegionResolutionVisitor<'a> { sess: Session, // Generated maps: - region_maps: RegionMaps, + region_maps: &'a RegionMaps, } @@ -91,22 +92,41 @@ impl RegionMaps { free_region_map.get().insert(sub, ~[sup]); } - pub fn record_parent(&self, sub: ast::NodeId, sup: ast::NodeId) { - debug!("record_parent(sub={:?}, sup={:?})", sub, sup); + pub fn record_encl_scope(&self, sub: ast::NodeId, sup: ast::NodeId) { + debug!("record_encl_scope(sub={}, sup={})", sub, sup); assert!(sub != sup); let mut scope_map = self.scope_map.borrow_mut(); scope_map.get().insert(sub, sup); } - pub fn record_cleanup_scope(&self, scope_id: ast::NodeId) { - //! Records that a scope is a CLEANUP SCOPE. This is invoked - //! from within regionck. We wait until regionck because we do - //! not know which operators are overloaded until that point, - //! and only overloaded operators result in cleanup scopes. + pub fn record_var_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) { + debug!("record_var_scope(sub={}, sup={})", var, lifetime); + assert!(var != lifetime); - let mut cleanup_scopes = self.cleanup_scopes.borrow_mut(); - cleanup_scopes.get().insert(scope_id); + let mut var_map = self.var_map.borrow_mut(); + var_map.get().insert(var, lifetime); + } + + pub fn record_rvalue_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) { + debug!("record_rvalue_scope(sub={}, sup={})", var, lifetime); + assert!(var != lifetime); + + let mut rvalue_scopes = self.rvalue_scopes.borrow_mut(); + rvalue_scopes.get().insert(var, lifetime); + } + + pub fn mark_as_terminating_scope(&self, scope_id: ast::NodeId) { + /*! + * Records that a scope is a TERMINATING SCOPE. Whenever we + * create automatic temporaries -- e.g. by an + * expression like `a().f` -- they will be freed within + * the innermost terminating scope. + */ + + debug!("record_terminating_scope(scope_id={})", scope_id); + let mut terminating_scopes = self.terminating_scopes.borrow_mut(); + terminating_scopes.get().insert(scope_id); } pub fn opt_encl_scope(&self, id: ast::NodeId) -> Option { @@ -122,24 +142,51 @@ impl RegionMaps { let scope_map = self.scope_map.borrow(); match scope_map.get().find(&id) { Some(&r) => r, - None => { fail!("No enclosing scope for id {:?}", id); } + None => { fail!("No enclosing scope for id {}", id); } } } - pub fn is_cleanup_scope(&self, scope_id: ast::NodeId) -> bool { - let cleanup_scopes = self.cleanup_scopes.borrow(); - cleanup_scopes.get().contains(&scope_id) + pub fn var_scope(&self, var_id: ast::NodeId) -> ast::NodeId { + /*! + * Returns the lifetime of the local variable `var_id` + */ + + let var_map = self.var_map.borrow(); + match var_map.get().find(&var_id) { + Some(&r) => r, + None => { fail!("No enclosing scope for id {}", var_id); } + } } - pub fn cleanup_scope(&self, expr_id: ast::NodeId) -> ast::NodeId { - //! Returns the scope when temps in expr will be cleaned up + pub fn temporary_scope(&self, expr_id: ast::NodeId) -> Option { + //! Returns the scope when temp created by expr_id will be cleaned up + + // check for a designated rvalue scope + let rvalue_scopes = self.rvalue_scopes.borrow(); + match rvalue_scopes.get().find(&expr_id) { + Some(&s) => { + debug!("temporary_scope({}) = {} [custom]", expr_id, s); + return Some(s); + } + None => { } + } + // else, locate the innermost terminating scope let mut id = self.encl_scope(expr_id); - let cleanup_scopes = self.cleanup_scopes.borrow(); - while !cleanup_scopes.get().contains(&id) { - id = self.encl_scope(id); + let terminating_scopes = self.terminating_scopes.borrow(); + while !terminating_scopes.get().contains(&id) { + match self.opt_encl_scope(id) { + Some(p) => { + id = p; + } + None => { + debug!("temporary_scope({}) = None", expr_id); + return None; + } + } } - return id; + debug!("temporary_scope({}) = {} [enclosing]", expr_id, id); + return Some(id); } pub fn encl_region(&self, id: ast::NodeId) -> ty::Region { @@ -148,6 +195,12 @@ impl RegionMaps { ty::ReScope(self.encl_scope(id)) } + pub fn var_region(&self, id: ast::NodeId) -> ty::Region { + //! Returns the lifetime of the variable `id`. + + ty::ReScope(self.var_scope(id)) + } + pub fn scopes_intersect(&self, scope1: ast::NodeId, scope2: ast::NodeId) -> bool { self.is_subscope_of(scope1, scope2) || @@ -168,7 +221,7 @@ impl RegionMaps { let scope_map = self.scope_map.borrow(); match scope_map.get().find(&s) { None => { - debug!("is_subscope_of({:?}, {:?}, s={:?})=false", + debug!("is_subscope_of({}, {}, s={})=false", subscope, superscope, s); return false; @@ -177,7 +230,7 @@ impl RegionMaps { } } - debug!("is_subscope_of({:?}, {:?})=true", + debug!("is_subscope_of({}, {})=true", subscope, superscope); return true; @@ -323,67 +376,138 @@ impl RegionMaps { } /// Records the current parent (if any) as the parent of `child_id`. -fn parent_to_expr(visitor: &mut RegionResolutionVisitor, - cx: Context, child_id: ast::NodeId, sp: Span) { - debug!("region::parent_to_expr(span={:?})", - visitor.sess.codemap.span_to_str(sp)); - for parent_id in cx.parent.iter() { - visitor.region_maps.record_parent(child_id, *parent_id); +fn record_superlifetime(visitor: &mut RegionResolutionVisitor, + cx: Context, + child_id: ast::NodeId, + _sp: Span) { + for &parent_id in cx.parent.iter() { + visitor.region_maps.record_encl_scope(child_id, parent_id); + } +} + +/// Records the lifetime of a local variable as `cx.var_parent` +fn record_var_lifetime(visitor: &mut RegionResolutionVisitor, + cx: Context, + var_id: ast::NodeId, + _sp: Span) { + match cx.var_parent { + Some(parent_id) => { + visitor.region_maps.record_var_scope(var_id, parent_id); + } + None => { + // this can happen in extern fn declarations like + // + // extern fn isalnum(c: c_int) -> c_int + } } } fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block, cx: Context) { - // Record the parent of this block. - parent_to_expr(visitor, cx, blk.id, blk.span); + debug!("resolve_block(blk.id={})", blk.id); - // Descend. - let new_cx = Context {var_parent: Some(blk.id), - parent: Some(blk.id)}; - visit::walk_block(visitor, blk, new_cx); + // Record the parent of this block. + record_superlifetime(visitor, cx, blk.id, blk.span); + + // We treat the tail expression in the block (if any) somewhat + // differently from the statements. The issue has to do with + // temporary lifetimes. If the user writes: + // + // { + // ... (&foo()) ... + // } + // + + let subcx = Context {var_parent: Some(blk.id), parent: Some(blk.id)}; + visit::walk_block(visitor, blk, subcx); } fn resolve_arm(visitor: &mut RegionResolutionVisitor, arm: &ast::Arm, cx: Context) { + visitor.region_maps.mark_as_terminating_scope(arm.body.id); + + match arm.guard { + Some(expr) => { + visitor.region_maps.mark_as_terminating_scope(expr.id); + } + None => { } + } + visit::walk_arm(visitor, arm, cx); } fn resolve_pat(visitor: &mut RegionResolutionVisitor, pat: &ast::Pat, cx: Context) { - assert_eq!(cx.var_parent, cx.parent); - parent_to_expr(visitor, cx, pat.id, pat.span); + record_superlifetime(visitor, cx, pat.id, pat.span); + + // If this is a binding (or maybe a binding, I'm too lazy to check + // the def map) then record the lifetime of that binding. + match pat.node { + ast::PatIdent(..) => { + record_var_lifetime(visitor, cx, pat.id, pat.span); + } + _ => { } + } + visit::walk_pat(visitor, pat, cx); } fn resolve_stmt(visitor: &mut RegionResolutionVisitor, stmt: &ast::Stmt, cx: Context) { - match stmt.node { - ast::StmtDecl(..) => { - visit::walk_stmt(visitor, stmt, cx); - } - ast::StmtExpr(_, stmt_id) | - ast::StmtSemi(_, stmt_id) => { - parent_to_expr(visitor, cx, stmt_id, stmt.span); - let expr_cx = Context {parent: Some(stmt_id), ..cx}; - visit::walk_stmt(visitor, stmt, expr_cx); - } - ast::StmtMac(..) => visitor.sess.bug("unexpanded macro") - } + let stmt_id = stmt_id(stmt); + debug!("resolve_stmt(stmt.id={})", stmt_id); + + visitor.region_maps.mark_as_terminating_scope(stmt_id); + record_superlifetime(visitor, cx, stmt_id, stmt.span); + + let subcx = Context {parent: Some(stmt_id), ..cx}; + visit::walk_stmt(visitor, stmt, subcx); } fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr, cx: Context) { - parent_to_expr(visitor, cx, expr.id, expr.span); + debug!("resolve_expr(expr.id={})", expr.id); + + record_superlifetime(visitor, cx, expr.id, expr.span); let mut new_cx = cx; new_cx.parent = Some(expr.id); match expr.node { - ast::ExprAssignOp(..) | ast::ExprIndex(..) | ast::ExprBinary(..) | + // Conditional or repeating scopes are always terminating + // scopes, meaning that temporaries cannot outlive them. + // This ensures fixed size stacks. + + ast::ExprBinary(_, ast::BiAnd, _, r) | + ast::ExprBinary(_, ast::BiOr, _, r) => { + // For shortcircuiting operators, mark the RHS as a terminating + // scope since it only executes conditionally. + visitor.region_maps.mark_as_terminating_scope(r.id); + } + + ast::ExprIf(_, then, Some(otherwise)) => { + visitor.region_maps.mark_as_terminating_scope(then.id); + visitor.region_maps.mark_as_terminating_scope(otherwise.id); + } + + ast::ExprIf(_, then, None) => { + visitor.region_maps.mark_as_terminating_scope(then.id); + } + + ast::ExprLoop(body, _) | + ast::ExprWhile(_, body) => { + visitor.region_maps.mark_as_terminating_scope(body.id); + } + + ast::ExprMatch(..) => { + new_cx.var_parent = Some(expr.id); + } + + ast::ExprAssignOp(..) | ast::ExprIndex(..) | ast::ExprUnary(..) | ast::ExprCall(..) | ast::ExprMethodCall(..) => { // FIXME(#6268) Nested method calls // @@ -402,11 +526,7 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, // for an extended explanantion of why this distinction is // important. // - // parent_to_expr(new_cx, expr.callee_id); - } - - ast::ExprMatch(..) => { - new_cx.var_parent = Some(expr.id); + // record_superlifetime(new_cx, expr.callee_id); } _ => {} @@ -419,9 +539,254 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local, cx: Context) { - assert_eq!(cx.var_parent, cx.parent); - parent_to_expr(visitor, cx, local.id, local.span); + debug!("resolve_local(local.id={},local.init={})", + local.id,local.init.is_some()); + + let blk_id = match cx.var_parent { + Some(id) => id, + None => { + visitor.sess.span_bug( + local.span, + "Local without enclosing block"); + } + }; + + // For convenience in trans, associate with the local-id the var + // scope that will be used for any bindings declared in this + // pattern. + visitor.region_maps.record_var_scope(local.id, blk_id); + + // As an exception to the normal rules governing temporary + // lifetimes, initializers in a let have a temporary lifetime + // of the enclosing block. This means that e.g. a program + // like the following is legal: + // + // let ref x = HashMap::new(); + // + // Because the hash map will be freed in the enclosing block. + // + // We express the rules more formally based on 3 grammars (defined + // fully in the helpers below that implement them): + // + // 1. `E&`, which matches expressions like `&` that + // own a pointer into the stack. + // + // 2. `P&`, which matches patterns like `ref x` or `(ref x, ref + // y)` that produce ref bindings into the value they are + // matched against or something (at least partially) owned by + // the value they are matched against. (By partially owned, + // I mean that creating a binding into a ref-counted or managed value + // would still count.) + // + // 3. `ET`, which matches both rvalues like `foo()` as well as lvalues + // based on rvalues like `foo().x[2].y`. + // + // A subexpression `` that appears in a let initializer + // `let pat [: ty] = expr` has an extended temporary lifetime if + // any of the following conditions are met: + // + // A. `pat` matches `P&` and `expr` matches `ET` + // (covers cases where `pat` creates ref bindings into an rvalue + // produced by `expr`) + // B. `ty` is a borrowed pointer and `expr` matches `ET` + // (covers cases where coercion creates a borrow) + // C. `expr` matches `E&` + // (covers cases `expr` borrows an rvalue that is then assigned + // to memory (at least partially) owned by the binding) + // + // Here are some examples hopefully giving an intution where each + // rule comes into play and why: + // + // Rule A. `let (ref x, ref y) = (foo().x, 44)`. The rvalue `(22, 44)` + // would have an extended lifetime, but not `foo()`. + // + // Rule B. `let x: &[...] = [foo().x]`. The rvalue `[foo().x]` + // would have an extended lifetime, but not `foo()`. + // + // Rule C. `let x = &foo().x`. The rvalue ``foo()` would have extended + // lifetime. + // + // In some cases, multiple rules may apply (though not to the same + // rvalue). For example: + // + // let ref x = [&a(), &b()]; + // + // Here, the expression `[...]` has an extended lifetime due to rule + // A, but the inner rvalues `a()` and `b()` have an extended lifetime + // due to rule C. + // + // FIXME -- Note that `[]` patterns work more smoothly post-DST. + + match local.init { + Some(expr) => { + record_rvalue_scope_if_borrow_expr(visitor, expr, blk_id); + + if is_binding_pat(local.pat) || is_borrowed_ty(local.ty) { + record_rvalue_scope(visitor, expr, blk_id); + } + } + + None => { } + } + visit::walk_local(visitor, local, cx); + + fn is_binding_pat(pat: &ast::Pat) -> bool { + /*! + * True if `pat` match the `P&` nonterminal: + * + * P& = ref X + * | StructName { ..., P&, ... } + * | [ ..., P&, ... ] + * | ( ..., P&, ... ) + * | ~P& + * | box P& + */ + + match pat.node { + ast::PatIdent(ast::BindByRef(_), _, _) => true, + + ast::PatStruct(_, ref field_pats, _) => { + field_pats.iter().any(|fp| is_binding_pat(fp.pat)) + } + + ast::PatVec(ref pats1, ref pats2, ref pats3) => { + pats1.iter().any(|&p| is_binding_pat(p)) || + pats2.iter().any(|&p| is_binding_pat(p)) || + pats3.iter().any(|&p| is_binding_pat(p)) + } + + ast::PatTup(ref subpats) => { + subpats.iter().any(|&p| is_binding_pat(p)) + } + + ast::PatUniq(subpat) => { + is_binding_pat(subpat) + } + + _ => false, + } + } + + fn is_borrowed_ty(ty: &ast::Ty) -> bool { + /*! + * True if `ty` is a borrowed pointer type + * like `&int` or `&[...]`. + */ + + match ty.node { + ast::TyRptr(..) => true, + _ => false + } + } + + fn record_rvalue_scope_if_borrow_expr(visitor: &mut RegionResolutionVisitor, + expr: &ast::Expr, + blk_id: ast::NodeId) { + /*! + * If `expr` matches the `E&` grammar, then records an extended + * rvalue scope as appropriate: + * + * E& = & ET + * | StructName { ..., f: E&, ... } + * | [ ..., E&, ... ] + * | ( ..., E&, ... ) + * | {...; E&} + * | ~E& + * | E& as ... + * | ( E& ) + */ + + match expr.node { + ast::ExprAddrOf(_, subexpr) => { + record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); + record_rvalue_scope(visitor, subexpr, blk_id); + } + ast::ExprStruct(_, ref fields, _) => { + for field in fields.iter() { + record_rvalue_scope_if_borrow_expr( + visitor, field.expr, blk_id); + } + } + ast::ExprVstore(subexpr, _) => { + visitor.region_maps.record_rvalue_scope(subexpr.id, blk_id); + record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); + } + ast::ExprVec(ref subexprs, _) | + ast::ExprTup(ref subexprs) => { + for &subexpr in subexprs.iter() { + record_rvalue_scope_if_borrow_expr( + visitor, subexpr, blk_id); + } + } + ast::ExprUnary(_, ast::UnUniq, subexpr) => { + record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); + } + ast::ExprCast(subexpr, _) | + ast::ExprParen(subexpr) => { + record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id) + } + ast::ExprBlock(ref block) => { + match block.expr { + Some(subexpr) => { + record_rvalue_scope_if_borrow_expr( + visitor, subexpr, blk_id); + } + None => { } + } + } + _ => { + } + } + } + + fn record_rvalue_scope<'a>(visitor: &mut RegionResolutionVisitor, + expr: &'a ast::Expr, + blk_id: ast::NodeId) { + /*! + * Applied to an expression `expr` if `expr` -- or something + * owned or partially owned by `expr` -- is going to be + * indirectly referenced by a variable in a let statement. In + * that case, the "temporary lifetime" or `expr` is extended + * to be the block enclosing the `let` statement. + * + * More formally, if `expr` matches the grammar `ET`, record + * the rvalue scope of the matching `` as `blk_id`: + * + * ET = *ET + * | ET[...] + * | ET.f + * | (ET) + * | + * + * Note: ET is intended to match "rvalues or + * lvalues based on rvalues". + */ + + let mut expr = expr; + loop { + // Note: give all the expressions matching `ET` with the + // extended temporary lifetime, not just the innermost rvalue, + // because in trans if we must compile e.g. `*rvalue()` + // into a temporary, we request the temporary scope of the + // outer expression. + visitor.region_maps.record_rvalue_scope(expr.id, blk_id); + + match expr.node { + ast::ExprAddrOf(_, ref subexpr) | + ast::ExprUnary(_, ast::UnDeref, ref subexpr) | + ast::ExprField(ref subexpr, _, _) | + ast::ExprIndex(_, ref subexpr, _) | + ast::ExprParen(ref subexpr) => { + let subexpr: &'a @Expr = subexpr; // FIXME + expr = &**subexpr; + } + _ => { + return; + } + } + } + } } fn resolve_item(visitor: &mut RegionResolutionVisitor, @@ -439,22 +804,23 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor, sp: Span, id: ast::NodeId, cx: Context) { - debug!("region::resolve_fn(id={:?}, \ + debug!("region::resolve_fn(id={}, \ span={:?}, \ - body.id={:?}, \ - cx.parent={:?})", + body.id={}, \ + cx.parent={})", id, visitor.sess.codemap.span_to_str(sp), body.id, cx.parent); + visitor.region_maps.mark_as_terminating_scope(body.id); + // The arguments and `self` are parented to the body of the fn. let decl_cx = Context {parent: Some(body.id), - var_parent: Some(body.id), - ..cx}; + var_parent: Some(body.id)}; match *fk { visit::FkMethod(_, _, method) => { - visitor.region_maps.record_parent(method.self_id, body.id); + visitor.region_maps.record_var_scope(method.self_id, body.id); } _ => {} } @@ -471,7 +837,7 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor, visitor.visit_block(body, body_cx); } -impl Visitor for RegionResolutionVisitor { +impl<'a> Visitor for RegionResolutionVisitor<'a> { fn visit_block(&mut self, b: &Block, cx: Context) { resolve_block(self, b, cx); @@ -503,16 +869,33 @@ impl Visitor for RegionResolutionVisitor { } pub fn resolve_crate(sess: Session, crate: &ast::Crate) -> RegionMaps { + let maps = RegionMaps { + scope_map: RefCell::new(HashMap::new()), + var_map: RefCell::new(HashMap::new()), + free_region_map: RefCell::new(HashMap::new()), + rvalue_scopes: RefCell::new(HashMap::new()), + terminating_scopes: RefCell::new(HashSet::new()), + }; + { + let mut visitor = RegionResolutionVisitor { + sess: sess, + region_maps: &maps + }; + let cx = Context { parent: None, var_parent: None }; + visit::walk_crate(&mut visitor, crate, cx); + } + return maps; +} + +pub fn resolve_inlined_item(sess: Session, + region_maps: &RegionMaps, + item: &ast::InlinedItem) { + let cx = Context {parent: None, + var_parent: None}; let mut visitor = RegionResolutionVisitor { sess: sess, - region_maps: RegionMaps { - scope_map: RefCell::new(HashMap::new()), - free_region_map: RefCell::new(HashMap::new()), - cleanup_scopes: RefCell::new(HashSet::new()) - } + region_maps: region_maps, }; - let cx = Context { parent: None, var_parent: None }; - visit::walk_crate(&mut visitor, crate, cx); - return visitor.region_maps; + visit::walk_inlined_item(&mut visitor, item, cx); } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 8731bf07ec3d2..81bd7ba618907 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -2442,7 +2442,7 @@ impl Resolver { match type_result { BoundResult(target_module, name_bindings) => { debug!("(resolving single import) found type target: {:?}", - name_bindings.type_def.get().unwrap().type_def); + {name_bindings.type_def.get().unwrap().type_def}); import_resolution.type_target.set( Some(Target::new(target_module, name_bindings))); import_resolution.type_id.set(directive.id); diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index a7924946ed185..c7eb837aaf864 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -204,6 +204,8 @@ use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::common::*; use middle::trans::consts; use middle::trans::controlflow; @@ -221,7 +223,6 @@ use util::ppaux::{Repr, vec_map_to_str}; use std::cell::Cell; use std::hashmap::HashMap; -use std::ptr; use std::vec; use syntax::ast; use syntax::ast::Ident; @@ -315,16 +316,18 @@ pub enum opt_result<'a> { fn trans_opt<'a>(bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> { let _icx = push_ctxt("match::trans_opt"); let ccx = bcx.ccx(); - let bcx = bcx; + let mut bcx = bcx; match *o { lit(ExprLit(lit_expr)) => { - let datumblock = expr::trans_to_datum(bcx, lit_expr); - return single_result(datumblock.to_result()); + let lit_datum = unpack_datum!(bcx, expr::trans(bcx, lit_expr)); + let lit_datum = lit_datum.assert_rvalue(bcx); // literals are rvalues + let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx)); + return single_result(rslt(bcx, lit_datum.val)); } lit(UnitLikeStructLit(pat_id)) => { let struct_ty = ty::node_id_to_type(bcx.tcx(), pat_id); - let datumblock = datum::scratch_datum(bcx, struct_ty, "", true); - return single_result(datumblock.to_result(bcx)); + let datum = datum::rvalue_scratch_datum(bcx, struct_ty, ""); + return single_result(rslt(bcx, datum.val)); } lit(ConstLit(lit_id)) => { let (llval, _) = consts::get_const_val(bcx.ccx(), lit_id); @@ -1007,14 +1010,18 @@ fn extract_variant_args<'a>( ExtractedBlock { vals: args, bcx: bcx } } -fn match_datum<'a>(bcx: &'a Block<'a>, val: ValueRef, pat_id: ast::NodeId) - -> Datum { - //! Helper for converting from the ValueRef that we pass around in - //! the match code, which is always by ref, into a Datum. Eventually - //! we should just pass around a Datum and be done with it. +fn match_datum(bcx: &Block, + val: ValueRef, + pat_id: ast::NodeId) + -> Datum { + /*! + * Helper for converting from the ValueRef that we pass around in + * the match code, which is always an lvalue, into a Datum. Eventually + * we should just pass around a Datum and be done with it. + */ let ty = node_id_type(bcx, pat_id); - Datum {val: val, ty: ty, mode: datum::ByRef(RevokeClean)} + Datum(val, ty, Lvalue) } @@ -1054,13 +1061,11 @@ fn extract_vec_elems<'a>( ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable}, ty::vstore_slice(ty::ReStatic) ); - let scratch = scratch_datum(bcx, slice_ty, "", false); + let scratch = rvalue_scratch_datum(bcx, slice_ty, ""); Store(bcx, slice_begin, - GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]) - ); + GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])); Store(bcx, slice_len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])); elems[n] = scratch.val; - scratch.add_clean(bcx); } ExtractedBlock { vals: elems, bcx: bcx } @@ -1176,7 +1181,8 @@ impl<'a> DynamicFailureHandler<'a> { _ => (), } - let fail_cx = sub_block(self.bcx, "case_fallthrough"); + let fcx = self.bcx.fcx; + let fail_cx = fcx.new_block(false, "case_fallthrough", None); controlflow::trans_fail(fail_cx, Some(self.sp), self.msg); self.finished.set(Some(fail_cx.llbb)); fail_cx.llbb @@ -1297,30 +1303,31 @@ fn compare_values<'a>( fn store_non_ref_bindings<'a>( bcx: &'a Block<'a>, bindings_map: &BindingsMap, - mut opt_temp_cleanups: Option<&mut ~[ValueRef]>) - -> &'a Block<'a> { + opt_cleanup_scope: Option) + -> &'a Block<'a> +{ /*! - * - * For each copy/move binding, copy the value from the value - * being matched into its final home. This code executes once - * one of the patterns for a given arm has completely matched. - * It adds temporary cleanups to the `temp_cleanups` array, - * if one is provided. + * For each copy/move binding, copy the value from the value being + * matched into its final home. This code executes once one of + * the patterns for a given arm has completely matched. It adds + * cleanups to the `opt_cleanup_scope`, if one is provided. */ + let fcx = bcx.fcx; let mut bcx = bcx; for (_, &binding_info) in bindings_map.iter() { match binding_info.trmode { TrByValue(lldest) => { let llval = Load(bcx, binding_info.llmatch); // get a T* - let datum = Datum {val: llval, ty: binding_info.ty, - mode: ByRef(ZeroMem)}; - bcx = datum.store_to(bcx, INIT, lldest); - opt_temp_cleanups.mutate(|temp_cleanups| { - add_clean_temp_mem(bcx, lldest, binding_info.ty); - temp_cleanups.push(lldest); - temp_cleanups - }); + let datum = Datum(llval, binding_info.ty, Lvalue); + bcx = datum.store_to(bcx, lldest); + + match opt_cleanup_scope { + None => {} + Some(s) => { + fcx.schedule_drop_mem(s, lldest, binding_info.ty); + } + } } TrByRef => {} } @@ -1328,38 +1335,29 @@ fn store_non_ref_bindings<'a>( return bcx; } -fn insert_lllocals<'a>( - bcx: &'a Block<'a>, - bindings_map: &BindingsMap, - add_cleans: bool) - -> &'a Block<'a> { +fn insert_lllocals<'a>(bcx: &'a Block<'a>, + bindings_map: &BindingsMap, + cleanup_scope: cleanup::ScopeId) + -> &'a Block<'a> { /*! * For each binding in `data.bindings_map`, adds an appropriate entry into - * the `fcx.lllocals` map. If add_cleans is true, then adds cleanups for - * the bindings. + * the `fcx.lllocals` map, scheduling cleanup in `cleanup_scope`. */ + let fcx = bcx.fcx; + for (&ident, &binding_info) in bindings_map.iter() { let llval = match binding_info.trmode { // By value bindings: use the stack slot that we // copied/moved the value into TrByValue(lldest) => lldest, + // By ref binding: use the ptr into the matched value TrByRef => binding_info.llmatch }; - let datum = Datum { - val: llval, - ty: binding_info.ty, - mode: ByRef(ZeroMem) - }; - - if add_cleans { - match binding_info.trmode { - TrByValue(_) => datum.add_clean(bcx), - _ => {} - } - } + let datum = Datum(llval, binding_info.ty, Lvalue); + fcx.schedule_drop_mem(cleanup_scope, llval, binding_info.ty); { debug!("binding {:?} to {}", @@ -1396,24 +1394,23 @@ fn compile_guard<'r, vec_map_to_str(vals, |v| bcx.val_to_str(*v))); let _indenter = indenter(); + // Lest the guard itself should fail, introduce a temporary cleanup + // scope for any non-ref bindings we create. + let temp_scope = bcx.fcx.push_custom_cleanup_scope(); + let mut bcx = bcx; - let mut temp_cleanups = ~[]; - bcx = store_non_ref_bindings(bcx, - data.bindings_map, - Some(&mut temp_cleanups)); - bcx = insert_lllocals(bcx, data.bindings_map, false); - - let val = unpack_result!(bcx, { - with_scope_result(bcx, guard_expr.info(), "guard", |bcx| { - expr::trans_to_datum(bcx, guard_expr).to_result() - }) - }); - let val = bool_to_i1(bcx, val); + bcx = store_non_ref_bindings(bcx, data.bindings_map, + Some(cleanup::CustomScope(temp_scope))); + bcx = insert_lllocals(bcx, data.bindings_map, + cleanup::CustomScope(temp_scope)); - // Revoke the temp cleanups now that the guard successfully executed. - for llval in temp_cleanups.iter() { - revoke_clean(bcx, *llval); - } + let val = unpack_datum!(bcx, expr::trans(bcx, guard_expr)); + let val = val.to_llbool(bcx); + + // Cancel cleanups now that the guard successfully executed. If + // the guard was false, we will drop the values explicitly + // below. Otherwise, we'll add lvalue cleanups at the end. + bcx.fcx.pop_custom_cleanup_scope(temp_scope); return with_cond(bcx, Not(bcx, val), |bcx| { // Guard does not match: free the values we copied, @@ -1502,6 +1499,7 @@ fn compile_submatch_continue<'r, chk: &FailureHandler, col: uint, val: ValueRef) { + let fcx = bcx.fcx; let tcx = bcx.tcx(); let dm = tcx.def_map; @@ -1602,6 +1600,7 @@ fn compile_submatch_continue<'r, debug!("options={:?}", opts); let mut kind = no_branch; let mut test_val = val; + debug!("test_val={}", bcx.val_to_str(test_val)); if opts.len() > 0u { match opts[0] { var(_, repr) => { @@ -1621,8 +1620,7 @@ fn compile_submatch_continue<'r, }, vec_len(..) => { let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id)); - let unboxed = load_if_immediate(bcx, val, vt.vec_ty); - let (_, len) = tvec::get_base_and_len(bcx, unboxed, vt.vec_ty); + let (_, len) = tvec::get_base_and_len(bcx, val, vt.vec_ty); test_val = len; kind = compare_vec_len; } @@ -1636,7 +1634,7 @@ fn compile_submatch_continue<'r, } let else_cx = match kind { no_branch | single => bcx, - _ => sub_block(bcx, "match_else") + _ => bcx.fcx.new_temp_block("match_else") }; let sw = if kind == switch { Switch(bcx, test_val, else_cx.llbb, opts.len()) @@ -1657,7 +1655,7 @@ fn compile_submatch_continue<'r, let mut branch_chk = None; let mut opt_cx = else_cx; if !exhaustive || i+1 < len { - opt_cx = sub_block(bcx, "match_case"); + opt_cx = bcx.fcx.new_temp_block("match_case"); match kind { single => Br(bcx, opt_cx.llbb), switch => { @@ -1678,75 +1676,65 @@ fn compile_submatch_continue<'r, compare => { let t = node_id_type(bcx, pat_id); let Result {bcx: after_cx, val: matches} = { - with_scope_result(bcx, None, "compaReScope", |bcx| { - match trans_opt(bcx, opt) { - single_result( - Result {bcx, val}) => { - compare_values(bcx, test_val, val, t) - } - lower_bound( - Result {bcx, val}) => { + match trans_opt(bcx, opt) { + single_result(Result {bcx, val}) => { + compare_values(bcx, test_val, val, t) + } + lower_bound(Result {bcx, val}) => { + compare_scalar_types( + bcx, test_val, val, + t, ast::BiGe) + } + range_result(Result {val: vbegin, ..}, + Result {bcx, val: vend}) => { + let Result {bcx, val: llge} = compare_scalar_types( - bcx, test_val, val, - t, ast::BiGe) - } - range_result( - Result {val: vbegin, ..}, - Result {bcx, val: vend}) => { - let Result {bcx, val: llge} = - compare_scalar_types( - bcx, test_val, - vbegin, t, ast::BiGe); - let Result {bcx, val: llle} = - compare_scalar_types( - bcx, test_val, vend, - t, ast::BiLe); - rslt(bcx, And(bcx, llge, llle)) - } + bcx, test_val, + vbegin, t, ast::BiGe); + let Result {bcx, val: llle} = + compare_scalar_types( + bcx, test_val, vend, + t, ast::BiLe); + rslt(bcx, And(bcx, llge, llle)) } - }) + } }; - bcx = sub_block(after_cx, "compare_next"); + bcx = fcx.new_temp_block("compare_next"); CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); } compare_vec_len => { let Result {bcx: after_cx, val: matches} = { - with_scope_result(bcx, - None, - "compare_vec_len_scope", - |bcx| { - match trans_opt(bcx, opt) { - single_result( - Result {bcx, val}) => { - let value = compare_scalar_values( - bcx, test_val, val, - signed_int, ast::BiEq); - rslt(bcx, value) - } - lower_bound( - Result {bcx, val: val}) => { - let value = compare_scalar_values( - bcx, test_val, val, - signed_int, ast::BiGe); - rslt(bcx, value) - } - range_result( - Result {val: vbegin, ..}, - Result {bcx, val: vend}) => { - let llge = - compare_scalar_values( - bcx, test_val, - vbegin, signed_int, ast::BiGe); - let llle = - compare_scalar_values( - bcx, test_val, vend, - signed_int, ast::BiLe); - rslt(bcx, And(bcx, llge, llle)) - } + match trans_opt(bcx, opt) { + single_result( + Result {bcx, val}) => { + let value = compare_scalar_values( + bcx, test_val, val, + signed_int, ast::BiEq); + rslt(bcx, value) } - }) + lower_bound( + Result {bcx, val: val}) => { + let value = compare_scalar_values( + bcx, test_val, val, + signed_int, ast::BiGe); + rslt(bcx, value) + } + range_result( + Result {val: vbegin, ..}, + Result {bcx, val: vend}) => { + let llge = + compare_scalar_values( + bcx, test_val, + vbegin, signed_int, ast::BiGe); + let llle = + compare_scalar_values( + bcx, test_val, vend, + signed_int, ast::BiLe); + rslt(bcx, And(bcx, llge, llle)) + } + } }; - bcx = sub_block(after_cx, "compare_vec_len_next"); + bcx = fcx.new_temp_block("compare_vec_len_next"); // If none of these subcases match, move on to the // next condition. @@ -1812,9 +1800,7 @@ pub fn trans_match<'a>( dest: Dest) -> &'a Block<'a> { let _icx = push_ctxt("match::trans_match"); - with_scope(bcx, match_expr.info(), "match", |bcx| { - trans_match_inner(bcx, discr_expr, arms, dest) - }) + trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest) } fn create_bindings_map(bcx: &Block, pat: @ast::Pat) -> BindingsMap { @@ -1857,19 +1843,18 @@ fn create_bindings_map(bcx: &Block, pat: @ast::Pat) -> BindingsMap { return bindings_map; } -fn trans_match_inner<'a>( - scope_cx: &'a Block<'a>, - discr_expr: &ast::Expr, - arms: &[ast::Arm], - dest: Dest) - -> &'a Block<'a> { +fn trans_match_inner<'a>(scope_cx: &'a Block<'a>, + match_id: ast::NodeId, + discr_expr: &ast::Expr, + arms: &[ast::Arm], + dest: Dest) -> &'a Block<'a> { let _icx = push_ctxt("match::trans_match_inner"); + let fcx = scope_cx.fcx; let mut bcx = scope_cx; let tcx = bcx.tcx(); - let discr_datum = unpack_datum!(bcx, { - expr::trans_to_datum(bcx, discr_expr) - }); + let discr_datum = unpack_datum!(bcx, expr::trans_to_lvalue(bcx, discr_expr, + "match")); if bcx.unreachable.get() { return bcx; } @@ -1877,7 +1862,7 @@ fn trans_match_inner<'a>( let mut arm_datas = ~[]; let mut matches = ~[]; for arm in arms.iter() { - let body = scope_block(bcx, arm.body.info(), "case_body"); + let body = fcx.new_id_block("case_body", arm.body.id); let bindings_map = create_bindings_map(bcx, arm.pats[0]); let arm_data = ArmData { bodycx: body, @@ -1910,7 +1895,7 @@ fn trans_match_inner<'a>( Infallible } }; - let lldiscr = discr_datum.to_ref_llval(bcx); + let lldiscr = discr_datum.val; compile_submatch(bcx, matches, [lldiscr], &chk); let mut arm_cxs = ~[]; @@ -1926,14 +1911,15 @@ fn trans_match_inner<'a>( } // insert bindings into the lllocals map and add cleanups - bcx = insert_lllocals(bcx, arm_data.bindings_map, true); - + let cleanup_scope = fcx.push_custom_cleanup_scope(); + bcx = insert_lllocals(bcx, arm_data.bindings_map, + cleanup::CustomScope(cleanup_scope)); bcx = controlflow::trans_block(bcx, arm_data.arm.body, dest); - bcx = trans_block_cleanups(bcx, block_cleanups(arm_data.bodycx)); + bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope); arm_cxs.push(bcx); } - bcx = controlflow::join_blocks(scope_cx, arm_cxs); + bcx = scope_cx.fcx.join_blocks(match_id, arm_cxs); return bcx; } @@ -1944,17 +1930,18 @@ enum IrrefutablePatternBindingMode { BindArgument } -pub fn store_local<'a>( - bcx: &'a Block<'a>, - pat: @ast::Pat, - opt_init_expr: Option<@ast::Expr>) - -> &'a Block<'a> { +pub fn store_local<'a>(bcx: &'a Block<'a>, + local: &ast::Local) + -> &'a Block<'a> { /*! * Generates code for a local variable declaration like * `let ;` or `let = `. */ let _icx = push_ctxt("match::store_local"); let mut bcx = bcx; + let tcx = bcx.tcx(); + let pat = local.pat; + let opt_init_expr = local.init; return match opt_init_expr { Some(init_expr) => { @@ -1970,9 +1957,11 @@ pub fn store_local<'a>( // it assumes it is matching against a valid value. match simple_identifier(pat) { Some(path) => { + let var_scope = cleanup::var_scope(tcx, local.id); return mk_binding_alloca( - bcx, pat.id, path, BindLocal, - |bcx, datum| expr::trans_into(bcx, init_expr, expr::SaveIn(datum.val))); + bcx, pat.id, path, BindLocal, var_scope, (), + |(), bcx, v, _| expr::trans_into(bcx, init_expr, + expr::SaveIn(v))); } None => {} @@ -1980,17 +1969,15 @@ pub fn store_local<'a>( // General path. let init_datum = - unpack_datum!( - bcx, - expr::trans_to_datum(bcx, init_expr)); + unpack_datum!(bcx, expr::trans_to_lvalue(bcx, init_expr, "let")); if ty::type_is_bot(expr_ty(bcx, init_expr)) { create_dummy_locals(bcx, pat) } else { if bcx.sess().asm_comments() { add_comment(bcx, "creating zeroable ref llval"); } - let llptr = init_datum.to_ref_llval(bcx); - return bind_irrefutable_pat(bcx, pat, llptr, BindLocal); + let var_scope = cleanup::var_scope(tcx, local.id); + bind_irrefutable_pat(bcx, pat, init_datum.val, BindLocal, var_scope) } } None => { @@ -1998,22 +1985,27 @@ pub fn store_local<'a>( } }; - fn create_dummy_locals<'a>(mut bcx: &'a Block<'a>, pat: @ast::Pat) - -> &'a Block<'a> { + fn create_dummy_locals<'a>(mut bcx: &'a Block<'a>, + pat: @ast::Pat) + -> &'a Block<'a> { // create dummy memory for the variables if we have no // value to store into them immediately let tcx = bcx.tcx(); pat_bindings(tcx.def_map, pat, |_, p_id, _, path| { - bcx = mk_binding_alloca( - bcx, p_id, path, BindLocal, - |bcx, datum| { datum.cancel_clean(bcx); bcx }); - }); + let scope = cleanup::var_scope(tcx, p_id); + bcx = mk_binding_alloca( + bcx, p_id, path, BindLocal, scope, (), + |(), bcx, llval, ty| { zero_mem(bcx, llval, ty); bcx }); + }); bcx } } -pub fn store_arg<'a>(mut bcx: &'a Block<'a>, pat: @ast::Pat, arg: Datum) - -> &'a Block<'a> { +pub fn store_arg<'a>(mut bcx: &'a Block<'a>, + pat: @ast::Pat, + arg: Datum, + arg_scope: cleanup::ScopeId) + -> &'a Block<'a> { /*! * Generates code for argument patterns like `fn foo(: T)`. * Creates entries in the `llargs` map for each of the bindings @@ -2026,62 +2018,56 @@ pub fn store_arg<'a>(mut bcx: &'a Block<'a>, pat: @ast::Pat, arg: Datum) * if the argument type is `T`, then `llval` is a `T*`). In some * cases, this code may zero out the memory `llval` points at. */ + let _icx = push_ctxt("match::store_arg"); - // We always need to cleanup the argument as we exit the fn scope. - // Note that we cannot do it before for fear of a fn like - // fn getaddr(~ref x: ~uint) -> *uint {....} - // (From test `run-pass/func-arg-ref-pattern.rs`) - arg.add_clean(bcx); - - // Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an - // alloca, which only is the case on the general path, so lets disable the optimized path when - // debug info is enabled. - let arg_is_alloca = unsafe { llvm::LLVMIsAAllocaInst(arg.val) != ptr::null() }; - - let fast_path = (arg_is_alloca || !bcx.ccx().sess.opts.extra_debuginfo) - && simple_identifier(pat).is_some(); - - if fast_path { - // Optimized path for `x: T` case. This just adopts - // `llval` wholesale as the pointer for `x`, avoiding the - // general logic which may copy out of `llval`. - let mut llargs = bcx.fcx.llargs.borrow_mut(); - llargs.get().insert(pat.id, arg); - } else { - // General path. Copy out the values that are used in the - // pattern. - let llptr = arg.to_ref_llval(bcx); - bcx = bind_irrefutable_pat(bcx, pat, llptr, BindArgument); - } + match simple_identifier(pat) { + Some(path) => { + // Generate nicer LLVM for the common case of fn a pattern + // like `x: T` + mk_binding_alloca( + bcx, pat.id, path, BindArgument, arg_scope, arg, + |arg, bcx, llval, _| arg.store_to(bcx, llval)) + } - return bcx; + None => { + // General path. Copy out the values that are used in the + // pattern. + let arg = unpack_datum!( + bcx, arg.to_lvalue_datum_in_scope(bcx, "__arg", arg_scope)); + bind_irrefutable_pat(bcx, pat, arg.val, + BindArgument, arg_scope) + } + } } -fn mk_binding_alloca<'a>( - bcx: &'a Block<'a>, - p_id: ast::NodeId, - path: &ast::Path, - binding_mode: IrrefutablePatternBindingMode, - populate: |&'a Block<'a>, Datum| -> &'a Block<'a>) - -> &'a Block<'a> { +fn mk_binding_alloca<'a,A>(bcx: &'a Block<'a>, + p_id: ast::NodeId, + path: &ast::Path, + binding_mode: IrrefutablePatternBindingMode, + cleanup_scope: cleanup::ScopeId, + arg: A, + populate: |A, &'a Block<'a>, ValueRef, ty::t| -> &'a Block<'a>) + -> &'a Block<'a> { let var_ty = node_id_type(bcx, p_id); let ident = ast_util::path_to_ident(path); + + // Allocate memory on stack for the binding. let llval = alloc_ty(bcx, var_ty, bcx.ident(ident)); - let datum = Datum { - val: llval, - ty: var_ty, - mode: ByRef(ZeroMem) + + // Subtle: be sure that we *populate* the memory *before* + // we schedule the cleanup. + let bcx = populate(arg, bcx, llval, var_ty); + bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty); + + // Now that memory is initialized and has cleanup scheduled, + // create the datum and insert into the local variable map. + let datum = Datum(llval, var_ty, Lvalue); + let mut llmap = match binding_mode { + BindLocal => bcx.fcx.lllocals.borrow_mut(), + BindArgument => bcx.fcx.llargs.borrow_mut() }; - { - let mut llmap = match binding_mode { - BindLocal => bcx.fcx.lllocals.borrow_mut(), - BindArgument => bcx.fcx.llargs.borrow_mut() - }; - llmap.get().insert(p_id, datum); - } - let bcx = populate(bcx, datum); - datum.add_clean(bcx); + llmap.get().insert(p_id, datum); bcx } @@ -2089,7 +2075,8 @@ fn bind_irrefutable_pat<'a>( bcx: &'a Block<'a>, pat: @ast::Pat, val: ValueRef, - binding_mode: IrrefutablePatternBindingMode) + binding_mode: IrrefutablePatternBindingMode, + cleanup_scope: cleanup::ScopeId) -> &'a Block<'a> { /*! * A simple version of the pattern matching code that only handles @@ -2103,11 +2090,10 @@ fn bind_irrefutable_pat<'a>( * # Arguments * - bcx: starting basic block context * - pat: the irrefutable pattern being matched. - * - val: a pointer to the value being matched. If pat matches a value - * of type T, then this is a T*. If the value is moved from `pat`, - * then `*pat` will be zeroed; otherwise, it's existing cleanup - * applies. + * - val: the value being matched -- must be an lvalue (by ref, with cleanup) * - binding_mode: is this for an argument or a local variable? + * + * FIXME: convert `val` to `Datum` for more type safety */ debug!("bind_irrefutable_pat(bcx={}, pat={}, binding_mode={:?})", @@ -2133,24 +2119,20 @@ fn bind_irrefutable_pat<'a>( // binding will live and place it into the appropriate // map. bcx = mk_binding_alloca( - bcx, pat.id, path, binding_mode, - |bcx, var_datum| { + bcx, pat.id, path, binding_mode, cleanup_scope, (), + |(), bcx, llval, ty| { match pat_binding_mode { ast::BindByValue(_) => { // By value binding: move the value that `val` // points at into the binding's stack slot. - let datum = Datum { - val: val, - ty: var_datum.ty, - mode: ByRef(ZeroMem) - }; - datum.store_to(bcx, INIT, var_datum.val) + let d = Datum(val, ty, Lvalue); + d.store_to(bcx, llval) } ast::BindByRef(_) => { // By ref binding: the value of the variable // is the pointer `val` itself. - Store(bcx, val, var_datum.val); + Store(bcx, val, llval); bcx } } @@ -2158,7 +2140,8 @@ fn bind_irrefutable_pat<'a>( } for &inner_pat in inner.iter() { - bcx = bind_irrefutable_pat(bcx, inner_pat, val, binding_mode); + bcx = bind_irrefutable_pat(bcx, inner_pat, val, + binding_mode, cleanup_scope); } } ast::PatEnum(_, ref sub_pats) => { @@ -2176,7 +2159,8 @@ fn bind_irrefutable_pat<'a>( for sub_pat in sub_pats.iter() { for (i, argval) in args.vals.iter().enumerate() { bcx = bind_irrefutable_pat(bcx, sub_pat[i], - *argval, binding_mode); + *argval, binding_mode, + cleanup_scope); } } } @@ -2193,7 +2177,8 @@ fn bind_irrefutable_pat<'a>( let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i); bcx = bind_irrefutable_pat(bcx, *elem, - fldptr, binding_mode); + fldptr, binding_mode, + cleanup_scope); } } } @@ -2214,7 +2199,8 @@ fn bind_irrefutable_pat<'a>( let ix = ty::field_idx_strict(tcx, f.ident.name, field_tys); let fldptr = adt::trans_field_ptr(bcx, pat_repr, val, discr, ix); - bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, binding_mode); + bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, + binding_mode, cleanup_scope); } }) } @@ -2222,16 +2208,17 @@ fn bind_irrefutable_pat<'a>( let repr = adt::represent_node(bcx, pat.id); for (i, elem) in elems.iter().enumerate() { let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i); - bcx = bind_irrefutable_pat(bcx, *elem, fldptr, binding_mode); + bcx = bind_irrefutable_pat(bcx, *elem, fldptr, + binding_mode, cleanup_scope); } } ast::PatUniq(inner) => { let llbox = Load(bcx, val); - bcx = bind_irrefutable_pat(bcx, inner, llbox, binding_mode); + bcx = bind_irrefutable_pat(bcx, inner, llbox, binding_mode, cleanup_scope); } ast::PatRegion(inner) => { let loaded_val = Load(bcx, val); - bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode); + bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode, cleanup_scope); } ast::PatVec(..) => { bcx.tcx().sess.span_bug( @@ -2243,14 +2230,4 @@ fn bind_irrefutable_pat<'a>( return bcx; } -fn simple_identifier<'a>(pat: &'a ast::Pat) -> Option<&'a ast::Path> { - match pat.node { - ast::PatIdent(ast::BindByValue(_), ref path, None) => { - Some(path) - } - _ => { - None - } - } -} diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 25a69174bb2c5..91dcae2d1d308 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -628,6 +628,25 @@ pub fn num_args(r: &Repr, discr: Disr) -> uint { } } +/// Access a field, at a point when the value's case is known. +pub fn deref_ty(ccx: &CrateContext, r: &Repr) -> ty::t { + match *r { + CEnum(..) => { + ccx.sess.bug("deref of c-like enum") + } + Univariant(ref st, _) => { + st.fields[0] + } + General(_, ref cases) => { + assert!(cases.len() == 1); + cases[0].fields[0] + } + NullablePointer{ .. } => { + ccx.sess.bug("deref of nullable ptr") + } + } +} + /// Access a field, at a point when the value's case is known. pub fn trans_field_ptr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr, ix: uint) -> ValueRef { diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs index 11f217f3cb4b1..bae35f68ada56 100644 --- a/src/librustc/middle/trans/asm.rs +++ b/src/librustc/middle/trans/asm.rs @@ -18,8 +18,10 @@ use lib; use middle::trans::build::*; use middle::trans::callee; use middle::trans::common::*; -use middle::trans::expr::*; -use middle::trans::type_of::*; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; +use middle::trans::expr; +use middle::trans::type_of; use middle::trans::type_::Type; @@ -28,26 +30,23 @@ use syntax::ast; // Take an inline assembly expression and splat it out via LLVM pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm) -> &'a Block<'a> { + let fcx = bcx.fcx; let mut bcx = bcx; let mut constraints = ~[]; - let mut cleanups = ~[]; let mut output_types = ~[]; + let temp_scope = fcx.push_custom_cleanup_scope(); + // Prepare the output operands let outputs = ia.outputs.map(|&(c, out)| { constraints.push(c); - let out_datum = unpack_datum!(bcx, trans_to_datum(bcx, out)); - output_types.push(type_of(bcx.ccx(), out_datum.ty)); + let out_datum = unpack_datum!(bcx, expr::trans(bcx, out)); + output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty)); out_datum.val }); - for c in cleanups.iter() { - revoke_clean(bcx, *c); - } - cleanups.clear(); - // Now the input operands let inputs = ia.inputs.map(|&(c, input)| { constraints.push(c); @@ -56,14 +55,13 @@ pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm) callee::trans_arg_expr(bcx, expr_ty(bcx, input), input, - &mut cleanups, + cleanup::CustomScope(temp_scope), callee::DontAutorefArg) }) }); - for c in cleanups.iter() { - revoke_clean(bcx, *c); - } + // no failure occurred preparing operands, no need to cleanup + fcx.pop_custom_cleanup_scope(temp_scope); let mut constraints = constraints.connect(","); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 46f647cc1a64f..ca618250c27f7 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -37,17 +37,18 @@ use metadata::{csearch, encoder}; use middle::astencode; use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; use middle::lang_items::{MallocFnLangItem, ClosureExchangeMallocFnLangItem}; -use middle::lang_items::{EhPersonalityLangItem}; use middle::trans::_match; use middle::trans::adt; -use middle::trans::base; use middle::trans::build::*; use middle::trans::builder::{Builder, noname}; use middle::trans::callee; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::common::*; use middle::trans::consts; use middle::trans::controlflow; use middle::trans::datum; +// use middle::trans::datum::{Datum, Lvalue, Rvalue, ByRef, ByValue}; use middle::trans::debuginfo; use middle::trans::expr; use middle::trans::foreign; @@ -75,15 +76,12 @@ use std::hashmap::HashMap; use std::libc::c_uint; use std::vec; use std::local_data; -use syntax::ast::Name; use syntax::ast_map::{PathName, PathPrettyName, path_elem_to_str}; use syntax::ast_util::{local_def, is_local}; use syntax::attr; use syntax::codemap::Span; use syntax::parse::token; -use syntax::parse::token::{special_idents}; -use syntax::print::pprust::stmt_to_str; -use syntax::{ast, ast_util, codemap, ast_map}; +use syntax::{ast, ast_util, ast_map}; use syntax::attr::AttrMetaMethods; use syntax::abi::{X86, X86_64, Arm, Mips, Rust, RustIntrinsic, OsWin32}; use syntax::visit; @@ -757,7 +755,8 @@ pub fn iter_structural_ty<'r, } } ty::ty_enum(tid, ref substs) => { - let ccx = cx.ccx(); + let fcx = cx.fcx; + let ccx = fcx.ccx; let repr = adt::represent_type(ccx, t); let variants = ty::enum_variants(ccx.tcx, tid); @@ -773,16 +772,16 @@ pub fn iter_structural_ty<'r, } (_match::switch, Some(lldiscrim_a)) => { cx = f(cx, lldiscrim_a, ty::mk_int()); - let unr_cx = sub_block(cx, "enum-iter-unr"); + let unr_cx = fcx.new_temp_block("enum-iter-unr"); Unreachable(unr_cx); let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, n_variants); - let next_cx = sub_block(cx, "enum-iter-next"); + let next_cx = fcx.new_temp_block("enum-iter-next"); for variant in (*variants).iter() { let variant_cx = - sub_block(cx, ~"enum-iter-variant-" + - variant.disr_val.to_str()); + fcx.new_temp_block(~"enum-iter-variant-" + + variant.disr_val.to_str()); let variant_cx = iter_variant(variant_cx, repr, av, *variant, substs.tps, |x,y,z| f(x,y,z)); @@ -929,23 +928,27 @@ pub fn invoke<'a>( return (C_null(Type::i8()), bcx); } - match bcx.node_info { - None => debug!("invoke at ???"), - Some(node_info) => { + match bcx.opt_node_id { + None => { + debug!("invoke at ???"); + } + Some(id) => { debug!("invoke at {}", - bcx.sess().codemap.span_to_str(node_info.span)); + ast_map::node_id_to_str(bcx.tcx().items, + id, + token::get_ident_interner())); } } - if need_invoke(bcx) { + if bcx.fcx.needs_invoke() { unsafe { debug!("invoking {} at {}", llfn, bcx.llbb); for &llarg in llargs.iter() { debug!("arg: {}", llarg); } } - let normal_bcx = sub_block(bcx, "normal return"); - let landing_pad = get_landing_pad(bcx); + let normal_bcx = bcx.fcx.new_temp_block("normal-return"); + let landing_pad = bcx.fcx.get_landing_pad(); match call_info { Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), @@ -987,146 +990,9 @@ pub fn need_invoke(bcx: &Block) -> bool { return false; } - if have_cached_lpad(bcx) { - return true; - } - - // Walk the scopes to look for cleanups - let mut cur = bcx; - let mut cur_scope = cur.scope.get(); - loop { - cur_scope = match cur_scope { - Some(inf) => { - let cleanups = inf.cleanups.borrow(); - for cleanup in cleanups.get().iter() { - match *cleanup { - Clean(_, cleanup_type) | CleanTemp(_, _, cleanup_type) => { - if cleanup_type == normal_exit_and_unwind { - return true; - } - } - } - } - inf.parent - } - None => { - cur = match cur.parent { - Some(next) => next, - None => return false - }; - cur.scope.get() - } - } - } -} - -pub fn have_cached_lpad(bcx: &Block) -> bool { - let mut res = false; - in_lpad_scope_cx(bcx, |inf| { - match inf.landing_pad.get() { - Some(_) => res = true, - None => res = false - } - }); - return res; + bcx.fcx.needs_invoke() } -pub fn in_lpad_scope_cx<'a>(bcx: &'a Block<'a>, f: |si: &'a ScopeInfo<'a>|) { - let mut bcx = bcx; - let mut cur_scope = bcx.scope.get(); - loop { - cur_scope = match cur_scope { - Some(inf) => { - if !inf.empty_cleanups() || (inf.parent.is_none() && bcx.parent.is_none()) { - f(inf); - return; - } - inf.parent - } - None => { - bcx = block_parent(bcx); - bcx.scope.get() - } - } - } -} - -pub fn get_landing_pad<'a>(bcx: &'a Block<'a>) -> BasicBlockRef { - let _icx = push_ctxt("get_landing_pad"); - - let mut cached = None; - let mut pad_bcx = bcx; // Guaranteed to be set below - in_lpad_scope_cx(bcx, |inf| { - // If there is a valid landing pad still around, use it - match inf.landing_pad.get() { - Some(target) => cached = Some(target), - None => { - pad_bcx = lpad_block(bcx, "unwind"); - inf.landing_pad.set(Some(pad_bcx.llbb)); - } - } - }); - // Can't return from block above - match cached { Some(b) => return b, None => () } - // The landing pad return type (the type being propagated). Not sure what - // this represents but it's determined by the personality function and - // this is what the EH proposal example uses. - let llretty = Type::struct_([Type::i8p(), Type::i32()], false); - // The exception handling personality function. - let personality = callee::trans_fn_ref(bcx, - langcall(bcx, None, "", EhPersonalityLangItem), - 0).llfn; - // The only landing pad clause will be 'cleanup' - let llretval = LandingPad(pad_bcx, llretty, personality, 1u); - // The landing pad block is a cleanup - SetCleanup(pad_bcx, llretval); - - // We store the retval in a function-central alloca, so that calls to - // Resume can find it. - match bcx.fcx.personality.get() { - Some(addr) => Store(pad_bcx, llretval, addr), - None => { - let addr = alloca(pad_bcx, val_ty(llretval), ""); - bcx.fcx.personality.set(Some(addr)); - Store(pad_bcx, llretval, addr); - } - } - - // Unwind all parent scopes, and finish with a Resume instr - cleanup_and_leave(pad_bcx, None, None); - return pad_bcx.llbb; -} - -pub fn find_bcx_for_scope<'a>(bcx: &'a Block<'a>, scope_id: ast::NodeId) - -> &'a Block<'a> { - let mut bcx_sid = bcx; - let mut cur_scope = bcx_sid.scope.get(); - loop { - cur_scope = match cur_scope { - Some(inf) => { - match inf.node_info { - Some(NodeInfo { id, .. }) if id == scope_id => { - return bcx_sid - } - // FIXME(#6268, #6248) hacky cleanup for nested method calls - Some(NodeInfo { callee_id: Some(id), .. }) if id == scope_id => { - return bcx_sid - } - _ => inf.parent - } - } - None => { - bcx_sid = match bcx_sid.parent { - None => bcx.tcx().sess.bug(format!("no enclosing scope with id {}", scope_id)), - Some(bcx_par) => bcx_par - }; - bcx_sid.scope.get() - } - } - } -} - - pub fn do_spill(bcx: &Block, v: ValueRef, t: ty::t) -> ValueRef { if ty::type_is_bot(t) { return C_null(Type::i8p()); @@ -1181,139 +1047,7 @@ pub fn init_local<'a>(bcx: &'a Block<'a>, local: &ast::Local) } } - _match::store_local(bcx, local.pat, local.init) -} - -pub fn trans_stmt<'a>(cx: &'a Block<'a>, s: &ast::Stmt) -> &'a Block<'a> { - let _icx = push_ctxt("trans_stmt"); - debug!("trans_stmt({})", stmt_to_str(s, cx.tcx().sess.intr())); - - if cx.sess().asm_comments() { - add_span_comment(cx, s.span, stmt_to_str(s, cx.ccx().sess.intr())); - } - - let mut bcx = cx; - - match s.node { - ast::StmtExpr(e, _) | ast::StmtSemi(e, _) => { - bcx = expr::trans_into(cx, e, expr::Ignore); - } - ast::StmtDecl(d, _) => { - match d.node { - ast::DeclLocal(ref local) => { - bcx = init_local(bcx, *local); - if cx.sess().opts.extra_debuginfo { - debuginfo::create_local_var_metadata(bcx, *local); - } - } - ast::DeclItem(i) => trans_item(cx.fcx.ccx, i) - } - } - ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro") - } - - return bcx; -} - -// You probably don't want to use this one. See the -// next three functions instead. -pub fn new_block<'a>( - cx: &'a FunctionContext<'a>, - parent: Option<&'a Block<'a>>, - scope: Option<&'a ScopeInfo<'a>>, - is_lpad: bool, - name: &str, - opt_node_info: Option) - -> &'a Block<'a> { - unsafe { - let llbb = name.with_c_str(|buf| { - llvm::LLVMAppendBasicBlockInContext(cx.ccx.llcx, cx.llfn, buf) - }); - let bcx = Block::new(llbb, parent, is_lpad, opt_node_info, cx); - bcx.scope.set(scope); - for cx in parent.iter() { - if cx.unreachable.get() { - Unreachable(bcx); - break; - } - } - bcx - } -} - -pub fn simple_block_scope<'a>( - fcx: &'a FunctionContext<'a>, - parent: Option<&'a ScopeInfo<'a>>, - node_info: Option) - -> &'a ScopeInfo<'a> { - fcx.scope_info_arena.alloc(ScopeInfo { - parent: parent, - loop_break: None, - loop_label: None, - cleanups: RefCell::new(~[]), - cleanup_paths: RefCell::new(~[]), - landing_pad: Cell::new(None), - node_info: node_info, - }) -} - -// Use this when you're at the top block of a function or the like. -pub fn top_scope_block<'a>( - fcx: &'a FunctionContext<'a>, - opt_node_info: Option) - -> &'a Block<'a> { - new_block(fcx, - None, - Some(simple_block_scope(fcx, None, opt_node_info)), - false, - "function top level", - opt_node_info) -} - -pub fn scope_block<'a>( - bcx: &'a Block<'a>, - opt_node_info: Option, - n: &str) - -> &'a Block<'a> { - new_block(bcx.fcx, - Some(bcx), - Some(simple_block_scope(bcx.fcx, None, opt_node_info)), - bcx.is_lpad, - n, - opt_node_info) -} - -pub fn loop_scope_block<'a>( - bcx: &'a Block<'a>, - loop_break: &'a Block<'a>, - loop_label: Option, - n: &str, - opt_node_info: Option) - -> &'a Block<'a> { - new_block(bcx.fcx, - Some(bcx), - Some(bcx.fcx.scope_info_arena.alloc(ScopeInfo { - parent: None, - loop_break: Some(loop_break), - loop_label: loop_label, - cleanups: RefCell::new(~[]), - cleanup_paths: RefCell::new(~[]), - landing_pad: Cell::new(None), - node_info: opt_node_info, - })), - bcx.is_lpad, - n, - opt_node_info) -} - -// Use this when creating a block for the inside of a landing pad. -pub fn lpad_block<'a>(bcx: &'a Block<'a>, n: &str) -> &'a Block<'a> { - new_block(bcx.fcx, Some(bcx), None, true, n, None) -} - -// Use this when you're making a general CFG BB within a scope. -pub fn sub_block<'a>(bcx: &'a Block<'a>, n: &str) -> &'a Block<'a> { - new_block(bcx.fcx, Some(bcx), None, bcx.is_lpad, n, None) + _match::store_local(bcx, local) } pub fn raw_block<'a>( @@ -1321,236 +1055,7 @@ pub fn raw_block<'a>( is_lpad: bool, llbb: BasicBlockRef) -> &'a Block<'a> { - Block::new(llbb, None, is_lpad, None, fcx) -} - - -// trans_block_cleanups: Go through all the cleanups attached to this -// block and execute them. -// -// When translating a block that introduces new variables during its scope, we -// need to make sure those variables go out of scope when the block ends. We -// do that by running a 'cleanup' function for each variable. -// trans_block_cleanups runs all the cleanup functions for the block. -pub fn trans_block_cleanups<'a>(bcx: &'a Block<'a>, cleanups: ~[cleanup]) - -> &'a Block<'a> { - trans_block_cleanups_(bcx, cleanups, false) -} - -pub fn trans_block_cleanups_<'a>( - bcx: &'a Block<'a>, - cleanups: &[cleanup], - is_lpad: bool) - -> &'a Block<'a> { - let _icx = push_ctxt("trans_block_cleanups"); - // NB: Don't short-circuit even if this block is unreachable because - // GC-based cleanup needs to the see that the roots are live. - let no_lpads = bcx.ccx().sess.no_landing_pads(); - if bcx.unreachable.get() && !no_lpads { - return bcx - } - let mut bcx = bcx; - for cu in cleanups.rev_iter() { - match *cu { - Clean(cfn, cleanup_type) | CleanTemp(_, cfn, cleanup_type) => { - // Some types don't need to be cleaned up during - // landing pads because they can be freed en mass later - if cleanup_type == normal_exit_and_unwind || !is_lpad { - bcx = cfn.clean(bcx); - } - } - } - } - return bcx; -} - -// In the last argument, Some(block) mean jump to this block, and none means -// this is a landing pad and leaving should be accomplished with a resume -// instruction. -pub fn cleanup_and_leave<'a>( - bcx: &'a Block<'a>, - upto: Option, - leave: Option) { - let _icx = push_ctxt("cleanup_and_leave"); - let mut cur = bcx; - let mut bcx = bcx; - let is_lpad = leave == None; - loop { - debug!("cleanup_and_leave: leaving {}", cur.to_str()); - - let mut cur_scope = cur.scope.get(); - loop { - cur_scope = match cur_scope { - Some (inf) if !inf.empty_cleanups() => { - let (sub_cx, dest, inf_cleanups) = { - let inf = &*inf; - let mut skip = 0; - let mut dest = None; - { - let cleanup_paths = inf.cleanup_paths.borrow(); - let r = cleanup_paths.get() - .rev_iter() - .find(|cp| { - cp.target == leave - }); - for cp in r.iter() { - let cleanups = inf.cleanups.borrow(); - if cp.size == cleanups.get().len() { - Br(bcx, cp.dest); - return; - } - - skip = cp.size; - dest = Some(cp.dest); - } - } - let sub_cx = sub_block(bcx, "cleanup"); - Br(bcx, sub_cx.llbb); - let cleanups = inf.cleanups.borrow(); - let mut cleanup_paths = inf.cleanup_paths - .borrow_mut(); - cleanup_paths.get().push(cleanup_path { - target: leave, - size: cleanups.get().len(), - dest: sub_cx.llbb - }); - (sub_cx, dest, cleanups.get().tailn(skip).to_owned()) - }; - bcx = trans_block_cleanups_(sub_cx, - inf_cleanups, - is_lpad); - for &dest in dest.iter() { - Br(bcx, dest); - return; - } - inf.parent - } - Some(inf) => inf.parent, - None => break - } - } - - match upto { - Some(bb) => { if cur.llbb == bb { break; } } - _ => () - } - cur = match cur.parent { - Some(next) => next, - None => { assert!(upto.is_none()); break; } - }; - } - match leave { - Some(target) => Br(bcx, target), - None => { - let ll_load = Load(bcx, bcx.fcx.personality.get().unwrap()); - Resume(bcx, ll_load); - } - } -} - -pub fn cleanup_block<'a>(bcx: &'a Block<'a>, upto: Option) - -> &'a Block<'a> { - let _icx = push_ctxt("cleanup_block"); - let mut cur = bcx; - let mut bcx = bcx; - loop { - debug!("cleanup_block: {}", cur.to_str()); - - let mut cur_scope = cur.scope.get(); - loop { - cur_scope = match cur_scope { - Some(inf) => { - let cleanups = inf.cleanups.borrow(); - bcx = trans_block_cleanups_(bcx, - cleanups.get().to_owned(), - false); - inf.parent - } - None => break - } - } - - match upto { - Some(bb) => { if cur.llbb == bb { break; } } - _ => () - } - cur = match cur.parent { - Some(next) => next, - None => { assert!(upto.is_none()); break; } - }; - } - bcx -} - -pub fn cleanup_and_Br<'a>( - bcx: &'a Block<'a>, - upto: &'a Block<'a>, - target: BasicBlockRef) { - let _icx = push_ctxt("cleanup_and_Br"); - cleanup_and_leave(bcx, Some(upto.llbb), Some(target)); -} - -pub fn leave_block<'a>(bcx: &'a Block<'a>, out_of: &'a Block<'a>) - -> &'a Block<'a> { - let _icx = push_ctxt("leave_block"); - let next_cx = sub_block(block_parent(out_of), "next"); - if bcx.unreachable.get() { - Unreachable(next_cx); - } - cleanup_and_Br(bcx, out_of, next_cx.llbb); - next_cx -} - -pub fn with_scope<'a>( - bcx: &'a Block<'a>, - opt_node_info: Option, - name: &str, - f: |&'a Block<'a>| -> &'a Block<'a>) - -> &'a Block<'a> { - let _icx = push_ctxt("with_scope"); - - debug!("with_scope(bcx={}, opt_node_info={:?}, name={})", - bcx.to_str(), opt_node_info, name); - let _indenter = indenter(); - - let scope = simple_block_scope(bcx.fcx, bcx.scope.get(), opt_node_info); - bcx.scope.set(Some(scope)); - let ret = f(bcx); - let ret = trans_block_cleanups_(ret, scope.cleanups.get(), false); - bcx.scope.set(scope.parent); - ret -} - -pub fn with_scope_result<'a>( - bcx: &'a Block<'a>, - opt_node_info: Option, - _name: &str, - f: |&'a Block<'a>| -> Result<'a>) - -> Result<'a> { - let _icx = push_ctxt("with_scope_result"); - - let scope = simple_block_scope(bcx.fcx, bcx.scope.get(), opt_node_info); - bcx.scope.set(Some(scope)); - let Result { bcx: out_bcx, val } = f(bcx); - let out_bcx = trans_block_cleanups_(out_bcx, scope.cleanups.get(), false); - bcx.scope.set(scope.parent); - - rslt(out_bcx, val) -} - -pub fn with_scope_datumblock<'a>( - bcx: &'a Block<'a>, - opt_node_info: Option, - name: &str, - f: |&'a Block| -> datum::DatumBlock<'a>) - -> datum::DatumBlock<'a> { - use middle::trans::datum::DatumBlock; - - let _icx = push_ctxt("with_scope_result"); - let scope_cx = scope_block(bcx, opt_node_info, name); - Br(bcx, scope_cx.llbb); - let DatumBlock {bcx, datum} = f(scope_cx); - DatumBlock {bcx: leave_block(bcx, scope_cx), datum: datum} + Block::new(llbb, is_lpad, None, fcx) } pub fn block_locals(b: &ast::Block, it: |@ast::Local|) { @@ -1573,8 +1078,9 @@ pub fn with_cond<'a>( f: |&'a Block<'a>| -> &'a Block<'a>) -> &'a Block<'a> { let _icx = push_ctxt("with_cond"); - let next_cx = base::sub_block(bcx, "next"); - let cond_cx = base::sub_block(bcx, "cond"); + let fcx = bcx.fcx; + let next_cx = fcx.new_temp_block("next"); + let cond_cx = fcx.new_temp_block("cond"); CondBr(bcx, val, cond_cx.llbb, next_cx.llbb); let after_cx = f(cond_cx); if !after_cx.terminated.get() { @@ -1725,24 +1231,25 @@ pub fn make_return_pointer(fcx: &FunctionContext, output_type: ty::t) // NB: must keep 4 fns in sync: // // - type_of_fn -// - create_llargs_for_fn_args. +// - create_datums_for_fn_args. // - new_fn_ctxt // - trans_args // // Be warned! You must call `init_function` before doing anything with the // returned function context. -pub fn new_fn_ctxt_w_id(ccx: @CrateContext, - path: ast_map::Path, - llfndecl: ValueRef, - id: ast::NodeId, - output_type: ty::t, - param_substs: Option<@param_substs>, - sp: Option) - -> FunctionContext { +pub fn new_fn_ctxt_detailed(ccx: @CrateContext, + path: ast_map::Path, + llfndecl: ValueRef, + id: ast::NodeId, + output_type: ty::t, + param_substs: Option<@param_substs>, + sp: Option) + -> FunctionContext { for p in param_substs.iter() { p.validate(); } - debug!("new_fn_ctxt_w_id(path={}, id={:?}, \ - param_substs={})", + debug!("new_fn_ctxt_detailed(path={}, + id={:?}, \ + param_substs={})", path_str(ccx.sess, path), id, param_substs.repr(ccx.tcx)); @@ -1776,9 +1283,9 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext, span: sp, path: path, block_arena: TypedArena::new(), - scope_info_arena: TypedArena::new(), ccx: ccx, debug_context: debug_context, + scopes: RefCell::new(~[]) }; fcx.llenv.set(unsafe { llvm::LLVMGetParam(llfndecl, fcx.env_arg_pos() as c_uint) @@ -1793,10 +1300,9 @@ pub fn init_function<'a>( fcx: &'a FunctionContext<'a>, skip_retptr: bool, output_type: ty::t, - param_substs: Option<@param_substs>, - opt_node_info: Option) { + param_substs: Option<@param_substs>) { unsafe { - let entry_bcx = top_scope_block(fcx, opt_node_info); + let entry_bcx = fcx.new_temp_block("entry-block"); Load(entry_bcx, C_null(Type::i8p())); fcx.entry_bcx.set(Some(entry_bcx)); @@ -1835,108 +1341,71 @@ pub fn new_fn_ctxt(ccx: @CrateContext, -> FunctionContext { // FIXME(#11385): Do not call `init_function` here; it will typecheck // but segfault. - new_fn_ctxt_w_id(ccx, path, llfndecl, -1, output_type, None, sp) + new_fn_ctxt_detailed(ccx, path, llfndecl, -1, output_type, None, sp) } // NB: must keep 4 fns in sync: // // - type_of_fn -// - create_llargs_for_fn_args. +// - create_datums_for_fn_args. // - new_fn_ctxt // - trans_args -// create_llargs_for_fn_args: Creates a mapping from incoming arguments to -// allocas created for them. -// -// When we translate a function, we need to map its incoming arguments to the -// spaces that have been created for them (by code in the llallocas field of -// the function's fn_ctxt). create_llargs_for_fn_args populates the llargs -// field of the fn_ctxt with -fn create_llargs_for_fn_args(cx: &FunctionContext, +fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue { + use middle::trans::datum::{ByRef, ByValue}; + + datum::Rvalue { + mode: if arg_is_indirect(cx.ccx, t) { ByRef } else { ByValue } + } +} + +// work around bizarre resolve errors +type RvalueDatum = datum::Datum; +type LvalueDatum = datum::Datum; + +// create_datums_for_fn_args: creates rvalue datums for `self` and each of the +// incoming function arguments. These will later be stored into +// appropriate lvalue datums. +fn create_datums_for_fn_args(cx: &FunctionContext, self_arg: Option, arg_tys: &[ty::t]) - -> ~[datum::Datum] { - let _icx = push_ctxt("create_llargs_for_fn_args"); - - match self_arg { - Some(t) => { - cx.llself.set(Some(datum::Datum { - val: cx.llenv.get(), - ty: t, - mode: if arg_is_indirect(cx.ccx, t) { - datum::ByRef(datum::ZeroMem) - } else { - datum::ByValue - } - })); - } - None => {} - } + -> (Option, ~[RvalueDatum]) { + let _icx = push_ctxt("create_datums_for_fn_args"); + + let self_datum = self_arg.map( + |t| datum::Datum(cx.llenv.get(), t, arg_kind(cx, t))); // Return an array wrapping the ValueRefs that we get from // llvm::LLVMGetParam for each argument into datums. - arg_tys.iter().enumerate().map(|(i, &arg_ty)| { - let llarg = unsafe { llvm::LLVMGetParam(cx.llfn, cx.arg_pos(i) as c_uint) }; - datum::Datum { - val: llarg, - ty: arg_ty, - mode: if arg_is_indirect(cx.ccx, arg_ty) { - datum::ByRef(datum::ZeroMem) - } else { - datum::ByValue - } - } - }).collect() + let arg_datums = arg_tys.iter().enumerate().map(|(i, &arg_ty)| { + let llarg = unsafe { + llvm::LLVMGetParam(cx.llfn, cx.arg_pos(i) as c_uint) + }; + datum::Datum(llarg, arg_ty, arg_kind(cx, arg_ty)) + }).collect(); + + (self_datum, arg_datums) } fn copy_args_to_allocas<'a>(fcx: &FunctionContext<'a>, + arg_scope: cleanup::CustomScopeIndex, bcx: &'a Block<'a>, args: &[ast::Arg], - method: Option<&ast::Method>, - raw_llargs: &[datum::Datum]) + self_datum: Option, + arg_datums: ~[RvalueDatum]) -> &'a Block<'a> { - debug!("copy_args_to_allocas: args=[{}]", - raw_llargs.map(|d| d.to_str(fcx.ccx)).connect(", ")); + debug!("copy_args_to_allocas"); let _icx = push_ctxt("copy_args_to_allocas"); let mut bcx = bcx; - match fcx.llself.get() { - Some(slf) => { - let needs_indirection = if slf.mode.is_by_value() { - // FIXME(eddyb) #11445 Always needs indirection because of cleanup. - if true { - true - } else { - match method { - Some(method) => { - match method.explicit_self.node { - ast::SelfValue(ast::MutMutable) => true, - _ => false - } - } - None => true - } - } - } else { - false - }; - let slf = if needs_indirection { - // HACK(eddyb) this is just slf.to_ref_datum(bcx) with a named alloca. - let alloc = alloc_ty(bcx, slf.ty, "__self"); - Store(bcx, slf.val, alloc); - datum::Datum { - val: alloc, - ty: slf.ty, - mode: datum::ByRef(datum::ZeroMem) - } - } else { - slf - }; - + let arg_scope_id = cleanup::CustomScope(arg_scope); + match self_datum { + Some(slf_rv) => { + let slf = unpack_datum!( + bcx, slf_rv.to_lvalue_datum_in_scope(bcx, "__self", + arg_scope_id)); fcx.llself.set(Some(slf)); - slf.add_clean(bcx); - if fcx.ccx.sess.opts.extra_debuginfo { debuginfo::create_self_argument_metadata(bcx, slf.ty, slf.val); } @@ -1944,24 +1413,7 @@ fn copy_args_to_allocas<'a>(fcx: &FunctionContext<'a>, _ => {} } - for (i, &arg) in raw_llargs.iter().enumerate() { - let needs_indirection = if arg.mode.is_by_value() { - if fcx.ccx.sess.opts.extra_debuginfo { - true - } else { - // FIXME(eddyb) #11445 Always needs indirection because of cleanup. - if true { - true - } else { - match args[i].pat.node { - ast::PatIdent(ast::BindByValue(ast::MutMutable), _, _) => true, - _ => false - } - } - } - } else { - false - }; + for (i, arg_datum) in arg_datums.move_iter().enumerate() { // For certain mode/type combinations, the raw llarg values are passed // by value. However, within the fn body itself, we want to always // have all locals and arguments be by-ref so that we can cancel the @@ -1969,25 +1421,8 @@ fn copy_args_to_allocas<'a>(fcx: &FunctionContext<'a>, // the argument would be passed by value, we store it into an alloca. // This alloca should be optimized away by LLVM's mem-to-reg pass in // the event it's not truly needed. - let arg = if needs_indirection { - // HACK(eddyb) this is just arg.to_ref_datum(bcx) with a named alloca. - let alloc = match args[i].pat.node { - ast::PatIdent(_, ref path, _) => { - let name = ast_util::path_to_ident(path).name; - alloc_ty(bcx, arg.ty, token::interner_get(name)) - } - _ => alloc_ty(bcx, arg.ty, "__arg") - }; - Store(bcx, arg.val, alloc); - datum::Datum { - val: alloc, - ty: arg.ty, - mode: datum::ByRef(datum::ZeroMem) - } - } else { - arg - }; - bcx = _match::store_arg(bcx, args[i].pat, arg); + + bcx = _match::store_arg(bcx, args[i].pat, arg_datum, arg_scope_id); if fcx.ccx.sess.opts.extra_debuginfo { debuginfo::create_argument_metadata(bcx, &args[i]); @@ -2056,7 +1491,6 @@ pub fn trans_closure(ccx: @CrateContext, self_arg: Option, param_substs: Option<@param_substs>, id: ast::NodeId, - method: Option<&ast::Method>, _attributes: &[ast::Attribute], output_type: ty::t, maybe_load_env: |&FunctionContext|) { @@ -2068,14 +1502,17 @@ pub fn trans_closure(ccx: @CrateContext, debug!("trans_closure(..., param_substs={})", param_substs.repr(ccx.tcx)); - let fcx = new_fn_ctxt_w_id(ccx, - path, - llfndecl, - id, - output_type, - param_substs, - Some(body.span)); - init_function(&fcx, false, output_type, param_substs, body.info()); + let fcx = new_fn_ctxt_detailed(ccx, + path, + llfndecl, + id, + output_type, + param_substs, + Some(body.span)); + init_function(&fcx, false, output_type, param_substs); + + // cleanup scope for the incoming arguments + let arg_scope = fcx.push_custom_cleanup_scope(); // Create the first basic block in the function and keep a handle on it to // pass to finish_fn later. @@ -2085,9 +1522,11 @@ pub fn trans_closure(ccx: @CrateContext, // Set up arguments to the function. let arg_tys = ty::ty_fn_args(node_id_type(bcx, id)); - let raw_llargs = create_llargs_for_fn_args(&fcx, self_arg, arg_tys); + let (self_datum, arg_datums) = + create_datums_for_fn_args(&fcx, self_arg, arg_tys); - bcx = copy_args_to_allocas(&fcx, bcx, decl.inputs, method, raw_llargs); + bcx = copy_args_to_allocas(&fcx, arg_scope, bcx, + decl.inputs, self_datum, arg_datums); maybe_load_env(&fcx); @@ -2108,8 +1547,15 @@ pub fn trans_closure(ccx: @CrateContext, } match fcx.llreturn.get() { - Some(llreturn) => cleanup_and_Br(bcx, bcx_top, llreturn), - None => bcx = cleanup_block(bcx, Some(bcx_top.llbb)) + Some(_) => { + Br(bcx, fcx.return_exit_block()); + fcx.pop_custom_cleanup_scope(arg_scope); + } + None => { + // Microoptimization writ large: avoid creating a separate + // llreturn basic block + bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope); + } }; // Put return block after all other blocks. @@ -2135,9 +1581,7 @@ pub fn trans_fn(ccx: @CrateContext, self_arg: Option, param_substs: Option<@param_substs>, id: ast::NodeId, - method: Option<&ast::Method>, attrs: &[ast::Attribute]) { - let the_path_str = path_str(ccx.sess, path); let _s = StatRecorder::new(ccx, the_path_str); debug!("trans_fn(self_arg={:?}, param_substs={})", @@ -2153,44 +1597,15 @@ pub fn trans_fn(ccx: @CrateContext, self_arg, param_substs, id, - method, attrs, output_type, |_fcx| { }); } -fn insert_synthetic_type_entries(bcx: &Block, - fn_args: &[ast::Arg], - arg_tys: &[ty::t]) { - /*! - * For tuple-like structs and enum-variants, we generate - * synthetic AST nodes for the arguments. These have no types - * in the type table and no entries in the moves table, - * so the code in `copy_args_to_allocas` and `bind_irrefutable_pat` - * gets upset. This hack of a function bridges the gap by inserting types. - * - * This feels horrible. I think we should just have a special path - * for these functions and not try to use the generic code, but - * that's not the problem I'm trying to solve right now. - nmatsakis - */ - - let tcx = bcx.tcx(); - for i in range(0u, fn_args.len()) { - debug!("setting type of argument {} (pat node {}) to {}", - i, fn_args[i].pat.id, bcx.ty_to_str(arg_tys[i])); - - let pat_id = fn_args[i].pat.id; - let arg_ty = arg_tys[i]; - - let mut node_types = tcx.node_types.borrow_mut(); - node_types.get().insert(pat_id as uint, arg_ty); - } -} - pub fn trans_enum_variant(ccx: @CrateContext, _enum_id: ast::NodeId, variant: &ast::Variant, - args: &[ast::VariantArg], + _args: &[ast::VariantArg], disr: ty::Disr, param_substs: Option<@param_substs>, llfndecl: ValueRef) { @@ -2199,14 +1614,13 @@ pub fn trans_enum_variant(ccx: @CrateContext, trans_enum_variant_or_tuple_like_struct( ccx, variant.node.id, - args, disr, param_substs, llfndecl); } pub fn trans_tuple_struct(ccx: @CrateContext, - fields: &[ast::StructField], + _fields: &[ast::StructField], ctor_id: ast::NodeId, param_substs: Option<@param_substs>, llfndecl: ValueRef) { @@ -2215,46 +1629,16 @@ pub fn trans_tuple_struct(ccx: @CrateContext, trans_enum_variant_or_tuple_like_struct( ccx, ctor_id, - fields, 0, param_substs, llfndecl); } -trait IdAndTy { - fn id(&self) -> ast::NodeId; - fn ty(&self) -> ast::P; -} - -impl IdAndTy for ast::VariantArg { - fn id(&self) -> ast::NodeId { self.id } - fn ty(&self) -> ast::P { self.ty } -} - -impl IdAndTy for ast::StructField { - fn id(&self) -> ast::NodeId { self.node.id } - fn ty(&self) -> ast::P { self.node.ty } -} - -fn trans_enum_variant_or_tuple_like_struct( - ccx: @CrateContext, - ctor_id: ast::NodeId, - args: &[A], - disr: ty::Disr, - param_substs: Option<@param_substs>, - llfndecl: ValueRef) { - // Translate variant arguments to function arguments. - let fn_args = args.map(|varg| { - ast::Arg { - ty: varg.ty(), - pat: ast_util::ident_to_pat( - ccx.tcx.sess.next_node_id(), - codemap::DUMMY_SP, - special_idents::arg), - id: varg.id(), - } - }); - +fn trans_enum_variant_or_tuple_like_struct(ccx: @CrateContext, + ctor_id: ast::NodeId, + disr: ty::Disr, + param_substs: Option<@param_substs>, + llfndecl: ValueRef) { let no_substs: &[ty::t] = []; let ty_param_substs = match param_substs { Some(ref substs) => { @@ -2280,38 +1664,32 @@ fn trans_enum_variant_or_tuple_like_struct( ty_to_str(ccx.tcx, ctor_ty))) }; - let fcx = new_fn_ctxt_w_id(ccx, - ~[], - llfndecl, - ctor_id, - result_ty, - param_substs, - None); - init_function(&fcx, false, result_ty, param_substs, None); + let fcx = new_fn_ctxt_detailed(ccx, + ~[], + llfndecl, + ctor_id, + result_ty, + param_substs, + None); + init_function(&fcx, false, result_ty, param_substs); let arg_tys = ty::ty_fn_args(ctor_ty); - let raw_llargs = create_llargs_for_fn_args(&fcx, None, arg_tys); + let (_, arg_datums) = create_datums_for_fn_args(&fcx, None, arg_tys); let bcx = fcx.entry_bcx.get().unwrap(); - insert_synthetic_type_entries(bcx, fn_args, arg_tys); - let bcx = copy_args_to_allocas(&fcx, bcx, fn_args, None, raw_llargs); - let repr = adt::represent_type(ccx, result_ty); adt::trans_start_init(bcx, repr, fcx.llretptr.get().unwrap(), disr); - for (i, fn_arg) in fn_args.iter().enumerate() { + for (i, arg_datum) in arg_datums.move_iter().enumerate() { let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr.get().unwrap(), disr, i); - let llarg = { - let llargs = fcx.llargs.borrow(); - llargs.get().get_copy(&fn_arg.pat.id) - }; - llarg.move_to(bcx, datum::INIT, lldestptr); + arg_datum.store_to(bcx, lldestptr); } + finish_fn(&fcx, bcx); } @@ -2380,7 +1758,6 @@ pub fn trans_item(ccx: @CrateContext, item: &ast::Item) { None, None, item.id, - None, item.attrs); } else { // Be sure to travel more than just one layer deep to catch nested diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index 0dd0d1589ff58..44410ae24b9e4 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -24,6 +24,7 @@ use std::cast; use std::libc::{c_uint, c_ulonglong, c_char}; pub fn terminate(cx: &Block, _: &str) { + debug!("terminate({})", cx.to_str()); cx.terminated.set(true); } @@ -315,12 +316,16 @@ pub fn ArrayMalloc(cx: &Block, Ty: Type, Val: ValueRef) -> ValueRef { pub fn Alloca(cx: &Block, Ty: Type, name: &str) -> ValueRef { unsafe { if cx.unreachable.get() { return llvm::LLVMGetUndef(Ty.ptr_to().to_ref()); } - let b = cx.fcx.ccx.builder(); - b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); - b.alloca(Ty, name) + AllocaFcx(cx.fcx, Ty, name) } } +pub fn AllocaFcx(fcx: &FunctionContext, Ty: Type, name: &str) -> ValueRef { + let b = fcx.ccx.builder(); + b.position_before(fcx.alloca_insert_pt.get().unwrap()); + b.alloca(Ty, name) +} + pub fn ArrayAlloca(cx: &Block, Ty: Type, Val: ValueRef) -> ValueRef { unsafe { if cx.unreachable.get() { return llvm::LLVMGetUndef(Ty.ptr_to().to_ref()); } diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index c1451a8fe1e12..310ae95ea2a08 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -27,6 +27,8 @@ use middle::trans::base; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::common; use middle::trans::common::*; use middle::trans::datum::*; @@ -60,11 +62,10 @@ pub struct FnData { pub struct MethodData { llfn: ValueRef, llself: ValueRef, - temp_cleanup: Option } pub enum CalleeData { - Closure(Datum), + Closure(Datum), Fn(FnData), Method(MethodData) } @@ -74,7 +75,7 @@ pub struct Callee<'a> { data: CalleeData } -pub fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { +fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { let _icx = push_ctxt("trans_callee"); debug!("callee::trans(expr={})", expr.repr(bcx.tcx())); @@ -90,13 +91,15 @@ pub fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { return datum_callee(bcx, expr); fn datum_callee<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { - let DatumBlock {bcx, datum} = expr::trans_to_datum(bcx, expr); + let DatumBlock {bcx: mut bcx, datum} = expr::trans(bcx, expr); match ty::get(datum.ty).sty { ty::ty_bare_fn(..) => { - let llval = datum.to_appropriate_llval(bcx); + let llval = datum.to_llscalarish(bcx); return Callee {bcx: bcx, data: Fn(FnData {llfn: llval})}; } ty::ty_closure(..) => { + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "callee", expr.id)); return Callee {bcx: bcx, data: Closure(datum)}; } _ => { @@ -458,10 +461,10 @@ pub fn trans_call<'a>( -> &'a Block<'a> { let _icx = push_ctxt("trans_call"); trans_call_inner(in_cx, - call_ex.info(), + Some(common::expr_info(call_ex)), expr_ty(in_cx, f), node_id_type(in_cx, id), - |cx| trans(cx, f), + |cx, _| trans(cx, f), args, Some(dest), DontAutorefArg).bcx @@ -481,10 +484,10 @@ pub fn trans_method_call<'a>( rcvr.repr(in_cx.tcx())); trans_call_inner( in_cx, - call_ex.info(), + Some(common::expr_info(call_ex)), node_id_type(in_cx, callee_id), expr_ty(in_cx, call_ex), - |cx| { + |cx, arg_cleanup_scope| { let origin_opt = { let mut method_map = cx.ccx().maps.method_map.borrow_mut(); method_map.get().find_copy(&call_ex.id) @@ -495,7 +498,11 @@ pub fn trans_method_call<'a>( call_ex.repr(in_cx.tcx()), origin.repr(in_cx.tcx())); - meth::trans_method_callee(cx, callee_id, rcvr, origin) + meth::trans_method_callee(cx, + callee_id, + rcvr, + origin, + arg_cleanup_scope) } None => { cx.tcx().sess.span_bug(call_ex.span, "method call expr wasn't in method map") @@ -523,7 +530,7 @@ pub fn trans_lang_call<'a>( None, fty, rty, - |bcx| { + |bcx, _| { trans_fn_ref_with_vtables_to_callee(bcx, did, 0, @@ -551,8 +558,11 @@ pub fn trans_lang_call_with_type_params<'a>( let rty = ty::ty_fn_ret(fty); return callee::trans_call_inner( - bcx, None, fty, rty, - |bcx| { + bcx, + None, + fty, + rty, + |bcx, _| { let callee = trans_fn_ref_with_vtables_to_callee(bcx, did, 0, type_params, @@ -577,11 +587,13 @@ pub fn trans_lang_call_with_type_params<'a>( } pub fn trans_call_inner<'a>( - in_cx: &'a Block<'a>, + bcx: &'a Block<'a>, call_info: Option, callee_ty: ty::t, ret_ty: ty::t, - get_callee: |&'a Block<'a>| -> Callee<'a>, + get_callee: |bcx: &'a Block<'a>, + arg_cleanup_scope: cleanup::ScopeId| + -> Callee<'a>, args: CallArgs, dest: Option, autoref_arg: AutorefArg) @@ -593,171 +605,182 @@ pub fn trans_call_inner<'a>( * this into two functions seems like a good idea). * * In particular, for lang items, it is invoked with a dest of - * None, and + * None, and in that case the return value contains the result of + * the fn. The lang item must not return a structural type or else + * all heck breaks loose. + * + * For non-lang items, `dest` is always Some, and hence the result + * is written into memory somewhere. Nonetheless we return the + * actual return value of the function. */ + // Introduce a temporary cleanup scope that will contain cleanups + // for the arguments while they are being evaluated. The purpose + // this cleanup is to ensure that, should a failure occur while + // evaluating argument N, the values for arguments 0...N-1 are all + // cleaned up. If no failure occurs, the values are handed off to + // the callee, and hence none of the cleanups in this temporary + // scope will ever execute. + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let tcx = ccx.tcx; + let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); - base::with_scope_result(in_cx, call_info, "call", |cx| { - let callee = get_callee(cx); - let mut bcx = callee.bcx; - let ccx = cx.ccx(); + let callee = get_callee(bcx, cleanup::CustomScope(arg_cleanup_scope)); + let mut bcx = callee.bcx; - let (llfn, llenv) = unsafe { - match callee.data { - Fn(d) => { - (d.llfn, llvm::LLVMGetUndef(Type::opaque_box(ccx).ptr_to().to_ref())) - } - Method(d) => { - // Weird but true: we pass self in the *environment* slot! - (d.llfn, d.llself) - } - Closure(d) => { - // Closures are represented as (llfn, llclosure) pair: - // load the requisite values out. - let pair = d.to_ref_llval(bcx); - let llfn = GEPi(bcx, pair, [0u, abi::fn_field_code]); - let llfn = Load(bcx, llfn); - let llenv = GEPi(bcx, pair, [0u, abi::fn_field_box]); - let llenv = Load(bcx, llenv); - (llfn, llenv) - } + let (llfn, llenv) = unsafe { + match callee.data { + Fn(d) => { + (d.llfn, llvm::LLVMGetUndef(Type::opaque_box(ccx).ptr_to().to_ref())) } - }; - - let abi = match ty::get(callee_ty).sty { - ty::ty_bare_fn(ref f) => f.abis, - _ => AbiSet::Rust() - }; - let is_rust_fn = - abi.is_rust() || - abi.is_intrinsic(); - - // Generate a location to store the result. If the user does - // not care about the result, just make a stack slot. - let opt_llretslot = match dest { - None => { - assert!(!type_of::return_uses_outptr(in_cx.ccx(), ret_ty)); - None + Method(d) => { + // Weird but true: we pass self in the *environment* slot! + (d.llfn, d.llself) } - Some(expr::SaveIn(dst)) => Some(dst), - Some(expr::Ignore) => { - if !ty::type_is_voidish(in_cx.tcx(), ret_ty) { - Some(alloc_ty(bcx, ret_ty, "__llret")) - } else { - unsafe { - Some(llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())) - } + Closure(d) => { + // Closures are represented as (llfn, llclosure) pair: + // load the requisite values out. + let pair = d.to_llref(); + let llfn = GEPi(bcx, pair, [0u, abi::fn_field_code]); + let llfn = Load(bcx, llfn); + let llenv = GEPi(bcx, pair, [0u, abi::fn_field_box]); + let llenv = Load(bcx, llenv); + (llfn, llenv) + } + } + }; + + let abi = match ty::get(callee_ty).sty { + ty::ty_bare_fn(ref f) => f.abis, + _ => AbiSet::Rust() + }; + let is_rust_fn = + abi.is_rust() || + abi.is_intrinsic(); + + // Generate a location to store the result. If the user does + // not care about the result, just make a stack slot. + let opt_llretslot = match dest { + None => { + assert!(!type_of::return_uses_outptr(ccx, ret_ty)); + None + } + Some(expr::SaveIn(dst)) => Some(dst), + Some(expr::Ignore) => { + if !ty::type_is_voidish(tcx, ret_ty) { + Some(alloc_ty(bcx, ret_ty, "__llret")) + } else { + unsafe { + Some(llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())) } } - }; + } + }; - let mut llresult = unsafe { - llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref()) - }; + let mut llresult = unsafe { + llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref()) + }; - // The code below invokes the function, using either the Rust - // conventions (if it is a rust fn) or the native conventions - // (otherwise). The important part is that, when all is sad - // and done, either the return value of the function will have been - // written in opt_llretslot (if it is Some) or `llresult` will be - // set appropriately (otherwise). - if is_rust_fn { - let mut llargs = ~[]; - - // Push the out-pointer if we use an out-pointer for this - // return type, otherwise push "undef". - if type_of::return_uses_outptr(in_cx.ccx(), ret_ty) { - llargs.push(opt_llretslot.unwrap()); - } + // The code below invokes the function, using either the Rust + // conventions (if it is a rust fn) or the native conventions + // (otherwise). The important part is that, when all is sad + // and done, either the return value of the function will have been + // written in opt_llretslot (if it is Some) or `llresult` will be + // set appropriately (otherwise). + if is_rust_fn { + let mut llargs = ~[]; + + // Push the out-pointer if we use an out-pointer for this + // return type, otherwise push "undef". + if type_of::return_uses_outptr(ccx, ret_ty) { + llargs.push(opt_llretslot.unwrap()); + } - // Push the environment. - llargs.push(llenv); + // Push the environment. + llargs.push(llenv); - // Push the arguments. - bcx = trans_args(bcx, args, callee_ty, - autoref_arg, &mut llargs); + // Push the arguments. + bcx = trans_args(bcx, args, callee_ty, + autoref_arg, &mut llargs, + cleanup::CustomScope(arg_cleanup_scope)); - // Now that the arguments have finished evaluating, we - // need to revoke the cleanup for the self argument - match callee.data { - Method(d) => { - for &v in d.temp_cleanup.iter() { - revoke_clean(bcx, v); - } - } - _ => {} - } + fcx.pop_custom_cleanup_scope(arg_cleanup_scope); - // A function pointer is called without the declaration available, so we have to apply - // any attributes with ABI implications directly to the call instruction. Right now, the - // only attribute we need to worry about is `sret`. - let mut attrs = ~[]; - if type_of::return_uses_outptr(in_cx.ccx(), ret_ty) { - attrs.push((1, StructRetAttribute)); - } + // A function pointer is called without the declaration + // available, so we have to apply any attributes with ABI + // implications directly to the call instruction. Right now, + // the only attribute we need to worry about is `sret`. + let mut attrs = ~[]; + if type_of::return_uses_outptr(ccx, ret_ty) { + attrs.push((1, StructRetAttribute)); + } - // The `noalias` attribute on the return value is useful to a function ptr caller. - match ty::get(ret_ty).sty { - // `~` pointer return values never alias because ownership is transferred - ty::ty_uniq(..) | + // The `noalias` attribute on the return value is useful to a + // function ptr caller. + match ty::get(ret_ty).sty { + // `~` pointer return values never alias because ownership + // is transferred + ty::ty_uniq(..) | ty::ty_vec(_, ty::vstore_uniq) => { - attrs.push((0, NoAliasAttribute)); - } - _ => () + attrs.push((0, NoAliasAttribute)); } + _ => () + } - // Invoke the actual rust fn and update bcx/llresult. - let (llret, b) = base::invoke(bcx, llfn, llargs, attrs, call_info); - bcx = b; - llresult = llret; - - // If the Rust convention for this type is return via - // the return value, copy it into llretslot. - match opt_llretslot { - Some(llretslot) => { - if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !ty::type_is_voidish(bcx.tcx(), ret_ty) - { - Store(bcx, llret, llretslot); - } + // Invoke the actual rust fn and update bcx/llresult. + let (llret, b) = base::invoke(bcx, llfn, llargs, attrs, call_info); + bcx = b; + llresult = llret; + + // If the Rust convention for this type is return via + // the return value, copy it into llretslot. + match opt_llretslot { + Some(llretslot) => { + if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && + !ty::type_is_voidish(bcx.tcx(), ret_ty) + { + Store(bcx, llret, llretslot); } - None => {} } - } else { - // Lang items are the only case where dest is None, and - // they are always Rust fns. - assert!(dest.is_some()); - - let mut llargs = ~[]; - bcx = trans_args(bcx, args, callee_ty, - autoref_arg, &mut llargs); - let arg_tys = match args { - ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(), - ArgVals(_) => fail!("expected arg exprs.") - }; - bcx = foreign::trans_native_call(bcx, callee_ty, - llfn, opt_llretslot.unwrap(), llargs, arg_tys); + None => {} } + } else { + // Lang items are the only case where dest is None, and + // they are always Rust fns. + assert!(dest.is_some()); + + let mut llargs = ~[]; + bcx = trans_args(bcx, args, callee_ty, + autoref_arg, &mut llargs, + cleanup::CustomScope(arg_cleanup_scope)); + fcx.pop_custom_cleanup_scope(arg_cleanup_scope); + let arg_tys = match args { + ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(), + ArgVals(_) => fail!("expected arg exprs.") + }; + bcx = foreign::trans_native_call(bcx, callee_ty, + llfn, opt_llretslot.unwrap(), llargs, arg_tys); + } - // If the caller doesn't care about the result of this fn call, - // drop the temporary slot we made. - match dest { - None => { - assert!(!type_of::return_uses_outptr(bcx.ccx(), ret_ty)); - } - Some(expr::Ignore) => { - // drop the value if it is not being saved. - bcx = glue::drop_ty(bcx, opt_llretslot.unwrap(), ret_ty); - } - Some(expr::SaveIn(_)) => { } + // If the caller doesn't care about the result of this fn call, + // drop the temporary slot we made. + match dest { + None => { + assert!(!type_of::return_uses_outptr(bcx.ccx(), ret_ty)); } - - if ty::type_is_bot(ret_ty) { - Unreachable(bcx); + Some(expr::Ignore) => { + // drop the value if it is not being saved. + bcx = glue::drop_ty(bcx, opt_llretslot.unwrap(), ret_ty); } + Some(expr::SaveIn(_)) => { } + } - rslt(bcx, llresult) - }) + if ty::type_is_bot(ret_ty) { + Unreachable(bcx); + } + + rslt(bcx, llresult) } pub enum CallArgs<'a> { @@ -770,10 +793,11 @@ pub fn trans_args<'a>( args: CallArgs, fn_ty: ty::t, autoref_arg: AutorefArg, - llargs: &mut ~[ValueRef]) - -> &'a Block<'a> { + llargs: &mut ~[ValueRef], + arg_cleanup_scope: cleanup::ScopeId) + -> &'a Block<'a> +{ let _icx = push_ctxt("trans_args"); - let mut temp_cleanups = ~[]; let arg_tys = ty::ty_fn_args(fn_ty); let variadic = ty::fn_is_variadic(fn_ty); @@ -796,7 +820,7 @@ pub fn trans_args<'a>( trans_arg_expr(bcx, arg_ty, *arg_expr, - &mut temp_cleanups, + arg_cleanup_scope, autoref_arg) }); llargs.push(arg_val); @@ -807,13 +831,6 @@ pub fn trans_args<'a>( } } - // now that all arguments have been successfully built, we can revoke any - // temporary cleanups, as they are only needed if argument construction - // should fail (for example, cleanup of copy mode args). - for c in temp_cleanups.iter() { - revoke_clean(bcx, *c) - } - bcx } @@ -822,16 +839,15 @@ pub enum AutorefArg { DoAutorefArg } -// temp_cleanups: cleanups that should run only if failure occurs before the -// call takes place: pub fn trans_arg_expr<'a>( bcx: &'a Block<'a>, formal_arg_ty: ty::t, arg_expr: &ast::Expr, - temp_cleanups: &mut ~[ValueRef], + arg_cleanup_scope: cleanup::ScopeId, autoref_arg: AutorefArg) -> Result<'a> { let _icx = push_ctxt("trans_arg_expr"); + let mut bcx = bcx; let ccx = bcx.ccx(); debug!("trans_arg_expr(formal_arg_ty=({}), arg_expr={})", @@ -839,14 +855,13 @@ pub fn trans_arg_expr<'a>( arg_expr.repr(bcx.tcx())); // translate the arg expr to a datum - let arg_datumblock = expr::trans_to_datum(bcx, arg_expr); - let arg_datum = arg_datumblock.datum; - let bcx = arg_datumblock.bcx; + let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_expr)); + let arg_datum_ty = arg_datum.ty; debug!(" arg datum: {}", arg_datum.to_str(bcx.ccx())); let mut val; - if ty::type_is_bot(arg_datum.ty) { + if ty::type_is_bot(arg_datum_ty) { // For values of type _|_, we generate an // "undef" value, as such a value should never // be inspected. It's important for the value @@ -859,34 +874,31 @@ pub fn trans_arg_expr<'a>( // FIXME(#3548) use the adjustments table match autoref_arg { DoAutorefArg => { - val = arg_datum.to_ref_llval(bcx); + // We will pass argument by reference + // We want an lvalue, so that we can pass by reference and + let arg_datum = unpack_datum!( + bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_expr.id)); + val = arg_datum.val; } DontAutorefArg => { - let need_scratch = ty::type_needs_drop(bcx.tcx(), arg_datum.ty) || - (bcx.expr_is_lval(arg_expr) && - arg_datum.appropriate_mode(bcx.ccx()).is_by_ref()); - - let arg_datum = if need_scratch { - let scratch = scratch_datum(bcx, arg_datum.ty, "__self", false); - arg_datum.store_to_datum(bcx, INIT, scratch); - - // Technically, ownership of val passes to the callee. - // However, we must cleanup should we fail before the - // callee is actually invoked. - scratch.add_clean(bcx); - temp_cleanups.push(scratch.val); - - scratch - } else { - arg_datum - }; - - debug!("by copy arg with type {}", bcx.ty_to_str(arg_datum.ty)); - val = arg_datum.to_appropriate_llval(bcx); + // Make this an rvalue, since we are going to be + // passing ownership. + let arg_datum = unpack_datum!( + bcx, arg_datum.to_rvalue_datum(bcx, "arg")); + + // Now that arg_datum is owned, get it into the appropriate + // mode (ref vs value). + let arg_datum = unpack_datum!( + bcx, arg_datum.to_appropriate_datum(bcx)); + + // Technically, ownership of val passes to the callee. + // However, we must cleanup should we fail before the + // callee is actually invoked. + val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope); } } - if formal_arg_ty != arg_datum.ty { + if formal_arg_ty != arg_datum_ty { // this could happen due to e.g. subtyping let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); debug!("casting actual type ({}) to match formal ({})", diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs new file mode 100644 index 0000000000000..2ecc84ebc0c02 --- /dev/null +++ b/src/librustc/middle/trans/cleanup.rs @@ -0,0 +1,948 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Code pertaining to cleanup of temporaries as well as execution of + * drop glue. See discussion in `doc.rs` for a high-level summary. + */ + +use lib::llvm::{BasicBlockRef, ValueRef}; +use middle::lang_items::{EhPersonalityLangItem}; +use middle::trans::base; +use middle::trans::build; +use middle::trans::callee; +use middle::trans::common; +use middle::trans::common::{Block, FunctionContext}; +use middle::trans::glue; +use middle::trans::type_::Type; +use middle::ty; +use syntax::ast; +use syntax::ast_map; +use syntax::parse::token; +use syntax::opt_vec; +use syntax::opt_vec::OptVec; +use util::ppaux::Repr; + +pub struct CleanupScope<'a> { + // The id of this cleanup scope. If the id is None, + // this is a *temporary scope* that is pushed during trans to + // cleanup miscellaneous garbage that trans may generate whose + // lifetime is a subset of some expression. See module doc for + // more details. + kind: CleanupScopeKind<'a>, + + // Cleanups to run upon scope exit. + cleanups: OptVec<~Cleanup>, + + cached_early_exits: OptVec, + cached_landing_pad: Option, +} + +pub struct CustomScopeIndex { + priv index: uint +} + +pub static EXIT_BREAK: uint = 0; +pub static EXIT_LOOP: uint = 1; +pub static EXIT_MAX: uint = 2; + +enum CleanupScopeKind<'a> { + CustomScopeKind, + AstScopeKind(ast::NodeId), + LoopScopeKind(ast::NodeId, [&'a Block<'a>, ..EXIT_MAX]) +} + +#[deriving(Eq)] +enum EarlyExitLabel { + UnwindExit, + ReturnExit, + LoopExit(ast::NodeId, uint) +} + +struct CachedEarlyExit { + label: EarlyExitLabel, + cleanup_block: BasicBlockRef, +} + +pub trait Cleanup { + fn clean_on_unwind(&self) -> bool; + fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a>; +} + +pub enum ScopeId { + AstScope(ast::NodeId), + CustomScope(CustomScopeIndex) +} + +impl<'a> CleanupMethods<'a> for FunctionContext<'a> { + fn push_ast_cleanup_scope(&self, id: ast::NodeId) { + /*! + * Invoked when we start to trans the code contained + * within a new cleanup scope. + */ + + debug!("push_ast_cleanup_scope({})", + ast_map::node_id_to_str(self.ccx.tcx.items, id, + token::get_ident_interner())); + + // FIXME(#2202) -- currently closure bodies have a parent + // region, which messes up the assertion below, since there + // are no cleanup scopes on the stack at the start of + // trans'ing a closure body. I think though that this should + // eventually be fixed by closure bodies not having a parent + // region, though that's a touch unclear, and it might also be + // better just to narrow this assertion more (i.e., by + // excluding id's that correspond to closure bodies only). For + // now we just say that if there is already an AST scope on the stack, + // this new AST scope had better be its immediate child. + let top_scope = self.top_ast_scope(); + if top_scope.is_some() { + assert_eq!(self.ccx.tcx.region_maps.opt_encl_scope(id), top_scope); + } + + self.push_scope(CleanupScope::new(AstScopeKind(id))); + } + + fn push_loop_cleanup_scope(&self, + id: ast::NodeId, + exits: [&'a Block<'a>, ..EXIT_MAX]) { + debug!("push_loop_cleanup_scope({})", + ast_map::node_id_to_str(self.ccx.tcx.items, id, + token::get_ident_interner())); + assert_eq!(Some(id), self.top_ast_scope()); + + self.push_scope(CleanupScope::new(LoopScopeKind(id, exits))); + } + + fn push_custom_cleanup_scope(&self) -> CustomScopeIndex { + let index = self.scopes_len(); + debug!("push_custom_cleanup_scope(): {}", index); + self.push_scope(CleanupScope::new(CustomScopeKind)); + CustomScopeIndex { index: index } + } + + fn pop_and_trans_ast_cleanup_scope(&self, + bcx: &'a Block<'a>, + cleanup_scope: ast::NodeId) + -> &'a Block<'a> { + /*! + * Removes the cleanup scope for id `cleanup_scope`, which + * must be at the top of the cleanup stack, and generates the + * code to do its cleanups for normal exit. + */ + + debug!("pop_and_trans_ast_cleanup_scope({})", + ast_map::node_id_to_str(self.ccx.tcx.items, cleanup_scope, + token::get_ident_interner())); + + assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope))); + + let scope = self.pop_scope(); + self.trans_scope_cleanups(bcx, &scope) + + } + + fn pop_loop_cleanup_scope(&self, + cleanup_scope: ast::NodeId) { + /*! + * Removes the loop cleanup scope for id `cleanup_scope`, which + * must be at the top of the cleanup stack. Does not generate + * any cleanup code, since loop scopes should exit by + * branching to a block generated by `normal_exit_block`. + */ + + debug!("pop_loop_cleanup_scope({})", + ast_map::node_id_to_str(self.ccx.tcx.items, cleanup_scope, + token::get_ident_interner())); + + assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope))); + + let _ = self.pop_scope(); + } + + fn pop_custom_cleanup_scope(&self, + custom_scope: CustomScopeIndex) { + /*! + * Removes the top cleanup scope from the stack without + * executing its cleanups. The top cleanup scope must + * be the temporary scope `custom_scope`. + */ + + debug!("pop_custom_cleanup_scope({})", custom_scope.index); + assert!(self.is_valid_to_pop_custom_scope(custom_scope)); + let _ = self.pop_scope(); + } + + fn pop_and_trans_custom_cleanup_scope(&self, + bcx: &'a Block<'a>, + custom_scope: CustomScopeIndex) + -> &'a Block<'a> { + /*! + * Removes the top cleanup scope from the stack, which must be + * a temporary scope, and generates the code to do its + * cleanups for normal exit. + */ + + debug!("pop_and_trans_custom_cleanup_scope({:?})", custom_scope); + assert!(self.is_valid_to_pop_custom_scope(custom_scope)); + + let scope = self.pop_scope(); + self.trans_scope_cleanups(bcx, &scope) + } + + fn top_loop_scope(&self) -> ast::NodeId { + /*! + * Returns the id of the top-most loop scope + */ + + let scopes = self.scopes.borrow(); + for scope in scopes.get().iter().invert() { + match scope.kind { + LoopScopeKind(id, _) => { + return id; + } + _ => {} + } + } + self.ccx.tcx.sess.bug("No loop scope found"); + } + + fn normal_exit_block(&self, + cleanup_scope: ast::NodeId, + exit: uint) -> BasicBlockRef { + /*! + * Returns a block to branch to which will perform all pending + * cleanups and then break/continue (depending on `exit`) out + * of the loop with id `cleanup_scope` + */ + + self.trans_cleanups_to_exit_scope(LoopExit(cleanup_scope, exit)) + } + + fn return_exit_block(&self) -> BasicBlockRef { + /*! + * Returns a block to branch to which will perform all pending + * cleanups and then return from this function + */ + + self.trans_cleanups_to_exit_scope(ReturnExit) + } + + fn schedule_drop_mem(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t) { + /*! + * Schedules a (deep) drop of `val`, which is a pointer to an + * instance of `ty` + */ + + if !ty::type_needs_drop(self.ccx.tcx, ty) { return; } + let drop = ~DropValue { + is_immediate: false, + on_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx, ty), + val: val, + ty: ty + }; + + debug!("schedule_drop_mem({:?}, val={}, ty={})", + cleanup_scope, + self.ccx.tn.val_to_str(val), + ty.repr(self.ccx.tcx)); + + self.schedule_clean(cleanup_scope, drop as ~Cleanup); + } + + fn schedule_drop_immediate(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t) { + /*! + * Schedules a (deep) drop of `val`, which is an instance of `ty` + */ + + if !ty::type_needs_drop(self.ccx.tcx, ty) { return; } + let drop = ~DropValue { + is_immediate: true, + on_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx, ty), + val: val, + ty: ty + }; + + debug!("schedule_drop_immediate({:?}, val={}, ty={})", + cleanup_scope, + self.ccx.tn.val_to_str(val), + ty.repr(self.ccx.tcx)); + + self.schedule_clean(cleanup_scope, drop as ~Cleanup); + } + + fn schedule_free_value(&self, + cleanup_scope: ScopeId, + val: ValueRef, + heap: common::heap) { + /*! + * Schedules a call to `free(val)`. Note that this is a shallow + * operation. + */ + + let drop = ~FreeValue { ptr: val, heap: heap }; + + debug!("schedule_free_value({:?}, val={}, heap={:?})", + cleanup_scope, + self.ccx.tn.val_to_str(val), + heap); + + self.schedule_clean(cleanup_scope, drop as ~Cleanup); + } + + fn schedule_clean(&self, + cleanup_scope: ScopeId, + cleanup: ~Cleanup) { + match cleanup_scope { + AstScope(id) => self.schedule_clean_in_ast_scope(id, cleanup), + CustomScope(id) => self.schedule_clean_in_custom_scope(id, cleanup), + } + } + + fn schedule_clean_in_ast_scope(&self, + cleanup_scope: ast::NodeId, + cleanup: ~Cleanup) { + /*! + * Schedules a cleanup to occur upon exit from `cleanup_scope`. + * If `cleanup_scope` is not provided, then the cleanup is scheduled + * in the topmost scope, which must be a temporary scope. + */ + + debug!("schedule_clean_in_ast_scope(cleanup_scope={:?})", + cleanup_scope); + + let mut scopes = self.scopes.borrow_mut(); + for scope in scopes.get().mut_iter().invert() { + if scope.kind.is_ast_with_id(cleanup_scope) { + scope.cleanups.push(cleanup); + scope.clear_cached_exits(); + return; + } else { + // will be adding a cleanup to some enclosing scope + scope.clear_cached_exits(); + } + } + + self.ccx.tcx.sess.bug( + format!("No cleanup scope {} found", + ast_map::node_id_to_str(self.ccx.tcx.items, cleanup_scope, + token::get_ident_interner()))); + } + + fn schedule_clean_in_custom_scope(&self, + custom_scope: CustomScopeIndex, + cleanup: ~Cleanup) { + /*! + * Schedules a cleanup to occur in the top-most scope, + * which must be a temporary scope. + */ + + debug!("schedule_clean_in_custom_scope(custom_scope={})", + custom_scope.index); + + assert!(self.is_valid_custom_scope(custom_scope)); + + let mut scopes = self.scopes.borrow_mut(); + let scope = &mut scopes.get()[custom_scope.index]; + scope.cleanups.push(cleanup); + scope.clear_cached_exits(); + } + + fn needs_invoke(&self) -> bool { + /*! + * Returns true if there are pending cleanups that should + * execute on failure. + */ + + let scopes = self.scopes.borrow(); + scopes.get().iter().invert().any(|s| s.needs_invoke()) + } + + fn get_landing_pad(&self) -> BasicBlockRef { + /*! + * Returns a basic block to branch to in the event of a failure. + * This block will run the failure cleanups and eventually + * invoke the LLVM `Resume` instruction. + */ + + let _icx = base::push_ctxt("get_landing_pad"); + + debug!("get_landing_pad"); + + let orig_scopes_len = self.scopes_len(); + assert!(orig_scopes_len > 0); + + // Remove any scopes that do not have cleanups on failure: + let mut popped_scopes = opt_vec::Empty; + while !self.top_scope(|s| s.needs_invoke()) { + debug!("top scope does not need invoke"); + popped_scopes.push(self.pop_scope()); + } + + // Check for an existing landing pad in the new topmost scope: + let llbb = self.get_or_create_landing_pad(); + + // Push the scopes we removed back on: + while !popped_scopes.is_empty() { + self.push_scope(popped_scopes.pop()); + } + + assert_eq!(self.scopes_len(), orig_scopes_len); + + return llbb; + } +} + +impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> { + fn top_ast_scope(&self) -> Option { + /*! + * Returns the id of the current top-most AST scope, if any. + */ + let scopes = self.scopes.borrow(); + for scope in scopes.get().iter().invert() { + match scope.kind { + CustomScopeKind | LoopScopeKind(..) => {} + AstScopeKind(i) => { + return Some(i); + } + } + } + None + } + + fn top_nonempty_cleanup_scope(&self) -> Option { + let scopes = self.scopes.borrow(); + scopes.get().iter().invert().position(|s| !s.cleanups.is_empty()) + } + + fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { + let scopes = self.scopes.borrow(); + self.is_valid_custom_scope(custom_scope) && + custom_scope.index == scopes.get().len() - 1 + } + + fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { + let scopes = self.scopes.borrow(); + custom_scope.index < scopes.get().len() && + scopes.get()[custom_scope.index].kind.is_temp() + } + + fn trans_scope_cleanups(&self, // cannot borrow self, will recurse + bcx: &'a Block<'a>, + scope: &CleanupScope) -> &'a Block<'a> { + /*! Generates the cleanups for `scope` into `bcx` */ + + let mut bcx = bcx; + if !bcx.unreachable.get() { + for cleanup in scope.cleanups.iter().invert() { + bcx = cleanup.trans(bcx); + } + } + bcx + } + + fn scopes_len(&self) -> uint { + let scopes = self.scopes.borrow(); + scopes.get().len() + } + + fn push_scope(&self, scope: CleanupScope<'a>) { + let mut scopes = self.scopes.borrow_mut(); + scopes.get().push(scope); + } + + fn pop_scope(&self) -> CleanupScope<'a> { + debug!("popping cleanup scope {}, {} scopes remaining", + self.top_scope(|s| s.block_name("")), + self.scopes_len() - 1); + + let mut scopes = self.scopes.borrow_mut(); + scopes.get().pop() + } + + fn top_scope(&self, f: |&CleanupScope<'a>| -> R) -> R { + let scopes = self.scopes.borrow(); + f(scopes.get().last()) + } + + fn trans_cleanups_to_exit_scope(&self, + label: EarlyExitLabel) + -> BasicBlockRef { + /*! + * Used when the caller wishes to jump to an early exit, such + * as a return, break, continue, or unwind. This function will + * generate all cleanups between the top of the stack and the + * exit `label` and return a basic block that the caller can + * branch to. + * + * For example, if the current stack of cleanups were as follows: + * + * AST 22 + * Custom 1 + * AST 23 + * Loop 23 + * Custom 2 + * AST 24 + * + * and the `label` specifies a break from `Loop 23`, then this + * function would generate a series of basic blocks as follows: + * + * Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk + * + * where `break_blk` is the block specified in `Loop 23` as + * the target for breaks. The return value would be the first + * basic block in that sequence (`Cleanup(AST 24)`). The + * caller could then branch to `Cleanup(AST 24)` and it will + * perform all cleanups and finally branch to the `break_blk`. + */ + + debug!("trans_cleanups_to_exit_scope label={:?} scopes={}", + label, self.scopes_len()); + + let orig_scopes_len = self.scopes_len(); + let mut prev_llbb; + let mut popped_scopes = opt_vec::Empty; + + // First we pop off all the cleanup stacks that are + // traversed until the exit is reached, pushing them + // onto the side vector `popped_scopes`. No code is + // generated at this time. + // + // So, continuing the example from above, we would wind up + // with a `popped_scopes` vector of `[AST 24, Custom 2]`. + // (Presuming that there are no cached exits) + loop { + if self.scopes_len() == 0 { + match label { + UnwindExit => { + // Generate a block that will `Resume`. + let prev_bcx = self.new_block(true, "resume", None); + let personality = self.personality.get().expect( + "create_landing_pad() should have set this"); + build::Resume(prev_bcx, + build::Load(prev_bcx, personality)); + prev_llbb = prev_bcx.llbb; + break; + } + + ReturnExit => { + prev_llbb = self.get_llreturn(); + break; + } + + LoopExit(id, _) => { + self.ccx.tcx.sess.bug(format!( + "Cannot exit from scope {:?}, \ + not in scope", id)); + } + } + } + + // Check if we have already cached the unwinding of this + // scope for this label. If so, we can stop popping scopes + // and branch to the cached label, since it contains the + // cleanups for any subsequent scopes. + match self.top_scope(|s| s.cached_early_exit(label)) { + Some(cleanup_block) => { + prev_llbb = cleanup_block; + break; + } + None => { } + } + + // Pop off the scope, since we will be generating + // unwinding code for it. If we are searching for a loop exit, + // and this scope is that loop, then stop popping and set + // `prev_llbb` to the appropriate exit block from the loop. + popped_scopes.push(self.pop_scope()); + let scope = popped_scopes.last(); + match label { + UnwindExit | ReturnExit => { } + LoopExit(id, exit) => { + match scope.kind.early_exit_block(id, exit) { + Some(exitllbb) => { + prev_llbb = exitllbb; + break; + } + + None => { } + } + } + } + } + + debug!("trans_cleanups_to_exit_scope: popped {} scopes", + popped_scopes.len()); + + // Now push the popped scopes back on. As we go, + // we track in `prev_llbb` the exit to which this scope + // should branch when it's done. + // + // So, continuing with our example, we will start out with + // `prev_llbb` being set to `break_blk` (or possibly a cached + // early exit). We will then pop the scopes from `popped_scopes` + // and generate a basic block for each one, prepending it in the + // series and updating `prev_llbb`. So we begin by popping `Custom 2` + // and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)` + // branch to `prev_llbb == break_blk`, giving us a sequence like: + // + // Cleanup(Custom 2) -> prev_llbb + // + // We then pop `AST 24` and repeat the process, giving us the sequence: + // + // Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb + // + // At this point, `popped_scopes` is empty, and so the final block + // that we return to the user is `Cleanup(AST 24)`. + while !popped_scopes.is_empty() { + let mut scope = popped_scopes.pop(); + + if scope.cleanups.iter().any(|c| cleanup_is_suitable_for(*c, label)) + { + let name = scope.block_name("clean"); + debug!("generating cleanups for {}", name); + let bcx_in = self.new_block(label.is_unwind(), name, None); + let mut bcx_out = bcx_in; + for cleanup in scope.cleanups.iter().invert() { + if cleanup_is_suitable_for(*cleanup, label) { + bcx_out = cleanup.trans(bcx_out); + } + } + build::Br(bcx_out, prev_llbb); + prev_llbb = bcx_in.llbb; + } else { + debug!("no suitable cleanups in {}", + scope.block_name("clean")); + } + + scope.add_cached_early_exit(label, prev_llbb); + self.push_scope(scope); + } + + debug!("trans_cleanups_to_exit_scope: prev_llbb={}", prev_llbb); + + assert_eq!(self.scopes_len(), orig_scopes_len); + prev_llbb + } + + fn get_or_create_landing_pad(&self) -> BasicBlockRef { + /*! + * Creates a landing pad for the top scope, if one does not + * exist. The landing pad will perform all cleanups necessary + * for an unwind and then `resume` to continue error + * propagation: + * + * landing_pad -> ... cleanups ... -> [resume] + * + * (The cleanups and resume instruction are created by + * `trans_cleanups_to_exit_scope()`, not in this function + * itself.) + */ + + let pad_bcx; + + debug!("get_or_create_landing_pad"); + + // Check if a landing pad block exists; if not, create one. + { + let mut scopes = self.scopes.borrow_mut(); + let last_scope = scopes.get().mut_last(); + match last_scope.cached_landing_pad { + Some(llbb) => { return llbb; } + None => { + let name = last_scope.block_name("unwind"); + pad_bcx = self.new_block(true, name, None); + last_scope.cached_landing_pad = Some(pad_bcx.llbb); + } + } + } + + // The landing pad return type (the type being propagated). Not sure what + // this represents but it's determined by the personality function and + // this is what the EH proposal example uses. + let llretty = Type::struct_([Type::i8p(), Type::i32()], false); + + // The exception handling personality function. + let def_id = common::langcall(pad_bcx, None, "", EhPersonalityLangItem); + let llpersonality = callee::trans_fn_ref(pad_bcx, def_id, 0).llfn; + + // The only landing pad clause will be 'cleanup' + let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u); + + // The landing pad block is a cleanup + build::SetCleanup(pad_bcx, llretval); + + // We store the retval in a function-central alloca, so that calls to + // Resume can find it. + match self.personality.get() { + Some(addr) => { + build::Store(pad_bcx, llretval, addr); + } + None => { + let addr = base::alloca(pad_bcx, common::val_ty(llretval), ""); + self.personality.set(Some(addr)); + build::Store(pad_bcx, llretval, addr); + } + } + + // Generate the cleanup block and branch to it. + let cleanup_llbb = self.trans_cleanups_to_exit_scope(UnwindExit); + build::Br(pad_bcx, cleanup_llbb); + + return pad_bcx.llbb; + } +} + +impl<'a> CleanupScope<'a> { + fn new(kind: CleanupScopeKind<'a>) -> CleanupScope<'a> { + CleanupScope { + kind: kind, + cleanups: opt_vec::Empty, + cached_early_exits: opt_vec::Empty, + cached_landing_pad: None, + } + } + + fn clear_cached_exits(&mut self) { + self.cached_early_exits = opt_vec::Empty; + self.cached_landing_pad = None; + } + + fn cached_early_exit(&self, + label: EarlyExitLabel) + -> Option { + self.cached_early_exits.iter(). + find(|e| e.label == label). + map(|e| e.cleanup_block) + } + + fn add_cached_early_exit(&mut self, + label: EarlyExitLabel, + blk: BasicBlockRef) { + self.cached_early_exits.push( + CachedEarlyExit { label: label, + cleanup_block: blk }); + } + + fn needs_invoke(&self) -> bool { + /*! True if this scope has cleanups for use during unwinding */ + + self.cached_landing_pad.is_some() || + self.cleanups.iter().any(|c| c.clean_on_unwind()) + } + + fn block_name(&self, prefix: &str) -> ~str { + /*! + * Returns a suitable name to use for the basic block that + * handles this cleanup scope + */ + + match self.kind { + CustomScopeKind => format!("{}_custom_", prefix), + AstScopeKind(id) => format!("{}_ast_{}_", prefix, id), + LoopScopeKind(id, _) => format!("{}_loop_{}_", prefix, id), + } + } +} + +impl<'a> CleanupScopeKind<'a> { + fn is_temp(&self) -> bool { + match *self { + CustomScopeKind => true, + LoopScopeKind(..) | AstScopeKind(..) => false, + } + } + + fn is_ast_with_id(&self, id: ast::NodeId) -> bool { + match *self { + CustomScopeKind | LoopScopeKind(..) => false, + AstScopeKind(i) => i == id + } + } + + fn is_loop_with_id(&self, id: ast::NodeId) -> bool { + match *self { + CustomScopeKind | AstScopeKind(..) => false, + LoopScopeKind(i, _) => i == id + } + } + + fn early_exit_block(&self, + id: ast::NodeId, + exit: uint) -> Option { + /*! + * If this is a loop scope with id `id`, return the early + * exit block `exit`, else `None` + */ + + match *self { + LoopScopeKind(i, ref exits) if id == i => Some(exits[exit].llbb), + _ => None, + } + } +} + +impl EarlyExitLabel { + fn is_unwind(&self) -> bool { + match *self { + UnwindExit => true, + _ => false + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Cleanup types + +pub struct DropValue { + is_immediate: bool, + on_unwind: bool, + val: ValueRef, + ty: ty::t, +} + +impl Cleanup for DropValue { + fn clean_on_unwind(&self) -> bool { + self.on_unwind + } + + fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> { + if self.is_immediate { + glue::drop_ty_immediate(bcx, self.val, self.ty) + } else { + glue::drop_ty(bcx, self.val, self.ty) + } + } +} + +pub struct FreeValue { + ptr: ValueRef, + heap: common::heap, +} + +impl Cleanup for FreeValue { + fn clean_on_unwind(&self) -> bool { + true + } + + fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> { + match self.heap { + common::heap_managed => { + glue::trans_free(bcx, self.ptr) + } + common::heap_exchange | common::heap_exchange_closure => { + glue::trans_exchange_free(bcx, self.ptr) + } + } + } +} + +pub fn temporary_scope(tcx: ty::ctxt, + id: ast::NodeId) + -> ScopeId { + match tcx.region_maps.temporary_scope(id) { + Some(scope) => { + let r = AstScope(scope); + debug!("temporary_scope({}) = {:?}", id, r); + r + } + None => { + tcx.sess.bug(format!("no temporary scope available for expr {}", id)) + } + } +} + +pub fn var_scope(tcx: ty::ctxt, + id: ast::NodeId) + -> ScopeId { + let r = AstScope(tcx.region_maps.var_scope(id)); + debug!("var_scope({}) = {:?}", id, r); + r +} + +fn cleanup_is_suitable_for(c: &Cleanup, + label: EarlyExitLabel) -> bool { + !label.is_unwind() || c.clean_on_unwind() +} + +/////////////////////////////////////////////////////////////////////////// +// These traits just exist to put the methods into this file. + +pub trait CleanupMethods<'a> { + fn push_ast_cleanup_scope(&self, id: ast::NodeId); + fn push_loop_cleanup_scope(&self, + id: ast::NodeId, + exits: [&'a Block<'a>, ..EXIT_MAX]); + fn push_custom_cleanup_scope(&self) -> CustomScopeIndex; + fn pop_and_trans_ast_cleanup_scope(&self, + bcx: &'a Block<'a>, + cleanup_scope: ast::NodeId) + -> &'a Block<'a>; + fn pop_loop_cleanup_scope(&self, + cleanup_scope: ast::NodeId); + fn pop_custom_cleanup_scope(&self, + custom_scope: CustomScopeIndex); + fn pop_and_trans_custom_cleanup_scope(&self, + bcx: &'a Block<'a>, + custom_scope: CustomScopeIndex) + -> &'a Block<'a>; + fn top_loop_scope(&self) -> ast::NodeId; + fn normal_exit_block(&self, + cleanup_scope: ast::NodeId, + exit: uint) -> BasicBlockRef; + fn return_exit_block(&self) -> BasicBlockRef; + fn schedule_drop_mem(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t); + fn schedule_drop_immediate(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t); + fn schedule_free_value(&self, + cleanup_scope: ScopeId, + val: ValueRef, + heap: common::heap); + fn schedule_clean(&self, + cleanup_scope: ScopeId, + cleanup: ~Cleanup); + fn schedule_clean_in_ast_scope(&self, + cleanup_scope: ast::NodeId, + cleanup: ~Cleanup); + fn schedule_clean_in_custom_scope(&self, + custom_scope: CustomScopeIndex, + cleanup: ~Cleanup); + fn needs_invoke(&self) -> bool; + fn get_landing_pad(&self) -> BasicBlockRef; +} + +trait CleanupHelperMethods<'a> { + fn top_ast_scope(&self) -> Option; + fn top_nonempty_cleanup_scope(&self) -> Option; + fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool; + fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool; + fn trans_scope_cleanups(&self, + bcx: &'a Block<'a>, + scope: &CleanupScope<'a>) -> &'a Block<'a>; + fn trans_cleanups_to_exit_scope(&self, + label: EarlyExitLabel) + -> BasicBlockRef; + fn get_or_create_landing_pad(&self) -> BasicBlockRef; + fn scopes_len(&self) -> uint; + fn push_scope(&self, scope: CleanupScope<'a>); + fn pop_scope(&self) -> CleanupScope<'a>; + fn top_scope(&self, f: |&CleanupScope<'a>| -> R) -> R; +} diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index f3d061f70b150..58f7171452e7d 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -16,7 +16,7 @@ use middle::moves; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::common::*; -use middle::trans::datum::{Datum, INIT}; +use middle::trans::datum::{Datum, Lvalue}; use middle::trans::debuginfo; use middle::trans::expr; use middle::trans::glue; @@ -112,7 +112,7 @@ pub enum EnvAction { pub struct EnvValue { action: EnvAction, - datum: Datum + datum: Datum } impl EnvAction { @@ -219,7 +219,7 @@ pub fn store_environment<'a>( // Copy expr values into boxed bindings. let mut bcx = bcx; - for (i, bv) in bound_values.iter().enumerate() { + for (i, bv) in bound_values.move_iter().enumerate() { debug!("Copy {} into closure", bv.to_str(ccx)); if ccx.sess.asm_comments() { @@ -230,17 +230,13 @@ pub fn store_environment<'a>( let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]); match bv.action { - EnvCopy => { - bcx = bv.datum.copy_to(bcx, INIT, bound_data); - } - EnvMove => { - bcx = bv.datum.move_to(bcx, INIT, bound_data); + EnvCopy | EnvMove => { + bcx = bv.datum.store_to(bcx, bound_data); } EnvRef => { - Store(bcx, bv.datum.to_ref_llval(bcx), bound_data); + Store(bcx, bv.datum.to_llref(), bound_data); } } - } ClosureResult { llbox: llbox, cdata_ty: cdata_ty, bcx: bcx } @@ -413,7 +409,6 @@ pub fn trans_expr_fn<'a>( None, bcx.fcx.param_substs, user_id, - None, [], ty::ty_fn_ret(fty), |fcx| load_environment(fcx, cdata_ty, cap_vars, sigil)); diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index f98fec7cbef30..cb2987ac16a11 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -20,8 +20,9 @@ use lib; use middle::lang_items::LangItem; use middle::trans::base; use middle::trans::build; +use middle::trans::cleanup; use middle::trans::datum; -use middle::trans::glue; +use middle::trans::datum::{Datum, Lvalue}; use middle::trans::debuginfo; use middle::trans::type_::Type; use middle::ty::substs; @@ -37,8 +38,7 @@ use std::cast; use std::cell::{Cell, RefCell}; use std::hashmap::HashMap; use std::libc::{c_uint, c_longlong, c_ulonglong, c_char}; -use std::vec; -use syntax::ast::{Name, Ident}; +use syntax::ast::{Ident}; use syntax::ast_map::{Path, PathElem, PathPrettyName}; use syntax::codemap::Span; use syntax::parse::token; @@ -122,6 +122,15 @@ pub struct tydesc_info { * */ +pub struct NodeInfo { + id: ast::NodeId, + span: Span, +} + +pub fn expr_info(expr: &ast::Expr) -> NodeInfo { + NodeInfo { id: expr.id, span: expr.span } +} + pub struct Stats { n_static_tydescs: Cell, n_glues_created: Cell, @@ -185,6 +194,10 @@ impl Repr for param_substs { } } +// work around bizarre resolve errors +type RvalueDatum = datum::Datum; +type LvalueDatum = datum::Datum; + // Function context. Every LLVM function we create will have one of // these. pub struct FunctionContext<'a> { @@ -213,13 +226,15 @@ pub struct FunctionContext<'a> { // allocas, so that LLVM will coalesce them into a single alloca call. alloca_insert_pt: Cell>, llreturn: Cell>, + // The 'self' value currently in use in this function, if there // is one. // // NB: This is the type of the self *variable*, not the self *type*. The // self type is set only for default methods, while the self variable is // set for all methods. - llself: Cell>, + llself: Cell>, + // The a value alloca'd for calls to upcalls.rust_personality. Used when // outputting the resume instruction. personality: Cell>, @@ -230,10 +245,12 @@ pub struct FunctionContext<'a> { caller_expects_out_pointer: bool, // Maps arguments to allocas created for them in llallocas. - llargs: RefCell>, + llargs: RefCell>, + // Maps the def_ids for local variables to the allocas created for // them in llallocas. - lllocals: RefCell>, + lllocals: RefCell>, + // Same as above, but for closure upvars llupvars: RefCell>, @@ -253,14 +270,14 @@ pub struct FunctionContext<'a> { // The arena that blocks are allocated from. block_arena: TypedArena>, - // The arena that scope info is allocated from. - scope_info_arena: TypedArena>, - // This function's enclosing crate context. ccx: @CrateContext, // Used and maintained by the debuginfo module. debug_context: debuginfo::FunctionDebugContext, + + // Cleanup scopes. + scopes: RefCell<~[cleanup::CleanupScope<'a>]>, } impl<'a> FunctionContext<'a> { @@ -302,315 +319,67 @@ impl<'a> FunctionContext<'a> { self.llreturn.get().unwrap() } -} - -pub fn warn_not_to_commit(ccx: &CrateContext, msg: &str) { - if !ccx.do_not_commit_warning_issued.get() { - ccx.do_not_commit_warning_issued.set(true); - ccx.sess.warn(msg.to_str() + " -- do not commit like this!"); - } -} - -// Heap selectors. Indicate which heap something should go on. -#[deriving(Eq)] -pub enum heap { - heap_managed, - heap_exchange, - heap_exchange_closure -} - -#[deriving(Clone, Eq)] -pub enum cleantype { - normal_exit_only, - normal_exit_and_unwind -} - -// Cleanup functions - -/// A cleanup function: a built-in destructor. -pub trait CleanupFunction { - fn clean<'a>(&self, block: &'a Block<'a>) -> &'a Block<'a>; -} - -/// A cleanup function that calls the "drop glue" (destructor function) on -/// a datum. -struct DatumDroppingCleanupFunction { - datum: datum::Datum -} - -impl CleanupFunction for DatumDroppingCleanupFunction { - fn clean<'a>(&self, block: &'a Block<'a>) -> &'a Block<'a> { - self.datum.drop_val(block) - } -} - -/// A cleanup function that frees some memory in the garbage-collected heap. -pub struct GCHeapFreeingCleanupFunction { - ptr: ValueRef, -} - -impl CleanupFunction for GCHeapFreeingCleanupFunction { - fn clean<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> { - glue::trans_free(bcx, self.ptr) - } -} - -/// A cleanup function that frees some memory in the exchange heap. -pub struct ExchangeHeapFreeingCleanupFunction { - ptr: ValueRef, -} - -impl CleanupFunction for ExchangeHeapFreeingCleanupFunction { - fn clean<'a>(&self, bcx: &'a Block) -> &'a Block<'a> { - glue::trans_exchange_free(bcx, self.ptr) - } -} -pub enum cleanup { - Clean(@CleanupFunction, cleantype), - CleanTemp(ValueRef, @CleanupFunction, cleantype), -} - -// Can't use deriving(Clone) because of the managed closure. -impl Clone for cleanup { - fn clone(&self) -> cleanup { - match *self { - Clean(f, ct) => Clean(f, ct), - CleanTemp(v, f, ct) => CleanTemp(v, f, ct), + pub fn new_block(&'a self, + is_lpad: bool, + name: &str, + opt_node_id: Option) + -> &'a Block<'a> { + unsafe { + let llbb = name.with_c_str(|buf| { + llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx, + self.llfn, + buf) + }); + Block::new(llbb, is_lpad, opt_node_id, self) } } -} - -// Used to remember and reuse existing cleanup paths -// target: none means the path ends in an resume instruction -#[deriving(Clone)] -pub struct cleanup_path { - target: Option, - size: uint, - dest: BasicBlockRef -} - -pub fn shrink_scope_clean(scope_info: &ScopeInfo, size: uint) { - scope_info.landing_pad.set(None); - let new_cleanup_paths = { - let cleanup_paths = scope_info.cleanup_paths.borrow(); - cleanup_paths.get() - .iter() - .take_while(|&cu| cu.size <= size) - .map(|&x| x) - .collect() - }; - scope_info.cleanup_paths.set(new_cleanup_paths) -} - -pub fn grow_scope_clean(scope_info: &ScopeInfo) { - scope_info.landing_pad.set(None); -} -pub fn cleanup_type(cx: ty::ctxt, ty: ty::t) -> cleantype { - if ty::type_needs_unwind_cleanup(cx, ty) { - normal_exit_and_unwind - } else { - normal_exit_only + pub fn new_id_block(&'a self, + name: &str, + node_id: ast::NodeId) + -> &'a Block<'a> { + self.new_block(false, name, Some(node_id)) } -} - -pub fn add_clean(bcx: &Block, val: ValueRef, ty: ty::t) { - if !ty::type_needs_drop(bcx.tcx(), ty) { return; } - - debug!("add_clean({}, {}, {})", bcx.to_str(), bcx.val_to_str(val), ty.repr(bcx.tcx())); - - let cleanup_type = cleanup_type(bcx.tcx(), ty); - in_scope_cx(bcx, None, |scope_info| { - { - let mut cleanups = scope_info.cleanups.borrow_mut(); - cleanups.get().push(Clean(@DatumDroppingCleanupFunction { - datum: datum::Datum { - val: val, - ty: ty, - mode: datum::ByRef(datum::ZeroMem) - } - } as @CleanupFunction, - cleanup_type)); - } - grow_scope_clean(scope_info); - }) -} - -pub fn add_clean_temp_immediate(bcx: &Block, val: ValueRef, ty: ty::t) { - if !ty::type_needs_drop(bcx.tcx(), ty) { return; } - - debug!("add_clean_temp_immediate({}, {}, {})", - bcx.to_str(), bcx.val_to_str(val), - ty.repr(bcx.tcx())); - let cleanup_type = cleanup_type(bcx.tcx(), ty); - in_scope_cx(bcx, None, |scope_info| { - { - let mut cleanups = scope_info.cleanups.borrow_mut(); - cleanups.get().push(CleanTemp(val, @DatumDroppingCleanupFunction { - datum: datum::Datum { - val: val, - ty: ty, - mode: datum::ByValue - } - } as @CleanupFunction, - cleanup_type)); - } - grow_scope_clean(scope_info); - }) -} - -pub fn add_clean_temp_mem(bcx: &Block, val: ValueRef, t: ty::t) { - add_clean_temp_mem_in_scope_(bcx, None, val, t); -} - -pub fn add_clean_temp_mem_in_scope(bcx: &Block, - scope_id: ast::NodeId, - val: ValueRef, - t: ty::t) { - add_clean_temp_mem_in_scope_(bcx, Some(scope_id), val, t); -} -pub fn add_clean_temp_mem_in_scope_(bcx: &Block, scope_id: Option, - val: ValueRef, t: ty::t) { - if !ty::type_needs_drop(bcx.tcx(), t) { return; } - debug!("add_clean_temp_mem({}, {}, {})", - bcx.to_str(), bcx.val_to_str(val), - t.repr(bcx.tcx())); - let cleanup_type = cleanup_type(bcx.tcx(), t); - in_scope_cx(bcx, scope_id, |scope_info| { - { - let mut cleanups = scope_info.cleanups.borrow_mut(); - cleanups.get().push(CleanTemp(val, @DatumDroppingCleanupFunction { - datum: datum::Datum { - val: val, - ty: t, - mode: datum::ByRef(datum::RevokeClean) - } - } as @CleanupFunction, - cleanup_type)); - } - grow_scope_clean(scope_info); - }) -} + pub fn new_temp_block(&'a self, + name: &str) + -> &'a Block<'a> { + self.new_block(false, name, None) + } -pub fn add_clean_free(cx: &Block, ptr: ValueRef, heap: heap) { - let free_fn = match heap { - heap_managed => { - @GCHeapFreeingCleanupFunction { - ptr: ptr, - } as @CleanupFunction - } - heap_exchange | heap_exchange_closure => { - @ExchangeHeapFreeingCleanupFunction { - ptr: ptr, - } as @CleanupFunction - } - }; - in_scope_cx(cx, None, |scope_info| { - { - let mut cleanups = scope_info.cleanups.borrow_mut(); - cleanups.get().push(CleanTemp(ptr, - free_fn, - normal_exit_and_unwind)); + pub fn join_blocks(&'a self, + id: ast::NodeId, + in_cxs: &[&'a Block<'a>]) + -> &'a Block<'a> { + let out = self.new_id_block("join", id); + let mut reachable = false; + for bcx in in_cxs.iter() { + if !bcx.unreachable.get() { + build::Br(*bcx, out.llbb); + reachable = true; + } } - grow_scope_clean(scope_info); - }) -} - -// Note that this only works for temporaries. We should, at some point, move -// to a system where we can also cancel the cleanup on local variables, but -// this will be more involved. For now, we simply zero out the local, and the -// drop glue checks whether it is zero. -pub fn revoke_clean(cx: &Block, val: ValueRef) { - in_scope_cx(cx, None, |scope_info| { - let cleanup_pos = { - let mut cleanups = scope_info.cleanups.borrow_mut(); - debug!("revoke_clean({}, {}) revoking {:?} from {:?}", - cx.to_str(), cx.val_to_str(val), val, cleanups.get()); - cleanups.get().iter().position(|cu| { - match *cu { - CleanTemp(v, _, _) if v == val => true, - _ => false - } - }) - }; - debug!("revoke_clean({}, {}) revoking {:?}", - cx.to_str(), cx.val_to_str(val), cleanup_pos); - for &i in cleanup_pos.iter() { - let new_cleanups = { - let cleanups = scope_info.cleanups.borrow(); - vec::append(cleanups.get().slice(0u, i).to_owned(), - cleanups.get().slice(i + 1u, cleanups.get() - .len())) - }; - scope_info.cleanups.set(new_cleanups); - shrink_scope_clean(scope_info, i); + if !reachable { + build::Unreachable(out); } - }) -} - -pub fn block_cleanups(bcx: &Block) -> ~[cleanup] { - match bcx.scope.get() { - None => ~[], - Some(inf) => inf.cleanups.get(), - } -} - -pub struct ScopeInfo<'a> { - parent: Option<&'a ScopeInfo<'a>>, - loop_break: Option<&'a Block<'a>>, - loop_label: Option, - // A list of functions that must be run at when leaving this - // block, cleaning up any variables that were introduced in the - // block. - cleanups: RefCell<~[cleanup]>, - // Existing cleanup paths that may be reused, indexed by destination and - // cleared when the set of cleanups changes. - cleanup_paths: RefCell<~[cleanup_path]>, - // Unwinding landing pad. Also cleared when cleanups change. - landing_pad: Cell>, - // info about the AST node this scope originated from, if any - node_info: Option, -} - -impl<'a> ScopeInfo<'a> { - pub fn empty_cleanups(&self) -> bool { - let cleanups = self.cleanups.borrow(); - cleanups.get().is_empty() - } -} - -pub trait get_node_info { - fn info(&self) -> Option; -} - -impl get_node_info for ast::Expr { - fn info(&self) -> Option { - Some(NodeInfo {id: self.id, - callee_id: self.get_callee_id(), - span: self.span}) - } -} - -impl get_node_info for ast::Block { - fn info(&self) -> Option { - Some(NodeInfo {id: self.id, - callee_id: None, - span: self.span}) + return out; } } -impl get_node_info for Option<@ast::Expr> { - fn info(&self) -> Option { - self.as_ref().and_then(|s| s.info()) +pub fn warn_not_to_commit(ccx: &mut CrateContext, msg: &str) { + if !ccx.do_not_commit_warning_issued.get() { + ccx.do_not_commit_warning_issued.set(true); + ccx.sess.warn(msg.to_str() + " -- do not commit like this!"); } } -pub struct NodeInfo { - id: ast::NodeId, - callee_id: Option, - span: Span +// Heap selectors. Indicate which heap something should go on. +#[deriving(Eq)] +pub enum heap { + heap_managed, + heap_exchange, + heap_exchange_closure } // Basic block context. We create a block context for each basic block @@ -627,13 +396,14 @@ pub struct Block<'a> { llbb: BasicBlockRef, terminated: Cell, unreachable: Cell, - parent: Option<&'a Block<'a>>, - // The current scope within this basic block - scope: RefCell>>, + // Is this block part of a landing pad? is_lpad: bool, - // info about the AST node this block originated from, if any - node_info: Option, + + // AST node-id associated with this block, if any. Used for + // debugging purposes only. + opt_node_id: Option, + // The function context for the function to which this block is // attached. fcx: &'a FunctionContext<'a>, @@ -642,20 +412,17 @@ pub struct Block<'a> { impl<'a> Block<'a> { pub fn new<'a>( llbb: BasicBlockRef, - parent: Option<&'a Block<'a>>, is_lpad: bool, - node_info: Option, + opt_node_id: Option, fcx: &'a FunctionContext<'a>) -> &'a Block<'a> { fcx.block_arena.alloc(Block { llbb: llbb, terminated: Cell::new(false), unreachable: Cell::new(false), - parent: parent, - scope: RefCell::new(None), is_lpad: is_lpad, - node_info: node_info, - fcx: fcx, + opt_node_id: opt_node_id, + fcx: fcx }) } @@ -709,12 +476,8 @@ impl<'a> Block<'a> { } pub fn to_str(&self) -> ~str { - unsafe { - match self.node_info { - Some(node_info) => format!("[block {}]", node_info.id), - None => format!("[block {}]", transmute::<&Block, *Block>(self)), - } - } + let blk: *Block = self; + format!("[block {}]", blk) } } @@ -743,48 +506,6 @@ pub fn val_ty(v: ValueRef) -> Type { } } -pub fn in_scope_cx<'a>( - cx: &'a Block<'a>, - scope_id: Option, - f: |si: &'a ScopeInfo<'a>|) { - let mut cur = cx; - let mut cur_scope = cur.scope.get(); - loop { - cur_scope = match cur_scope { - Some(inf) => match scope_id { - Some(wanted) => match inf.node_info { - Some(NodeInfo { id: actual, .. }) if wanted == actual => { - debug!("in_scope_cx: selected cur={} (cx={}) info={:?}", - cur.to_str(), cx.to_str(), inf.node_info); - f(inf); - return; - }, - _ => inf.parent, - }, - None => { - debug!("in_scope_cx: selected cur={} (cx={}) info={:?}", - cur.to_str(), cx.to_str(), inf.node_info); - f(inf); - return; - } - }, - None => { - cur = block_parent(cur); - cur.scope.get() - } - } - } -} - -pub fn block_parent<'a>(cx: &'a Block<'a>) -> &'a Block<'a> { - match cx.parent { - Some(b) => b, - None => cx.sess().bug(format!("block_parent called on root block {:?}", - cx)) - } -} - - // Let T be the content of a box @T. tuplify_box_ty(t) returns the // representation of @T as a tuple (i.e., the ty::t version of what T_box() // returns). @@ -1012,7 +733,7 @@ pub enum mono_param_id { mono_repr(uint /* size */, uint /* align */, MonoDataClass, - datum::DatumMode), + datum::RvalueMode), } #[deriving(Eq,IterBytes)] diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 97e338eab8565..ec47dbacb39c0 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -15,10 +15,12 @@ use middle::trans::build::*; use middle::trans::callee; use middle::trans::common::*; use middle::trans::debuginfo; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::expr; use middle::ty; -use util::common::indenter; use util::ppaux; +use util::ppaux::Repr; use middle::trans::type_::Type; @@ -28,10 +30,56 @@ use syntax::ast_util; use syntax::codemap::Span; use syntax::visit::Visitor; -pub fn trans_block<'a>(bcx: &'a Block<'a>, b: &ast::Block, dest: expr::Dest) - -> &'a Block<'a> { +pub fn trans_stmt<'a>(cx: &'a Block<'a>, + s: &ast::Stmt) + -> &'a Block<'a> { + let _icx = push_ctxt("trans_stmt"); + let fcx = cx.fcx; + debug!("trans_stmt({})", s.repr(cx.tcx())); + + if cx.sess().asm_comments() { + add_span_comment(cx, s.span, s.repr(cx.tcx())); + } + + let mut bcx = cx; + + let id = ast_util::stmt_id(s); + fcx.push_ast_cleanup_scope(id); + + match s.node { + ast::StmtExpr(e, _) | ast::StmtSemi(e, _) => { + bcx = expr::trans_into(cx, e, expr::Ignore); + } + ast::StmtDecl(d, _) => { + match d.node { + ast::DeclLocal(ref local) => { + bcx = init_local(bcx, *local); + if cx.sess().opts.extra_debuginfo { + debuginfo::create_local_var_metadata(bcx, *local); + } + } + ast::DeclItem(i) => trans_item(cx.fcx.ccx, i) + } + } + ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro") + } + + bcx = fcx.pop_and_trans_ast_cleanup_scope( + bcx, ast_util::stmt_id(s)); + + return bcx; +} + +pub fn trans_block<'a>(bcx: &'a Block<'a>, + b: &ast::Block, + dest: expr::Dest) + -> &'a Block<'a> { let _icx = push_ctxt("trans_block"); + let fcx = bcx.fcx; let mut bcx = bcx; + + fcx.push_ast_cleanup_scope(b.id); + for s in b.stmts.iter() { bcx = trans_stmt(bcx, *s); } @@ -43,27 +91,26 @@ pub fn trans_block<'a>(bcx: &'a Block<'a>, b: &ast::Block, dest: expr::Dest) assert!(dest == expr::Ignore || bcx.unreachable.get()); } } + + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id); + return bcx; } -pub fn trans_if<'a>( - bcx: &'a Block<'a>, - cond: &ast::Expr, - thn: ast::P, - els: Option<@ast::Expr>, - dest: expr::Dest) - -> &'a Block<'a> { - debug!("trans_if(bcx={}, cond={}, thn={:?}, dest={})", - bcx.to_str(), bcx.expr_to_str(cond), thn.id, +pub fn trans_if<'a>(bcx: &'a Block<'a>, + if_id: ast::NodeId, + cond: &ast::Expr, + thn: ast::P, + els: Option<@ast::Expr>, + dest: expr::Dest) + -> &'a Block<'a> { + debug!("trans_if(bcx={}, if_id={}, cond={}, thn={:?}, dest={})", + bcx.to_str(), if_id, bcx.expr_to_str(cond), thn.id, dest.to_str(bcx.ccx())); - let _indenter = indenter(); - let _icx = push_ctxt("trans_if"); + let mut bcx = bcx; - let Result {bcx, val: cond_val} = - expr::trans_to_datum(bcx, cond).to_result(); - - let cond_val = bool_to_i1(bcx, cond_val); + let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool()); // Drop branches that are known to be impossible if is_const(cond_val) && !is_undef(cond_val) { @@ -76,11 +123,8 @@ pub fn trans_if<'a>( None => {} } // if true { .. } [else { .. }] - return with_scope(bcx, thn.info(), "if_true_then", |bcx| { - let bcx_out = trans_block(bcx, thn, dest); - debuginfo::clear_source_location(bcx.fcx); - bcx_out - }) + bcx = trans_block(bcx, thn, dest); + debuginfo::clear_source_location(bcx.fcx); } else { let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ; trans.visit_block(thn, ()); @@ -88,229 +132,174 @@ pub fn trans_if<'a>( match els { // if false { .. } else { .. } Some(elexpr) => { - return with_scope(bcx, - elexpr.info(), - "if_false_then", - |bcx| { - let bcx_out = trans_if_else(bcx, elexpr, dest, false); - debuginfo::clear_source_location(bcx.fcx); - bcx_out - }) + bcx = expr::trans_into(bcx, elexpr, dest); + debuginfo::clear_source_location(bcx.fcx); } + // if false { .. } - None => return bcx, + None => { } } } - } - let then_bcx_in = scope_block(bcx, thn.info(), "then"); + return bcx; + } + let name = format!("then-block-{}-", thn.id); + let then_bcx_in = bcx.fcx.new_id_block(name, thn.id); let then_bcx_out = trans_block(then_bcx_in, thn, dest); - debuginfo::clear_source_location(bcx.fcx); - let then_bcx_out = trans_block_cleanups(then_bcx_out, - block_cleanups(then_bcx_in)); - - // Calling trans_block directly instead of trans_expr - // because trans_expr will create another scope block - // context for the block, but we've already got the - // 'else' context - let (else_bcx_in, next_bcx) = match els { - Some(elexpr) => { - let else_bcx_in = scope_block(bcx, elexpr.info(), "else"); - let else_bcx_out = trans_if_else(else_bcx_in, elexpr, dest, true); - (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out])) - } - _ => { - let next_bcx = sub_block(bcx, "next"); - Br(then_bcx_out, next_bcx.llbb); - (next_bcx, next_bcx) - } - }; + let next_bcx; + match els { + Some(elexpr) => { + let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id); + let else_bcx_out = expr::trans_into(else_bcx_in, elexpr, dest); + next_bcx = bcx.fcx.join_blocks(if_id, + [then_bcx_out, else_bcx_out]); + CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); + } - debug!("then_bcx_in={}, else_bcx_in={}", - then_bcx_in.to_str(), else_bcx_in.to_str()); + None => { + next_bcx = bcx.fcx.new_id_block("next-block", if_id); + Br(then_bcx_out, next_bcx.llbb); + CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb); + } + } // Clear the source location because it is still set to whatever has been translated // right before. - debuginfo::clear_source_location(else_bcx_in.fcx); - CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); - return next_bcx; - - // trans `else [ if { .. } ... | { .. } ]` - fn trans_if_else<'a>( - else_bcx_in: &'a Block<'a>, - elexpr: @ast::Expr, - dest: expr::Dest, - cleanup: bool) - -> &'a Block<'a> { - let else_bcx_out = match elexpr.node { - ast::ExprIf(_, _, _) => { - let elseif_blk = ast_util::block_from_expr(elexpr); - trans_block(else_bcx_in, elseif_blk, dest) - } - ast::ExprBlock(blk) => { - trans_block(else_bcx_in, blk, dest) - } - // would be nice to have a constraint on ifs - _ => else_bcx_in.tcx().sess.bug("strange alternative in if") - }; - if cleanup { - debuginfo::clear_source_location(else_bcx_in.fcx); - trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in)) - } else { - else_bcx_out - } - } -} + debuginfo::clear_source_location(next_bcx.fcx); -pub fn join_blocks<'a>( - parent_bcx: &'a Block<'a>, - in_cxs: &[&'a Block<'a>]) - -> &'a Block<'a> { - let out = sub_block(parent_bcx, "join"); - let mut reachable = false; - for bcx in in_cxs.iter() { - if !bcx.unreachable.get() { - Br(*bcx, out.llbb); - reachable = true; - } - } - if !reachable { - Unreachable(out); - } - return out; + next_bcx } -pub fn trans_while<'a>( - bcx: &'a Block<'a>, - cond: &ast::Expr, - body: &ast::Block) - -> &'a Block<'a> { +pub fn trans_while<'a>(bcx: &'a Block<'a>, + loop_id: ast::NodeId, + cond: &ast::Expr, + body: &ast::Block) + -> &'a Block<'a> { let _icx = push_ctxt("trans_while"); - let next_bcx = sub_block(bcx, "while next"); + let fcx = bcx.fcx; // bcx // | - // loop_bcx - // | // cond_bcx_in <--------+ // | | // cond_bcx_out | // | | | // | body_bcx_in | - // +------+ | | + // cleanup_blk | | // | body_bcx_out --+ - // next_bcx + // next_bcx_in + + let next_bcx_in = fcx.new_id_block("while_exit", loop_id); + let cond_bcx_in = fcx.new_id_block("while_cond", cond.id); + let body_bcx_in = fcx.new_id_block("while_body", body.id); + + fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]); - let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`while`", - body.info()); - let cond_bcx_in = scope_block(loop_bcx, cond.info(), "while loop cond"); - let body_bcx_in = scope_block(loop_bcx, body.info(), "while loop body"); - Br(bcx, loop_bcx.llbb); - Br(loop_bcx, cond_bcx_in.llbb); + Br(bcx, cond_bcx_in.llbb); + + // compile the block where we will handle loop cleanups + let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK); // compile the condition let Result {bcx: cond_bcx_out, val: cond_val} = - expr::trans_to_datum(cond_bcx_in, cond).to_result(); - let cond_val = bool_to_i1(cond_bcx_out, cond_val); - let cond_bcx_out = - trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in)); - CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb); + expr::trans(cond_bcx_in, cond).to_llbool(); + CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb); // loop body: let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); - cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb); + Br(body_bcx_out, cond_bcx_in.llbb); - return next_bcx; + fcx.pop_loop_cleanup_scope(loop_id); + return next_bcx_in; } -pub fn trans_loop<'a>( - bcx: &'a Block<'a>, - body: &ast::Block, - opt_label: Option) - -> &'a Block<'a> { +pub fn trans_loop<'a>(bcx:&'a Block<'a>, + loop_id: ast::NodeId, + body: &ast::Block) + -> &'a Block<'a> { let _icx = push_ctxt("trans_loop"); - let next_bcx = sub_block(bcx, "next"); - let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`", - body.info()); + let fcx = bcx.fcx; + + // bcx + // | + // body_bcx_in + // | + // body_bcx_out + // + // next_bcx + // + // Links between body_bcx_in and next_bcx are created by + // break statements. + + let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id); + let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id); + + fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]); + Br(bcx, body_bcx_in.llbb); let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); - cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb); - return next_bcx; + Br(body_bcx_out, body_bcx_in.llbb); + + fcx.pop_loop_cleanup_scope(loop_id); + + return next_bcx_in; } -pub fn trans_break_cont<'a>( - bcx: &'a Block<'a>, - opt_label: Option, - to_end: bool) - -> &'a Block<'a> { +pub fn trans_break_cont<'a>(bcx: &'a Block<'a>, + expr_id: ast::NodeId, + opt_label: Option, + exit: uint) + -> &'a Block<'a> { let _icx = push_ctxt("trans_break_cont"); - // Locate closest loop block, outputting cleanup as we go. - let mut unwind = bcx; - let mut cur_scope = unwind.scope.get(); - let mut target; - loop { - cur_scope = match cur_scope { - Some(&ScopeInfo { - loop_break: Some(brk), - loop_label: l, - parent, - .. - }) => { - // If we're looking for a labeled loop, check the label... - target = if to_end { - brk - } else { - unwind - }; - match opt_label { - Some(desired) => match l { - Some(actual) if actual == desired => break, - // If it doesn't match the one we want, - // don't break - _ => parent, - }, - None => break, + let fcx = bcx.fcx; + + if bcx.unreachable.get() { + return bcx; + } + + // Locate loop that we will break to + let loop_id = match opt_label { + None => fcx.top_loop_scope(), + Some(_) => { + let def_map = bcx.tcx().def_map.borrow(); + match def_map.get().find(&expr_id) { + Some(&ast::DefLabel(loop_id)) => loop_id, + ref r => { + bcx.tcx().sess.bug(format!("{:?} in def-map for label", r)) } } - Some(inf) => inf.parent, - None => { - unwind = match unwind.parent { - Some(bcx) => bcx, - // This is a return from a loop body block - None => { - Store(bcx, - C_bool(!to_end), - bcx.fcx.llretptr.get().unwrap()); - cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn())); - Unreachable(bcx); - return bcx; - } - }; - unwind.scope.get() - } } - } - cleanup_and_Br(bcx, unwind, target.llbb); - Unreachable(bcx); + }; + + // Generate appropriate cleanup code and branch + let cleanup_llbb = fcx.normal_exit_block(loop_id, exit); + Br(bcx, cleanup_llbb); + Unreachable(bcx); // anything afterwards should be ignored return bcx; } -pub fn trans_break<'a>(bcx: &'a Block<'a>, label_opt: Option) - -> &'a Block<'a> { - return trans_break_cont(bcx, label_opt, true); +pub fn trans_break<'a>(bcx: &'a Block<'a>, + expr_id: ast::NodeId, + label_opt: Option) + -> &'a Block<'a> { + return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK); } -pub fn trans_cont<'a>(bcx: &'a Block<'a>, label_opt: Option) - -> &'a Block<'a> { - return trans_break_cont(bcx, label_opt, false); +pub fn trans_cont<'a>(bcx: &'a Block<'a>, + expr_id: ast::NodeId, + label_opt: Option) + -> &'a Block<'a> { + return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP); } -pub fn trans_ret<'a>(bcx: &'a Block<'a>, e: Option<@ast::Expr>) - -> &'a Block<'a> { +pub fn trans_ret<'a>(bcx: &'a Block<'a>, + e: Option<@ast::Expr>) + -> &'a Block<'a> { let _icx = push_ctxt("trans_ret"); + let fcx = bcx.fcx; let mut bcx = bcx; let dest = match bcx.fcx.llretptr.get() { None => expr::Ignore, @@ -322,7 +311,8 @@ pub fn trans_ret<'a>(bcx: &'a Block<'a>, e: Option<@ast::Expr>) } _ => () } - cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn())); + let cleanup_llbb = fcx.return_exit_block(); + Br(bcx, cleanup_llbb); Unreachable(bcx); return bcx; } @@ -338,8 +328,8 @@ pub fn trans_fail_expr<'a>( Some(arg_expr) => { let ccx = bcx.ccx(); let tcx = ccx.tcx; - let arg_datum = unpack_datum!( - bcx, expr::trans_to_datum(bcx, arg_expr)); + let arg_datum = + unpack_datum!(bcx, expr::trans_to_lvalue(bcx, arg_expr, "fail")); if ty::type_is_str(arg_datum.ty) { let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx); diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index c2591beac4a7d..4b89be64f53be 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -9,171 +9,142 @@ // except according to those terms. /*! - * - * A `Datum` contains all the information you need to describe the LLVM - * translation of a Rust value. It describes where the value is stored, - * what Rust type the value has, whether it is addressed by reference, - * and so forth. - * - * The idea of a datum is that, to the extent possible, you should not - * care about these details, but rather use the methods on the Datum - * type to "do what you want to do". For example, you can simply call - * `copy_to()` or `move_to()` to copy or move the value into a new - * home. - * - * # Datum location - * - * The primary two fields of a datum are the `val` and the `mode`. - * The `val` is an LLVM value ref. It may either *be the value* that - * is being tracked, or it may be a *pointer to the value being - * tracked*. This is specified in the `mode` field, which can either - * be `ByValue` or `ByRef`, respectively. The (Rust) type of the - * value stored in the datum is indicated in the field `ty`. - * - * Generally speaking, you probably do not want to access the `val` field - * unless you know what mode the value is in. Instead you should use one - * of the following accessors: - * - * - `to_value_llval()` converts to by-value - * - `to_ref_llval()` converts to by-ref, allocating a stack slot if necessary - * - `to_appropriate_llval()` converts to by-value if this is an - * immediate type, by-ref otherwise. This is particularly - * convenient for interfacing with the various code floating around - * that predates datums. - * - * # Datum cleanup styles - * - * Each datum carries with it an idea of how its value will be cleaned - * up. This is primarily determined by the mode: a `ByValue` datum - * will always be cleaned up by revoking cleanup using - * `revoke_clean()`, because there is no other option. By ref datums - * can sometimes be cleaned up via `revoke_clean` (in particular, - * by-ref datums that originated from rvalues), but sometimes they - * must be zeroed. This is indicated by the `DatumCleanup` - * parameter. Note that zeroing a by-ref datum *always works* to - * cancel the cleanup, but using `revoke_clean` is preferable since - * there is no runtime cost. Some older parts of the code (notably - * `match_`, at least at the time of this writing) rely on this and - * only use zeroing. - * - * # Copying, moving, and storing - * - * There are three methods for moving the value into a new - * location: - * - * - `copy_to()` will copy the value into a new location, meaning that - * the value is first mem-copied and then the new location is "taken" - * via the take glue, in effect creating a deep clone. - * - * - `move_to()` will copy the value, meaning that the value is mem-copied - * into its new home and then the cleanup on the this datum is revoked. - * This is a "shallow" clone. After `move_to()`, the current datum - * is invalid and should no longer be used. - * - * - `store_to()` either performs a copy or a move depending on the - * Rust type of the datum. - * - * # Scratch datum - * - * Sometimes you just need some temporary scratch space. The - * `scratch_datum()` function will yield you up a by-ref datum that - * points into the stack. It's your responsibility to ensure that - * whatever you put in there gets cleaned up etc. - * - * # Other actions - * - * There are various other helper methods on Datum, such as `deref()`, - * `get_base_and_len()` and so forth. These are documented on the - * methods themselves. Most are only suitable for some types of - * values. */ - + * See the section on datums in `doc.rs` for an overview of what + * Datums are and how they are intended to be used. + */ use lib; use lib::llvm::ValueRef; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::common::*; -use middle::trans::common; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::expr; use middle::trans::glue; use middle::trans::tvec; use middle::trans::type_of; use middle::trans::write_guard; use middle::ty; -use util::common::indenter; -use util::ppaux::ty_to_str; +use util::ppaux::{ty_to_str}; -use std::uint; use syntax::ast; use syntax::codemap::Span; -#[deriving(Eq)] -pub enum CopyAction { - INIT, - DROP_EXISTING -} - +/** + * A `Datum` encapsulates the result of evaluating an expression. It + * describes where the value is stored, what Rust type the value has, + * whether it is addressed by reference, and so forth. Please refer + * the section on datums in `doc.rs` for more details. + */ #[deriving(Clone)] -pub struct Datum { +pub struct Datum { /// The llvm value. This is either a pointer to the Rust value or - /// the value itself, depending on `mode` below. + /// the value itself, depending on `kind` below. val: ValueRef, /// The rust type of the value. ty: ty::t, /// Indicates whether this is by-ref or by-value. - mode: DatumMode, + kind: K, } -pub struct DatumBlock<'a> { +pub struct DatumBlock<'a, K> { bcx: &'a Block<'a>, - datum: Datum, + datum: Datum, +} + +pub enum Expr { + /// a fresh value that was produced and which has no cleanup yet + /// because it has not yet "landed" into its permanent home + RvalueExpr(Rvalue), + + /// `val` is a pointer into memory for which a cleanup is scheduled + /// (and thus has type *T). If you move out of an Lvalue, you must + /// zero out the memory (FIXME #5016). + LvalueExpr, +} + +#[deriving(Clone)] +pub struct Lvalue; + +pub struct Rvalue { + mode: RvalueMode +} + +pub fn Rvalue(m: RvalueMode) -> Rvalue { + Rvalue { mode: m } } -#[deriving(Clone, Eq, IterBytes)] -pub enum DatumMode { - /// `val` is a pointer to the actual value (and thus has type *T). - /// The argument indicates how to cancel cleanup of this datum if - /// the value is moved elsewhere, which can either be by zeroing - /// the memory or by canceling a registered cleanup. - ByRef(DatumCleanup), +// Make Datum linear for more type safety. +impl Drop for Rvalue { + fn drop(&mut self) { } +} + +#[deriving(Eq, IterBytes)] +pub enum RvalueMode { + /// `val` is a pointer to the actual value (and thus has type *T) + ByRef, /// `val` is the actual value (*only used for immediates* like ints, ptrs) ByValue, } -impl DatumMode { - pub fn is_by_ref(&self) -> bool { - match *self { ByRef(_) => true, ByValue => false } - } +pub fn Datum(val: ValueRef, ty: ty::t, kind: K) -> Datum { + Datum { val: val, ty: ty, kind: kind } +} - pub fn is_by_value(&self) -> bool { - match *self { ByRef(_) => false, ByValue => true } - } +pub fn DatumBlock<'a, K>(bcx: &'a Block<'a>, + datum: Datum) + -> DatumBlock<'a, K> { + DatumBlock { bcx: bcx, datum: datum } } -/// See `Datum cleanup styles` section at the head of this module. -#[deriving(Clone, Eq, IterBytes)] -pub enum DatumCleanup { - RevokeClean, - ZeroMem +pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { + return Datum(val, ty, Rvalue(ByValue)); } -pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { - return Datum {val: val, ty: ty, mode: ByValue}; +pub fn immediate_rvalue_bcx<'a>(bcx: &'a Block<'a>, + val: ValueRef, + ty: ty::t) + -> DatumBlock<'a, Rvalue> { + return DatumBlock(bcx, immediate_rvalue(val, ty)) } -pub fn immediate_rvalue_bcx<'a>(bcx: &'a Block<'a>, val: ValueRef, ty: ty::t) - -> DatumBlock<'a> { - DatumBlock { - bcx: bcx, - datum: immediate_rvalue(val, ty), - } + +pub fn lvalue_scratch_datum<'a, A>(bcx: &'a Block<'a>, + ty: ty::t, + name: &str, + zero: bool, + scope: cleanup::ScopeId, + arg: A, + populate: |A, &'a Block<'a>, ValueRef| + -> &'a Block<'a>) + -> DatumBlock<'a, Lvalue> { + /*! + * Allocates temporary space on the stack using alloca() and + * returns a by-ref Datum pointing to it. The memory will be + * dropped upon exit from `scope`. The callback `populate` should + * initialize the memory. If `zero` is true, the space will be + * zeroed when it is allocated; this is not necessary unless `bcx` + * does not dominate the end of `scope`. + */ + + let llty = type_of::type_of(bcx.ccx(), ty); + let scratch = alloca_maybe_zeroed(bcx, llty, name, zero); + + // Subtle. Populate the scratch memory *before* scheduling cleanup. + let bcx = populate(arg, bcx, scratch); + bcx.fcx.schedule_drop_mem(scope, scratch, ty); + + DatumBlock(bcx, Datum(scratch, ty, Lvalue)) } -pub fn scratch_datum(bcx: &Block, ty: ty::t, name: &str, zero: bool) - -> Datum { +pub fn rvalue_scratch_datum(bcx: &Block, + ty: ty::t, + name: &str) + -> Datum { /*! * Allocates temporary space on the stack using alloca() and * returns a by-ref Datum pointing to it. If `zero` is true, the @@ -185,11 +156,15 @@ pub fn scratch_datum(bcx: &Block, ty: ty::t, name: &str, zero: bool) */ let llty = type_of::type_of(bcx.ccx(), ty); - let scratch = alloca_maybe_zeroed(bcx, llty, name, zero); - Datum { val: scratch, ty: ty, mode: ByRef(RevokeClean) } + let scratch = alloca_maybe_zeroed(bcx, llty, name, false); + Datum(scratch, ty, Rvalue(ByRef)) +} + +pub fn is_by_value_type(ccx: &CrateContext, ty: ty::t) -> bool { + appropriate_rvalue_mode(ccx, ty) == ByValue } -pub fn appropriate_mode(ccx: &CrateContext, ty: ty::t) -> DatumMode { +pub fn appropriate_rvalue_mode(ccx: &CrateContext, ty: ty::t) -> RvalueMode { /*! * Indicates the "appropriate" mode for this value, * which is either by ref or by value, depending @@ -201,505 +176,356 @@ pub fn appropriate_mode(ccx: &CrateContext, ty: ty::t) -> DatumMode { } else if type_is_immediate(ccx, ty) { ByValue } else { - ByRef(RevokeClean) + ByRef } } -impl Datum { - pub fn store_to<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - dst: ValueRef) - -> &'a Block<'a> { - /*! - * - * Stores this value into its final home. This moves if - * `id` is located in the move table, but copies otherwise. - */ - - if ty::type_moves_by_default(bcx.tcx(), self.ty) { - self.move_to(bcx, action, dst) - } else { - self.copy_to(bcx, action, dst) - } +fn add_rvalue_clean(mode: RvalueMode, + fcx: &FunctionContext, + scope: cleanup::ScopeId, + val: ValueRef, + ty: ty::t) { + match mode { + ByValue => { fcx.schedule_drop_immediate(scope, val, ty); } + ByRef => { fcx.schedule_drop_mem(scope, val, ty); } } +} - pub fn store_to_dest<'a>(&self, bcx: &'a Block<'a>, dest: expr::Dest) - -> &'a Block<'a> { - match dest { - expr::Ignore => { - return bcx; - } - expr::SaveIn(addr) => { - return self.store_to(bcx, INIT, addr); - } - } - } +pub trait KindOps { - pub fn store_to_datum<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - datum: Datum) - -> &'a Block<'a> { - debug!("store_to_datum(self={}, action={:?}, datum={})", - self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx())); - assert!(datum.mode.is_by_ref()); - self.store_to(bcx, action, datum.val) - } - - pub fn move_to_datum<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - datum: Datum) - -> &'a Block<'a> { - assert!(datum.mode.is_by_ref()); - self.move_to(bcx, action, datum.val) - } - - pub fn copy_to_datum<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - datum: Datum) - -> &'a Block<'a> { - assert!(datum.mode.is_by_ref()); - self.copy_to(bcx, action, datum.val) - } - - pub fn copy_to<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - dst: ValueRef) - -> &'a Block<'a> { - /*! - * - * Copies the value into `dst`, which should be a pointer to a - * memory location suitable for `self.ty`. You PROBABLY want - * `store_to()` instead, which will move if possible but copy if - * neccessary. */ + /** + * Take appropriate action after the value in `datum` has been + * stored to a new location. + */ + fn post_store<'a>(&self, + bcx: &'a Block<'a>, + val: ValueRef, + ty: ty::t) + -> &'a Block<'a>; + + /** + * True if this mode is a reference mode, meaning that the datum's + * val field is a pointer to the actual value + */ + fn is_by_ref(&self) -> bool; - let _icx = push_ctxt("copy_to"); + /** + * Converts to an Expr kind + */ + fn to_expr_kind(self) -> Expr; - if ty::type_is_voidish(bcx.tcx(), self.ty) { - return bcx; - } +} - debug!("copy_to(self={}, action={:?}, dst={})", - self.to_str(bcx.ccx()), action, bcx.val_to_str(dst)); - - // Watch out for the case where we are writing the copying the - // value into the same location we read it out from. We want - // to avoid the case where we drop the existing value, which - // frees it, and then overwrite it with itself (which has been - // freed). - if action == DROP_EXISTING && - ty::type_needs_drop(bcx.tcx(), self.ty) - { - match self.mode { - ByRef(_) => { - let cast = PointerCast(bcx, dst, val_ty(self.val)); - let cmp = ICmp(bcx, lib::llvm::IntNE, cast, self.val); - with_cond(bcx, cmp, |bcx| { - self.copy_to_no_check(bcx, action, dst) - }) - } - ByValue => { - self.copy_to_no_check(bcx, action, dst) - } - } - } else { - self.copy_to_no_check(bcx, action, dst) - } +impl KindOps for Rvalue { + fn post_store<'a>(&self, + bcx: &'a Block<'a>, + _val: ValueRef, + _ty: ty::t) + -> &'a Block<'a> { + // No cleanup is scheduled for an rvalue, so we don't have + // to do anything after a move to cancel or duplicate it. + bcx } - pub fn copy_to_no_check<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - dst: ValueRef) - -> &'a Block<'a> { - /*! - * - * A helper for `copy_to()` which does not check to see if we - * are copying to/from the same value. */ + fn is_by_ref(&self) -> bool { + self.mode == ByRef + } - let _icx = push_ctxt("copy_to_no_check"); - let mut bcx = bcx; + fn to_expr_kind(self) -> Expr { + RvalueExpr(self) + } +} - if action == DROP_EXISTING { - bcx = glue::drop_ty(bcx, dst, self.ty); - } +impl KindOps for Lvalue { + fn post_store<'a>(&self, + bcx: &'a Block<'a>, + val: ValueRef, + ty: ty::t) + -> &'a Block<'a> { + /*! + * If an lvalue is moved, we must zero out the memory in which + * it resides so as to cancel cleanup. If an @T lvalue is + * copied, we must increment the reference count. + */ - match self.mode { - ByValue => { - Store(bcx, self.val, dst); - } - ByRef(_) => { - memcpy_ty(bcx, dst, self.val, self.ty); + if ty::type_needs_drop(bcx.tcx(), ty) { + if ty::type_moves_by_default(bcx.tcx(), ty) { + // cancel cleanup of affine values by zeroing out + let () = zero_mem(bcx, val, ty); + bcx + } else { + // incr. refcount for @T or newtype'd @T + glue::take_ty(bcx, val, ty) } + } else { + bcx } - - return glue::take_ty(bcx, dst, self.ty); } - // This works like copy_val, except that it deinitializes the source. - // Since it needs to zero out the source, src also needs to be an lval. - // - pub fn move_to<'a>( - &self, - bcx: &'a Block<'a>, - action: CopyAction, - dst: ValueRef) - -> &'a Block<'a> { - let _icx = push_ctxt("move_to"); - let mut bcx = bcx; + fn is_by_ref(&self) -> bool { + true + } - debug!("move_to(self={}, action={:?}, dst={})", - self.to_str(bcx.ccx()), action, bcx.val_to_str(dst)); + fn to_expr_kind(self) -> Expr { + LvalueExpr + } +} - if ty::type_is_voidish(bcx.tcx(), self.ty) { - return bcx; +impl KindOps for Expr { + fn post_store<'a>(&self, + bcx: &'a Block<'a>, + val: ValueRef, + ty: ty::t) + -> &'a Block<'a> { + match *self { + LvalueExpr => Lvalue.post_store(bcx, val, ty), + RvalueExpr(ref r) => r.post_store(bcx, val, ty), } + } - if action == DROP_EXISTING { - bcx = glue::drop_ty(bcx, dst, self.ty); + fn is_by_ref(&self) -> bool { + match *self { + LvalueExpr => Lvalue.is_by_ref(), + RvalueExpr(ref r) => r.is_by_ref() } + } - match self.mode { - ByRef(_) => { - memcpy_ty(bcx, dst, self.val, self.ty); - } - ByValue => { - Store(bcx, self.val, dst); - } - } + fn to_expr_kind(self) -> Expr { + self + } +} - self.cancel_clean(bcx); +impl Datum { + pub fn add_clean(self, + fcx: &FunctionContext, + scope: cleanup::ScopeId) + -> ValueRef { + /*! + * Schedules a cleanup for this datum in the given scope. + * That means that this datum is no longer an rvalue datum; + * hence, this function consumes the datum and returns the + * contained ValueRef. + */ - return bcx; + add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty); + self.val } - pub fn add_clean(&self, bcx: &Block) { + pub fn to_lvalue_datum_in_scope<'a>(self, + bcx: &'a Block<'a>, + name: &str, + scope: cleanup::ScopeId) + -> DatumBlock<'a, Lvalue> { /*! - * Schedules this datum for cleanup in `bcx`. The datum - * must be an rvalue. + * Returns an lvalue datum (that is, a by ref datum with + * cleanup scheduled). If `self` is not already an lvalue, + * cleanup will be scheduled in the temporary scope for `expr_id`. */ + let fcx = bcx.fcx; - match self.mode { - ByValue => { - add_clean_temp_immediate(bcx, self.val, self.ty); + match self.kind.mode { + ByRef => { + add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty); + DatumBlock(bcx, Datum(self.val, self.ty, Lvalue)) } - ByRef(RevokeClean) => { - add_clean_temp_mem(bcx, self.val, self.ty); + + ByValue => { + lvalue_scratch_datum( + bcx, self.ty, name, false, scope, self, + |this, bcx, llval| this.store_to(bcx, llval)) } - ByRef(ZeroMem) => { - add_clean(bcx, self.val, self.ty) + } + } + + pub fn to_ref_datum<'a>(self, bcx: &'a Block<'a>) -> DatumBlock<'a, Rvalue> { + let mut bcx = bcx; + match self.kind.mode { + ByRef => DatumBlock(bcx, self), + ByValue => { + let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref"); + bcx = self.store_to(bcx, scratch.val); + DatumBlock(bcx, scratch) } } } - pub fn cancel_clean(&self, bcx: &Block) { - if ty::type_needs_drop(bcx.tcx(), self.ty) { - match self.mode { - ByValue | - ByRef(RevokeClean) => { - revoke_clean(bcx, self.val); - } - ByRef(ZeroMem) => { - // Lvalues which potentially need to be dropped - // must be passed by ref, so that we can zero them - // out. - zero_mem(bcx, self.val, self.ty); + pub fn to_appropriate_datum<'a>(self, + bcx: &'a Block<'a>) + -> DatumBlock<'a, Rvalue> { + match self.appropriate_rvalue_mode(bcx.ccx()) { + ByRef => { + self.to_ref_datum(bcx) + } + ByValue => { + match self.kind.mode { + ByValue => DatumBlock(bcx, self), + ByRef => { + let llval = load(bcx, self.val, self.ty); + DatumBlock(bcx, Datum(llval, self.ty, Rvalue(ByValue))) + } } } } } +} - pub fn to_str(&self, ccx: &CrateContext) -> ~str { - format!("Datum \\{ val={}, ty={}, mode={:?} \\}", - ccx.tn.val_to_str(self.val), - ty_to_str(ccx.tcx, self.ty), - self.mode) +/** + * Methods suitable for "expr" datums that could be either lvalues or + * rvalues. These include coercions into lvalues/rvalues but also a number + * of more general operations. (Some of those operations could be moved to + * the more general `impl Datum`, but it's convenient to have them + * here since we can `match self.kind` rather than having to implement + * generic methods in `KindOps`.) + */ +impl Datum { + fn match_kind(self, + if_lvalue: |Datum| -> R, + if_rvalue: |Datum| -> R) + -> R { + let Datum { val, ty, kind } = self; + match kind { + LvalueExpr => if_lvalue(Datum(val, ty, Lvalue)), + RvalueExpr(r) => if_rvalue(Datum(val, ty, r)), + } } - pub fn to_value_datum(&self, bcx: &Block) -> Datum { - /*! - * - * Yields a by-value form of this datum. This may involve - * creation of a temporary stack slot. The value returned by - * this function is not separately rooted from this datum, so - * it will not live longer than the current datum. */ - - match self.mode { - ByValue => *self, - ByRef(_) => { - Datum {val: self.to_value_llval(bcx), mode: ByValue, - ty: self.ty} - } - } + pub fn is_by_ref(&self) -> bool { + self.kind.is_by_ref() } - pub fn to_value_llval(&self, bcx: &Block) -> ValueRef { + pub fn assert_lvalue(self, bcx: &Block) -> Datum { /*! - * - * Yields the value itself. */ + * Asserts that this datum *is* an lvalue and returns it. + */ - if ty::type_is_voidish(bcx.tcx(), self.ty) { - C_nil() - } else { - match self.mode { - ByValue => self.val, - ByRef(_) => { - if ty::type_is_bool(self.ty) { - LoadRangeAssert(bcx, self.val, 0, 2, lib::llvm::True) - } else { - Load(bcx, self.val) - } - } - } - } + self.match_kind( + |d| d, + |_| bcx.sess().bug("assert_lvalue given rvalue")) } - pub fn to_ref_datum(&self, bcx: &Block) -> Datum { + pub fn assert_rvalue(self, bcx: &Block) -> Datum { /*! - * Yields a by-ref form of this datum. This may involve - * creation of a temporary stack slot. The value returned by - * this function is not separately rooted from this datum, so - * it will not live longer than the current datum. + * Asserts that this datum *is* an lvalue and returns it. */ - match self.mode { - ByRef(_) => *self, - ByValue => { - Datum {val: self.to_ref_llval(bcx), mode: ByRef(RevokeClean), - ty: self.ty} - } - } + self.match_kind( + |_| bcx.sess().bug("assert_rvalue given lvalue"), + |r| r) } - pub fn to_ref_llval(&self, bcx: &Block) -> ValueRef { - match self.mode { - ByRef(_) => self.val, - ByValue => { - if ty::type_is_voidish(bcx.tcx(), self.ty) { - C_null(type_of::type_of(bcx.ccx(), self.ty).ptr_to()) - } else { - let slot = alloc_ty(bcx, self.ty, ""); - // The store created here can be modified through a reference, for example: - // - // // free the old allocation, and change the pointer to a new allocation - // fn foo(x: &mut ~u8) { - // *x = ~5; - // } - // - // foo(&mut ~5); - Store(bcx, self.val, slot); - // The old cleanup needs to be cancelled, in order for the destructor to observe - // any changes made through the reference. - self.cancel_clean(bcx); - add_clean_temp_mem(bcx, slot, self.ty); - slot - } + pub fn store_to_dest<'a>(self, + bcx: &'a Block<'a>, + dest: expr::Dest, + expr_id: ast::NodeId) + -> &'a Block<'a> { + match dest { + expr::Ignore => { + self.add_clean_if_rvalue(bcx, expr_id); + bcx + } + expr::SaveIn(addr) => { + self.store_to(bcx, addr) } } } - pub fn appropriate_mode(&self, ccx: &CrateContext) -> DatumMode { - /*! See the `appropriate_mode()` function */ - - appropriate_mode(ccx, self.ty) - } - - pub fn to_appropriate_llval(&self, bcx: &Block) -> ValueRef { + pub fn add_clean_if_rvalue<'a>(self, + bcx: &'a Block<'a>, + expr_id: ast::NodeId) { /*! - * - * Yields an llvalue with the `appropriate_mode()`. */ + * Arranges cleanup for `self` if it is an rvalue. Use when + * you are done working with a value that may need drop. + */ - match self.appropriate_mode(bcx.ccx()) { - ByValue => self.to_value_llval(bcx), - ByRef(_) => self.to_ref_llval(bcx) - } + self.match_kind( + |_| { /* Nothing to do, cleanup already arranged */ }, + |r| { + let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); + r.add_clean(bcx.fcx, scope); + }) } - pub fn to_appropriate_datum(&self, bcx: &Block) -> Datum { + pub fn clean<'a>(self, + bcx: &'a Block<'a>, + name: &'static str, + expr_id: ast::NodeId) + -> &'a Block<'a> { /*! - * - * Yields a datum with the `appropriate_mode()`. */ - - match self.appropriate_mode(bcx.ccx()) { - ByValue => self.to_value_datum(bcx), - ByRef(_) => self.to_ref_datum(bcx) - } - } + * Ensures that `self` will get cleaned up, if it is not an lvalue + * already. + */ - pub fn get_element(&self, - bcx: &Block, - ty: ty::t, - source: DatumCleanup, - gep: |ValueRef| -> ValueRef) - -> Datum { - let base_val = self.to_ref_llval(bcx); - Datum { - val: gep(base_val), - mode: ByRef(source), - ty: ty, - } + self.to_lvalue_datum(bcx, name, expr_id).bcx } - pub fn drop_val<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> { - if !ty::type_needs_drop(bcx.tcx(), self.ty) { - return bcx; - } - - return match self.mode { - ByRef(_) => glue::drop_ty(bcx, self.val, self.ty), - ByValue => glue::drop_ty_immediate(bcx, self.val, self.ty) - }; + pub fn to_lvalue_datum<'a>(self, + bcx: &'a Block<'a>, + name: &str, + expr_id: ast::NodeId) + -> DatumBlock<'a, Lvalue> { + self.match_kind( + |l| DatumBlock(bcx, l), + |r| { + let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); + r.to_lvalue_datum_in_scope(bcx, name, scope) + }) } - pub fn box_body(&self, bcx: &Block) -> Datum { + pub fn to_rvalue_datum<'a>(self, + bcx: &'a Block<'a>, + name: &'static str) + -> DatumBlock<'a, Rvalue> { /*! - * - * This datum must represent an @T or ~T box. Returns a new - * by-ref datum of type T, pointing at the contents. */ - - let (content_ty, header) = match ty::get(self.ty).sty { - ty::ty_box(typ) => (typ, true), - ty::ty_uniq(typ) => (typ, false), - _ => { - bcx.tcx().sess.bug(format!( - "box_body() invoked on non-box type {}", - ty_to_str(bcx.tcx(), self.ty))); - } - }; - - if !header { - let ptr = self.to_value_llval(bcx); - let ty = type_of::type_of(bcx.ccx(), content_ty); - let body = PointerCast(bcx, ptr, ty.ptr_to()); - Datum {val: body, ty: content_ty, mode: ByRef(ZeroMem)} - } else { // has a header - let ptr = self.to_value_llval(bcx); - let body = opaque_box_body(bcx, content_ty, ptr); - Datum {val: body, ty: content_ty, mode: ByRef(ZeroMem)} - } - } + * Ensures that we have an rvalue datum (that is, a datum with + * no cleanup scheduled). + */ - pub fn to_rptr(&self, bcx: &Block) -> Datum { - //! Returns a new datum of region-pointer type containing the - //! the same ptr as this datum (after converting to by-ref - //! using `to_ref_llval()`). - - // Convert to ref, yielding lltype *T. Then create a Rust - // type &'static T (which translates to *T). Construct new - // result (which will be by-value). Note that it is not - // significant *which* region we pick here. - let llval = self.to_ref_llval(bcx); - let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, - self.ty); - Datum {val: llval, ty: rptr_ty, mode: ByValue} - } - - /// bcx: Block wherein to generate insns. - /// span: Location where deref occurs. - /// expr_id: ID of deref expr. - /// derefs: Number of times deref'd already. - /// is_auto: If true, only deref if auto-derefable. - pub fn try_deref<'a>( - &self, - bcx: &'a Block<'a>, - span: Span, - expr_id: ast::NodeId, - derefs: uint, - is_auto: bool) - -> (Option, &'a Block<'a>) { - debug!("try_deref(expr_id={:?}, derefs={:?}, is_auto={}, self={:?})", - expr_id, derefs, is_auto, self.to_str(bcx.ccx())); - - let bcx = - write_guard::root_and_write_guard( - self, bcx, span, expr_id, derefs); - - match ty::get(self.ty).sty { - ty::ty_box(_) | ty::ty_uniq(_) => { - return (Some(self.box_body(bcx)), bcx); - } - ty::ty_ptr(mt) => { - if is_auto { // unsafe ptrs are not AUTO-derefable - return (None, bcx); - } else { - return (Some(deref_ptr(bcx, self, mt.ty)), bcx); + let mut bcx = bcx; + self.match_kind( + |l| { + match l.appropriate_rvalue_mode(bcx.ccx()) { + ByRef => { + let scratch = rvalue_scratch_datum(bcx, l.ty, name); + bcx = l.store_to(bcx, scratch.val); + DatumBlock(bcx, scratch) + } + ByValue => { + let v = load(bcx, l.val, l.ty); + l.kind.post_store(bcx, l.val, l.ty); + DatumBlock(bcx, Datum(v, l.ty, Rvalue(ByValue))) + } } - } - ty::ty_rptr(_, mt) => { - return (Some(deref_ptr(bcx, self, mt.ty)), bcx); - } - _ => { // not derefable. - return (None, bcx); - } - } - - fn deref_ptr(bcx: &Block, lv: &Datum, ty: ty::t) -> Datum { - Datum { - val: lv.to_value_llval(bcx), - ty: ty, - mode: ByRef(ZeroMem) - } - } + }, + |r| DatumBlock(bcx, r)) } - /// expr: The deref expression. - pub fn deref<'a>( - &self, - bcx: &'a Block<'a>, - expr: &ast::Expr, - derefs: uint) - -> DatumBlock<'a> { - match self.try_deref(bcx, expr.span, expr.id, derefs, false) { - (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres }, - (None, _) => { - bcx.ccx().sess.span_bug(expr.span, - "Cannot deref this expression"); - } - } +} + +/** + * Methods suitable only for lvalues. These include the various + * operations to extract components out of compound data structures, + * such as extracting the field from a struct or a particular element + * from an array. + */ +impl Datum { + pub fn to_llref(self) -> ValueRef { + /*! + * Converts a datum into a by-ref value. The datum type must + * be one which is always passed by reference. + */ + + self.val } - pub fn autoderef<'a>( - &self, - bcx: &'a Block<'a>, - span: Span, - expr_id: ast::NodeId, - max: uint) - -> DatumBlock<'a> { - let _icx = push_ctxt("autoderef"); - - debug!("autoderef(expr_id={}, max={:?}, self={:?})", - expr_id, max, self.to_str(bcx.ccx())); - let _indenter = indenter(); - - let mut datum = *self; - let mut derefs = 0u; - let mut bcx = bcx; - while derefs < max { - derefs += 1u; - match datum.try_deref(bcx, span, expr_id, derefs, true) { - (None, new_bcx) => { bcx = new_bcx; break } - (Some(datum_deref), new_bcx) => { - datum = datum_deref; - bcx = new_bcx; - } - } + pub fn get_element(&self, + ty: ty::t, + gep: |ValueRef| -> ValueRef) + -> Datum { + Datum { + val: gep(self.val), + kind: Lvalue, + ty: ty, } - - // either we were asked to deref a specific number of times, - // in which case we should have, or we asked to deref as many - // times as we can - assert!(derefs == max || max == uint::max_value); - DatumBlock { bcx: bcx, datum: datum } } pub fn get_vec_base_and_byte_len<'a>( @@ -723,17 +549,15 @@ impl Datum { //! Converts a vector into the slice pair. Des not root //! nor perform write guard checks. - let llval = self.to_appropriate_llval(bcx); - tvec::get_base_and_byte_len(bcx, llval, self.ty) + tvec::get_base_and_byte_len(bcx, self.val, self.ty) } - pub fn get_vec_base_and_len<'a>( - &self, - mut bcx: &'a Block<'a>, - span: Span, - expr_id: ast::NodeId, - derefs: uint) - -> (&'a Block<'a>, ValueRef, ValueRef) { + pub fn get_vec_base_and_len<'a>(&self, + mut bcx: &'a Block<'a>, + span: Span, + expr_id: ast::NodeId, + derefs: uint) + -> (&'a Block<'a>, ValueRef, ValueRef) { //! Converts a vector into the slice pair. Performs rooting //! and write guards checks. @@ -744,12 +568,118 @@ impl Datum { } pub fn get_vec_base_and_len_no_root<'a>(&self, bcx: &'a Block<'a>) - -> (ValueRef, ValueRef) { + -> (ValueRef, ValueRef) { //! Converts a vector into the slice pair. Des not root //! nor perform write guard checks. - let llval = self.to_appropriate_llval(bcx); - tvec::get_base_and_len(bcx, llval, self.ty) + tvec::get_base_and_len(bcx, self.val, self.ty) + } +} + +fn load<'a>(bcx: &'a Block<'a>, llptr: ValueRef, ty: ty::t) -> ValueRef { + /*! + * Private helper for loading from a by-ref datum. Handles various + * special cases where the type gives us better information about + * what we are loading. + */ + + if ty::type_is_voidish(bcx.tcx(), ty) { + C_nil() + } else if ty::type_is_bool(ty) { + LoadRangeAssert(bcx, llptr, 0, 2, lib::llvm::True) + } else { + Load(bcx, llptr) + } +} + +/** + * Generic methods applicable to any sort of datum. + */ +impl Datum { + pub fn to_expr_datum(self) -> Datum { + let Datum { val, ty, kind } = self; + Datum { val: val, ty: ty, kind: kind.to_expr_kind() } + } + + pub fn store_to<'a>(self, + bcx: &'a Block<'a>, + dst: ValueRef) + -> &'a Block<'a> { + /*! + * Moves or copies this value into a new home, as appropriate + * depending on the type of the datum. This method consumes + * the datum, since it would be incorrect to go on using the + * datum if the value represented is affine (and hence the value + * is moved). + */ + + self.shallow_copy(bcx, dst); + + self.kind.post_store(bcx, self.val, self.ty) + } + + fn shallow_copy<'a>(&self, + bcx: &'a Block<'a>, + dst: ValueRef) + -> &'a Block<'a> { + /*! + * Helper function that performs a shallow copy of this value + * into `dst`, which should be a pointer to a memory location + * suitable for `self.ty`. `dst` should contain uninitialized + * memory (either newly allocated, zeroed, or dropped). + * + * This function is private to datums because it leaves memory + * in an unstable state, where the source value has been + * copied but not zeroed. Public methods are `store_to` (if + * you no longer need the source value) or + * `shallow_copy_and_take` (if you wish the source value to + * remain valid). + */ + + let _icx = push_ctxt("copy_to_no_check"); + + if ty::type_is_voidish(bcx.tcx(), self.ty) { + return bcx; + } + + if self.kind.is_by_ref() { + memcpy_ty(bcx, dst, self.val, self.ty); + } else { + Store(bcx, self.val, dst); + } + + return bcx; + } + + pub fn shallow_copy_and_take<'a>(&self, + bcx: &'a Block<'a>, + dst: ValueRef) + -> &'a Block<'a> { + /*! + * Copies the value into a new location and runs any necessary + * take glue on the new location. This function always + * preserves the existing datum as a valid value. Therefore, + * it does not consume `self` and, also, cannot be applied to + * affine values (since they must never be duplicated). + */ + + assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty)); + let mut bcx = bcx; + bcx = self.shallow_copy(bcx, dst); + glue::take_ty(bcx, dst, self.ty) + } + + pub fn to_str(&self, ccx: &CrateContext) -> ~str { + format!("Datum({}, {}, {:?})", + ccx.tn.val_to_str(self.val), + ty_to_str(ccx.tcx, self.ty), + self.kind) + } + + pub fn appropriate_rvalue_mode(&self, ccx: &CrateContext) -> RvalueMode { + /*! See the `appropriate_rvalue_mode()` function */ + + appropriate_rvalue_mode(ccx, self.ty) } pub fn root_and_write_guard<'a>( @@ -762,47 +692,58 @@ impl Datum { write_guard::root_and_write_guard(self, bcx, span, expr_id, derefs) } - pub fn to_result<'a>(&self, bcx: &'a Block<'a>) -> common::Result<'a> { - rslt(bcx, self.to_appropriate_llval(bcx)) - } -} - -impl<'a> DatumBlock<'a> { - pub fn unpack(&self, bcx: &mut &'a Block<'a>) -> Datum { - *bcx = self.bcx; - return self.datum; - } + pub fn to_llscalarish<'a>(self, bcx: &'a Block<'a>) -> ValueRef { + /*! + * Converts `self` into a by-value `ValueRef`. Consumes this + * datum (i.e., absolves you of responsibility to cleanup the + * value). For this to work, the value must be something + * scalar-ish (like an int or a pointer) which (1) does not + * require drop glue and (2) is naturally passed around by + * value, and not by reference. + */ - pub fn assert_by_ref(&self) -> DatumBlock<'a> { - assert!(self.datum.mode.is_by_ref()); - *self + assert!(!ty::type_needs_drop(bcx.tcx(), self.ty)); + assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue); + if self.kind.is_by_ref() { + load(bcx, self.val, self.ty) + } else { + self.val + } } - pub fn drop_val(&self) -> &'a Block<'a> { - self.datum.drop_val(self.bcx) + pub fn to_llbool<'a>(self, bcx: &'a Block<'a>) -> ValueRef { + assert!(ty::type_is_bool(self.ty) || ty::type_is_bot(self.ty)) + let cond_val = self.to_llscalarish(bcx); + bool_to_i1(bcx, cond_val) } +} - pub fn store_to(&self, action: CopyAction, dst: ValueRef) - -> &'a Block<'a> { - self.datum.store_to(self.bcx, action, dst) +impl<'a, K:KindOps> DatumBlock<'a, K> { + pub fn to_expr_datumblock(self) -> DatumBlock<'a, Expr> { + DatumBlock(self.bcx, self.datum.to_expr_datum()) } +} - pub fn copy_to(&self, action: CopyAction, dst: ValueRef) - -> &'a Block<'a> { - self.datum.copy_to(self.bcx, action, dst) +impl<'a> DatumBlock<'a, Expr> { + pub fn assert_by_ref(self) -> DatumBlock<'a, Expr> { + assert!(self.datum.kind.is_by_ref()); + self } - pub fn move_to(&self, action: CopyAction, dst: ValueRef) - -> &'a Block<'a> { - self.datum.move_to(self.bcx, action, dst) + pub fn store_to(self, dst: ValueRef) -> &'a Block<'a> { + let DatumBlock { bcx, datum } = self; + datum.store_to(bcx, dst) } - pub fn to_value_llval(&self) -> ValueRef { - self.datum.to_value_llval(self.bcx) + pub fn store_to_dest(self, + dest: expr::Dest, + expr_id: ast::NodeId) -> &'a Block<'a> { + let DatumBlock { bcx, datum } = self; + datum.store_to_dest(bcx, dest, expr_id) } - pub fn to_result(&self) -> common::Result<'a> { - rslt(self.bcx, self.datum.to_appropriate_llval(self.bcx)) + pub fn shallow_copy(self, dst: ValueRef) -> &'a Block<'a> { + self.datum.shallow_copy(self.bcx, dst) } pub fn ccx(&self) -> @CrateContext { @@ -816,4 +757,9 @@ impl<'a> DatumBlock<'a> { pub fn to_str(&self) -> ~str { self.datum.to_str(self.ccx()) } + + pub fn to_llbool(self) -> Result<'a> { + let DatumBlock { datum, bcx } = self; + rslt(bcx, datum.to_llbool(bcx)) + } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 4e8b3c78dc872..88f38d9f4fd75 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -133,7 +133,7 @@ use middle::trans::adt; use middle::trans::base; use middle::trans::build; use middle::trans::common::*; -use middle::trans::datum; +use middle::trans::datum::{Datum, Lvalue}; use middle::trans::machine; use middle::trans::type_of; use middle::trans::type_::Type; @@ -396,7 +396,7 @@ pub fn create_match_binding_metadata(bcx: &Block, variable_ident: ast::Ident, node_id: ast::NodeId, span: Span, - datum: datum::Datum) { + datum: Datum) { if fn_should_be_ignored(bcx.fcx) { return; } diff --git a/src/librustc/middle/trans/doc.rs b/src/librustc/middle/trans/doc.rs new file mode 100644 index 0000000000000..c781d1dcbbfe5 --- /dev/null +++ b/src/librustc/middle/trans/doc.rs @@ -0,0 +1,227 @@ +/*! + +# Documentation for the trans module + +This module contains high-level summaries of how the various modules +in trans work. It is a work in progress. For detailed comments, +naturally, you can refer to the individual modules themselves. + +## The Expr module + +The expr module handles translation of expressions. The most general +translation routine is `trans()`, which will translate an expression +into a datum. `trans_into()` is also available, which will translate +an expression and write the result directly into memory, sometimes +avoiding the need for a temporary stack slot. Finally, +`trans_to_lvalue()` is available if you'd like to ensure that the +result has cleanup scheduled. + +Internally, each of these functions dispatches to various other +expression functions depending on the kind of expression. We divide +up expressions into: + +- **Datum expressions:** Those that most naturally yield values. + Examples would be `22`, `~x`, or `a + b` (when not overloaded). +- **DPS expressions:** Those that most naturally write into a location + in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`. +- **Statement expressions:** That that do not generate a meaningful + result. Examples would be `while { ... }` or `return 44`. + +## The Datum module + +A `Datum` encapsulates the result of evaluating a Rust expression. It +contains a `ValueRef` indicating the result, a `ty::t` describing the +the Rust type, but also a *kind*. The kind indicates whether the datum +has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of +rvalues -- whether or not the value is "by ref" or "by value". + +The datum API is designed to try and help you avoid memory errors like +forgetting to arrange cleanup or duplicating a value. The type of the +datum incorporates the kind, and thus reflects whether it has cleanup +scheduled: + +- `Datum` -- by ref, cleanup scheduled +- `Datum` -- by value or by ref, no cleanup scheduled +- `Datum` -- either `Datum` or `Datum` + +Rvalue and expr datums are noncopyable, and most of the methods on +datums consume the datum itself (with some notable exceptions). This +reflects the fact that datums may represent affine values which ought +to be consumed exactly once, and if you were to try to (for example) +store an affine value multiple times, you would be duplicating it, +which would certainly be a bug. + +Some of the datum methods, however, are designed to work only on +copyable values such as ints or pointers. Those methods may borrow the +datum (`&self`) rather than consume it, but they always include +assertions on the type of the value represented to check that this +makes sense. An example is `shallow_copy_and_take()`, which duplicates +a datum value. + +Translating an expression always yields a `Datum` result, but +the methods `to_[lr]value_datum()` can be used to coerce a +`Datum` into a `Datum` or `Datum` as +needed. Coercing to an lvalue is fairly common, and generally occurs +whenever it is necessary to inspect a value and pull out its +subcomponents (for example, a match, or indexing expression). Coercing +to an rvalue is more unusual; it occurs when moving values from place +to place, such as in an assignment expression or parameter passing. + +### Lvalues in detail + +An lvalue datum is one for which cleanup has been scheduled. Lvalue +datums are always located in memory, and thus the `ValueRef` for an +LLVM value is always a pointer to the actual Rust value. This means +that if the Datum has a Rust type of `int`, then the LLVM type of the +`ValueRef` will be `int*` (pointer to int). + +Because lvalues already have cleanups scheduled, the memory must be +zeroed to prevent the cleanup from taking place (presuming that the +Rust type needs drop in the first place, otherwise it doesn't +matter). The Datum code automatically performs this zeroing when the +value is stored to a new location, for example. + +Lvalues usually result from evaluating lvalue expressions. For +example, evaluating a local variable `x` yields an lvalue, as does a +reference to a field like `x.f` or an index `x[i]`. + +Lvalue datums can also arise by *converting* an rvalue into an lvalue. +This is done with the `to_lvalue_datum` method defined on +`Datum`. Basically this method just schedules cleanup if the +datum is an rvalue, possibly storing the value into a stack slot first +if needed. Converting rvalues into lvalues occurs in constructs like +`&foo()` or `match foo() { ref x => ... }`, where the user is +implicitly requesting a temporary. + +Somewhat surprisingly, not all lvalue expressions yield lvalue datums +when trans'd. Ultimately the reason for this is to micro-optimize +the resulting LLVM. For example, consider the following code: + + fn foo() -> ~int { ... } + let x = *foo(); + +The expression `*foo()` is an lvalue, but if you invoke `expr::trans`, +it will return an rvalue datum. See `deref_once` in expr.rs for +more details. + +### Rvalues in detail + +Rvalues datums are values with no cleanup scheduled. One must be +careful with rvalue datums to ensure that cleanup is properly +arranged, usually by converting to an lvalue datum or by invoking the +`add_clean` method. + +### Scratch datums + +Sometimes you need some temporary scratch space. The functions +`[lr]value_scratch_datum()` can be used to get temporary stack +space. As their name suggests, they yield lvalues and rvalues +respectively. That is, the slot from `lvalue_scratch_datum` will have +cleanup arranged, and the slot from `rvalue_scratch_datum` does not. + +## The Cleanup module + +The cleanup module tracks what values need to be cleaned up as scopes +are exited, either via failure or just normal control flow. The basic +idea is that the function context maintains a stack of cleanup scopes +that are pushed/popped as we traverse the AST tree. There is typically +at least one cleanup scope per AST node; some AST nodes may introduce +additional temporary scopes. + +Cleanup items can be scheduled into any of the scopes on the stack. +Typically, when a scope is popped, we will also generate the code for +each of its cleanups at that time. This corresponds to a normal exit +from a block (for example, an expression completing evaluation +successfully without failure). However, it is also possible to pop a +block *without* executing its cleanups; this is typically used to +guard intermediate values that must be cleaned up on failure, but not +if everything goes right. See the section on custom scopes below for +more details. + +Cleanup scopes come in three kinds: +- **AST scopes:** each AST node in a function body has a corresponding + AST scope. We push the AST scope when we start generate code for an AST + node and pop it once the AST node has been fully generated. +- **Loop scopes:** loops have an additional cleanup scope. Cleanups are + never scheduled into loop scopes; instead, they are used to record the + basic blocks that we should branch to when a `continue` or `break` statement + is encountered. +- **Custom scopes:** custom scopes are typically used to ensure cleanup + of intermediate values. + +### When to schedule cleanup + +Although the cleanup system is intended to *feel* fairly declarative, +it's still important to time calls to `schedule_clean()` correctly. +Basically, you should not schedule cleanup for memory until it has +been initialized, because if an unwind should occur before the memory +is fully initialized, then the cleanup will run and try to free or +drop uninitialized memory. If the initialization itself produces +byproducts that need to be freed, then you should use temporary custom +scopes to ensure that those byproducts will get freed on unwind. For +example, an expression like `~foo()` will first allocate a box in the +heap and then call `foo()` -- if `foo()` should fail, this box needs +to be *shallowly* freed. + +### Long-distance jumps + +In addition to popping a scope, which corresponds to normal control +flow exiting the scope, we may also *jump out* of a scope into some +earlier scope on the stack. This can occur in response to a `return`, +`break`, or `continue` statement, but also in response to failure. In +any of these cases, we will generate a series of cleanup blocks for +each of the scopes that is exited. So, if the stack contains scopes A +... Z, and we break out of a loop whose corresponding cleanup scope is +X, we would generate cleanup blocks for the cleanups in X, Y, and Z. +After cleanup is done we would branch to the exit point for scope X. +But if failure should occur, we would generate cleanups for all the +scopes from A to Z and then resume the unwind process afterwards. + +To avoid generating tons of code, we cache the cleanup blocks that we +create for breaks, returns, unwinds, and other jumps. Whenever a new +cleanup is scheduled, though, we must clear these cached blocks. A +possible improvement would be to keep the cached blocks but simply +generate a new block which performs the additional cleanup and then +branches to the existing cached blocks. + +### AST and loop cleanup scopes + +AST cleanup scopes are pushed when we begin and end processing an AST +node. They are used to house cleanups related to rvalue temporary that +get referenced (e.g., due to an expression like `&Foo()`). Whenever an +AST scope is popped, we always trans all the cleanups, adding the cleanup +code after the postdominator of the AST node. + +AST nodes that represent breakable loops also push a loop scope; the +loop scope never has any actual cleanups, it's just used to point to +the basic blocks where control should flow after a "continue" or +"break" statement. Popping a loop scope never generates code. + +### Custom cleanup scopes + +Custom cleanup scopes are used for a variety of purposes. The most +common though is to handle temporary byproducts, where cleanup only +needs to occur on failure. The general strategy is to push a custom +cleanup scope, schedule *shallow* cleanups into the custom scope, and +then pop the custom scope (without transing the cleanups) when +execution succeeds normally. This way the cleanups are only trans'd on +unwind, and only up until the point where execution succeeded, at +which time the complete value should be stored in an lvalue or some +other place where normal cleanup applies. + +To spell it out, here is an example. Imagine an expression `~expr`. +We would basically: + +1. Push a custom cleanup scope C. +2. Allocate the `~` box. +3. Schedule a shallow free in the scope C. +4. Trans `expr` into the box. +5. Pop the scope C. +6. Return the box as an rvalue. + +This way, if a failure occurs while transing `expr`, the custom +cleanup scope C is pushed and hence the box will be freed. The trans +code for `expr` itself is responsible for freeing any other byproducts +that may be in play. + +*/ diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 72fc3e8022820..385c0c86ae58f 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -9,110 +9,27 @@ // except according to those terms. /*! - -# Translation of expressions. - -## Recommended entry point - -If you wish to translate an expression, the preferred way to do -so is to use: - - expr::trans_into(block, expr, Dest) -> block - -This will generate code that evaluates `expr`, storing the result into -`Dest`, which must either be the special flag ignore (throw the result -away) or be a pointer to memory of the same type/size as the -expression. It returns the resulting basic block. This form will -handle all automatic adjustments for you. The value will be moved if -its type is linear and copied otherwise. - -## Translation to a datum - -In some cases, `trans_into()` is too narrow of an interface. -Generally this occurs either when you know that the result value is -going to be a scalar, or when you need to evaluate the expression into -some memory location so you can go and inspect it (e.g., assignments, -`match` expressions, the `&` operator). - -In such cases, you want the following function: - - trans_to_datum(block, expr) -> DatumBlock - -This function generates code to evaluate the expression and return a -`Datum` describing where the result is to be found. This function -tries to return its result in the most efficient way possible, without -introducing extra copies or sacrificing information. Therefore, for -lvalue expressions, you always get a by-ref `Datum` in return that -points at the memory for this lvalue. For rvalue expressions, we will -return a by-value `Datum` whenever possible, but it is often necessary -to allocate a stack slot, store the result of the rvalue in there, and -then return a pointer to the slot (see the discussion later on about -the different kinds of rvalues). - -NB: The `trans_to_datum()` function does perform adjustments, but -since it returns a pointer to the value "in place" it does not handle -moves. If you wish to copy/move the value returned into a new -location, you should use the Datum method `store_to()` (move or copy -depending on type). You can also use `move_to()` (force move) or -`copy_to()` (force copy) for special situations. - -## Translating local variables - -`trans_local_var()` can be used to trans a ref to a local variable -that is not an expression. This is needed for captures. - -## Ownership and cleanups - -The current system for cleanups associates required cleanups with -block contexts. Block contexts are structured into a tree that -resembles the code itself. Not every block context has cleanups -associated with it, only those blocks that have a kind of -`block_scope`. See `common::block_kind` for more details. - -If you invoke `trans_into()`, no cleanup is scheduled for you. The -value is written into the given destination and is assumed to be owned -by that destination. - -When you invoke `trans_to_datum()` on an rvalue, the resulting -datum/value will have an appropriate cleanup scheduled for the -innermost cleanup scope. If you later use `move_to()` or -`drop_val()`, this cleanup will be canceled. - -During the evaluation of an expression, temporary cleanups are created -and later canceled. These represent intermediate or partial results -which must be cleaned up in the event of task failure. - -## Implementation details - -We divide expressions into three categories, based on how they are most -naturally implemented: - -1. Lvalues -2. Datum rvalues -3. DPS rvalues -4. Statement rvalues - -Lvalues always refer to user-assignable memory locations. -Translating those always results in a by-ref datum; this introduces -no inefficiencies into the generated code, because all lvalues are -naturally addressable. - -Datum rvalues are rvalues that always generate datums as a result. -These are generally scalar results, such as `a+b` where `a` and `b` -are integers. - -DPS rvalues are rvalues that, when translated, must be given a -memory location to write into (or the Ignore flag). These are -generally expressions that produce structural results that are -larger than one word (e.g., a struct literal), but also expressions -(like `if`) that involve control flow (otherwise we'd have to -generate phi nodes). - -Finally, statement rvalues are rvalues that always produce a nil -return type, such as `while` loops or assignments (`a = b`). - -*/ - + * # Translation of Expressions + * + * Public entry points: + * + * - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression, + * storing the result into `dest`. This is the preferred form, if you + * can manage it. + * + * - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding + * `Datum` with the result. You can then store the datum, inspect + * the value, etc. This may introduce temporaries if the datum is a + * structural type. + * + * - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an + * expression and ensures that the result has a cleanup associated with it, + * creating a temporary stack slot if necessary. + * + * - `trans_local_var -> Datum`: looks up a local variable or upvar. + * + * See doc.rs for more comments. + */ use back::abi; use back::link; @@ -127,17 +44,21 @@ use middle::trans::base; use middle::trans::build::*; use middle::trans::callee::DoAutorefArg; use middle::trans::callee; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::closure; use middle::trans::common::*; use middle::trans::consts; use middle::trans::controlflow; use middle::trans::datum::*; use middle::trans::debuginfo; +use middle::trans::glue; use middle::trans::machine; use middle::trans::meth; use middle::trans::inline; use middle::trans::tvec; use middle::trans::type_of; +use middle::trans::write_guard; use middle::ty::struct_fields; use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe}; use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn}; @@ -175,31 +96,102 @@ impl Dest { } } -pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) - -> DatumBlock<'a> { - debug!("trans_to_datum(expr={})", bcx.expr_to_str(expr)); +pub fn trans_into<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + dest: Dest) + -> &'a Block<'a> { + /*! + * This function is equivalent to `trans(bcx, expr).store_to_dest(dest)` + * but it may generate better optimized LLVM code. + */ + + let mut bcx = bcx; + + let is_adjusted = { + let adjustments = bcx.tcx().adjustments.borrow(); + adjustments.get().contains_key(&expr.id) + }; + + if is_adjusted { + // use trans, which may be less efficient but + // which will perform the adjustments: + let datum = unpack_datum!(bcx, trans(bcx, expr)); + return datum.store_to_dest(bcx, dest, expr.id) + } + + debug!("trans_into() expr={}", expr.repr(bcx.tcx())); + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + + bcx.fcx.push_ast_cleanup_scope(expr.id); + + let kind = ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr); + bcx = match kind { + ty::LvalueExpr | ty::RvalueDatumExpr => { + trans_unadjusted(bcx, expr).store_to_dest(dest, expr.id) + } + ty::RvalueDpsExpr => { + trans_rvalue_dps_unadjusted(bcx, expr, dest) + } + ty::RvalueStmtExpr => { + trans_rvalue_stmt_unadjusted(bcx, expr) + } + }; + + bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id) +} + +pub fn trans<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr) + -> DatumBlock<'a, Expr> { + /*! + * Translates an expression, returning a datum (and new block) + * encapsulating the result. When possible, it is preferred to + * use `trans_into`, as that may avoid creating a temporary on + * the stack. + */ + + debug!("trans(expr={})", bcx.expr_to_str(expr)); let mut bcx = bcx; - let mut datum = unpack_datum!(bcx, trans_to_datum_unadjusted(bcx, expr)); + let fcx = bcx.fcx; + + fcx.push_ast_cleanup_scope(expr.id); + let datum = unpack_datum!(bcx, trans_unadjusted(bcx, expr)); + let datum = unpack_datum!(bcx, apply_adjustments(bcx, expr, datum)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id); + return DatumBlock(bcx, datum); +} + +fn apply_adjustments<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'a, Expr> { + /*! + * Helper for trans that apply adjustments from `expr` to `datum`, + * which should be the unadjusted translation of `expr`. + */ + + let mut bcx = bcx; + let mut datum = datum; let adjustment = { let adjustments = bcx.tcx().adjustments.borrow(); match adjustments.get().find_copy(&expr.id) { - None => { return DatumBlock {bcx: bcx, datum: datum}; } + None => { + return DatumBlock(bcx, datum); + } Some(adj) => { adj } } }; - debug!("unadjusted datum: {}", datum.to_str(bcx.ccx())); + debug!("unadjusted datum for expr {}: {}", + expr.id, datum.to_str(bcx.ccx())); match *adjustment { AutoAddEnv(..) => { datum = unpack_datum!(bcx, add_env(bcx, expr, datum)); } AutoDerefRef(ref adj) => { if adj.autoderefs > 0 { - datum = - unpack_datum!( - bcx, - datum.autoderef(bcx, expr.span, - expr.id, adj.autoderefs)); + datum = unpack_datum!( + bcx, deref_multiple(bcx, expr, datum, adj.autoderefs)); } datum = match adj.autoref { @@ -208,7 +200,7 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) } Some(AutoUnsafe(..)) | // region + unsafe ptrs have same repr Some(AutoPtr(..)) => { - unpack_datum!(bcx, auto_ref(bcx, datum)) + unpack_datum!(bcx, auto_ref(bcx, datum, expr)) } Some(AutoBorrowVec(..)) => { unpack_datum!(bcx, auto_slice(bcx, adj.autoderefs, @@ -230,51 +222,75 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) }; } AutoObject(..) => { - let adjusted_ty = ty::expr_ty_adjusted(bcx.tcx(), expr); - let scratch = scratch_datum(bcx, adjusted_ty, "__adjust", false); - - bcx = meth::trans_trait_cast(bcx, expr, expr.id, SaveIn(scratch.val), Some(datum)); - - datum = scratch.to_appropriate_datum(bcx); - datum.add_clean(bcx); + let scratch = rvalue_scratch_datum(bcx, adjusted_ty, "__adjust"); + bcx = meth::trans_trait_cast( + bcx, datum, expr.id, SaveIn(scratch.val)); + datum = scratch.to_expr_datum(); } } debug!("after adjustments, datum={}", datum.to_str(bcx.ccx())); return DatumBlock {bcx: bcx, datum: datum}; - fn auto_ref<'a>(bcx: &'a Block<'a>, datum: Datum) -> DatumBlock<'a> { - DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)} + fn auto_ref<'a>(bcx: &'a Block<'a>, + datum: Datum, + expr: &ast::Expr) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + + // Ensure cleanup of `datum` if not already scheduled and obtain + // a "by ref" pointer. + let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id)); + + // Compute final type. Note that we are loose with the region and + // mutability, since those things don't matter in trans. + let referent_ty = lv_datum.ty; + let ptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, referent_ty); + + // Get the pointer. + let llref = lv_datum.to_llref(); + + // Construct the resulting datum, using what was the "by ref" + // ValueRef of type `referent_ty` to be the "by value" ValueRef + // of type `&referent_ty`. + DatumBlock(bcx, Datum(llref, ptr_ty, RvalueExpr(Rvalue(ByValue)))) } fn auto_borrow_fn<'a>( bcx: &'a Block<'a>, adjusted_ty: ty::t, - datum: Datum) - -> DatumBlock<'a> { + datum: Datum) + -> DatumBlock<'a, Expr> { // Currently, all closure types are represented precisely the // same, so no runtime adjustment is required, but we still // must patchup the type. DatumBlock {bcx: bcx, - datum: Datum {val: datum.val, ty: adjusted_ty, - mode: datum.mode}} + datum: Datum {val: datum.val, + ty: adjusted_ty, + kind: datum.kind}} } fn auto_slice<'a>( bcx: &'a Block<'a>, autoderefs: uint, expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'a> { + datum: Datum) + -> DatumBlock<'a, Expr> { // This is not the most efficient thing possible; since slices // are two words it'd be better if this were compiled in // 'dest' mode, but I can't find a nice way to structure the // code and keep it DRY that accommodates that use case at the // moment. + let mut bcx = bcx; let tcx = bcx.tcx(); let unit_ty = ty::sequence_element_type(tcx, datum.ty); + // Arrange cleanup, if not already done. This is needed in + // case we are auto-slicing an owned vector or some such. + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "auto_slice", expr.id)); + let (bcx, base, len) = datum.get_vec_base_and_len(bcx, expr.span, expr.id, autoderefs+1); @@ -284,15 +300,16 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) ty::mt { ty: unit_ty, mutbl: ast::MutImmutable }, ty::vstore_slice(ty::ReStatic)); - let scratch = scratch_datum(bcx, slice_ty, "__adjust", false); - + let scratch = rvalue_scratch_datum(bcx, slice_ty, "__adjust"); Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])); Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])); - DatumBlock {bcx: bcx, datum: scratch} + DatumBlock(bcx, scratch.to_expr_datum()) } - fn add_env<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, datum: Datum) - -> DatumBlock<'a> { + fn add_env<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'a, Expr> { // This is not the most efficient thing possible; since closures // are two words it'd be better if this were compiled in // 'dest' mode, but I can't find a nice way to structure the @@ -302,31 +319,31 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) let tcx = bcx.tcx(); let closure_ty = expr_ty_adjusted(bcx, expr); debug!("add_env(closure_ty={})", closure_ty.repr(tcx)); - let scratch = scratch_datum(bcx, closure_ty, "__adjust", false); + let scratch = rvalue_scratch_datum(bcx, closure_ty, "__adjust"); let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]); - assert_eq!(datum.appropriate_mode(bcx.ccx()), ByValue); - Store(bcx, datum.to_appropriate_llval(bcx), llfn); + let llval = datum.to_llscalarish(bcx); + Store(bcx, llval, llfn); let llenv = GEPi(bcx, scratch.val, [0u, abi::fn_field_box]); Store(bcx, base::null_env_ptr(bcx.ccx()), llenv); - DatumBlock {bcx: bcx, datum: scratch} + DatumBlock(bcx, scratch.to_expr_datum()) } fn auto_slice_and_ref<'a>( bcx: &'a Block<'a>, autoderefs: uint, expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'a> { + datum: Datum) + -> DatumBlock<'a, Expr> { let DatumBlock { bcx, datum } = auto_slice(bcx, autoderefs, expr, datum); - auto_ref(bcx, datum) + auto_ref(bcx, datum, expr) } fn auto_borrow_obj<'a>( mut bcx: &'a Block<'a>, autoderefs: uint, expr: &ast::Expr, - source_datum: Datum) - -> DatumBlock<'a> { + source_datum: Datum) + -> DatumBlock<'a, Expr> { let tcx = bcx.tcx(); let target_obj_ty = expr_ty_adjusted(bcx, expr); debug!("auto_borrow_obj(target={})", @@ -343,7 +360,8 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) } }; - // check if any borrowing is really needed or we could reuse the source_datum instead + // check if any borrowing is really needed or we could reuse + // the source_datum instead match ty::get(target_obj_ty).sty { ty::ty_trait(_, _, ty::RegionTraitStore(target_scope), target_mutbl, _) => { if target_mutbl == ast::MutImmutable && target_mutbl == source_mutbl { @@ -361,14 +379,16 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) _ => {} } - let scratch = scratch_datum(bcx, target_obj_ty, - "__auto_borrow_obj", false); + let scratch = rvalue_scratch_datum(bcx, target_obj_ty, + "__auto_borrow_obj"); // Convert a @Object, ~Object, or &Object pair into an &Object pair. // Get a pointer to the source object, which is represented as // a (vtable, data) pair. - let source_llval = source_datum.to_ref_llval(bcx); + let source_datum = unpack_datum!( + bcx, source_datum.to_lvalue_datum(bcx, "auto_borrow_obj", expr.id)); + let source_llval = source_datum.to_llref(); // Set the vtable field of the new pair let vtable_ptr = GEPi(bcx, source_llval, [0u, abi::trt_field_vtable]); @@ -378,172 +398,87 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) // Load the data for the source, which is either an @T, // ~T, or &T, depending on source_obj_ty. let source_data_ptr = GEPi(bcx, source_llval, [0u, abi::trt_field_box]); - let source_data = Load(bcx, source_data_ptr); // always a ptr let target_data = match source_store { ty::BoxTraitStore(..) => { - // For deref of @T, create a dummy datum and use the datum's - // deref method. This is more work than just calling GEPi - // ourselves. Note that we don't know the type T, so - // just substitute `i8`-- it doesn't really matter for - // our purposes right now. + // For deref of @T, create a dummy datum and use the + // datum's deref method. This is more work than just + // calling GEPi ourselves, but it ensures that any + // necessary rooting is performed. Note that we don't + // know the type T, so just substitute `i8`-- it + // doesn't really matter for our purposes right now. let source_ty = ty::mk_box(tcx, ty::mk_i8()); - let source_datum = - Datum {val: source_data, - ty: source_ty, - mode: ByValue}; - let derefd_datum = - unpack_datum!(bcx, - source_datum.deref(bcx, - expr, - autoderefs)); - derefd_datum.to_rptr(bcx).to_value_llval(bcx) + let source_datum = Datum(source_data_ptr, source_ty, LvalueExpr); + let derefd_datum = unpack_datum!( + bcx, deref_once(bcx, expr, source_datum, autoderefs)); + derefd_datum.assert_lvalue(bcx).to_llref() } ty::UniqTraitStore(..) | ty::RegionTraitStore(..) => { - source_data + Load(bcx, source_data_ptr) } }; Store(bcx, target_data, GEPi(bcx, scratch.val, [0u, abi::trt_field_box])); - DatumBlock { bcx: bcx, datum: scratch } + DatumBlock(bcx, scratch.to_expr_datum()) } } -pub fn trans_into<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, dest: Dest) - -> &'a Block<'a> { - let adjustment_found = { - let adjustments = bcx.tcx().adjustments.borrow(); - adjustments.get().contains_key(&expr.id) - }; - if adjustment_found { - // use trans_to_datum, which is mildly less efficient but - // which will perform the adjustments: - let datumblock = trans_to_datum(bcx, expr); - return match dest { - Ignore => datumblock.bcx, - SaveIn(lldest) => datumblock.store_to(INIT, lldest) - }; - } - - trans_into_unadjusted(bcx, expr, dest) -} - -pub fn trans_into_unadjusted<'a>( - bcx: &'a Block<'a>, - expr: &ast::Expr, - dest: Dest) - -> &'a Block<'a> { - let ty = expr_ty(bcx, expr); - - debug!("trans_into_unadjusted(expr={}, dest={})", - bcx.expr_to_str(expr), - dest.to_str(bcx.ccx())); - let _indenter = indenter(); - - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - - let dest = { - if ty::type_is_voidish(bcx.tcx(), ty) { - Ignore - } else { - dest - } - }; - - let kind = bcx.expr_kind(expr); - debug!("expr kind = {:?}", kind); - return match kind { - ty::LvalueExpr => { - let datumblock = trans_lvalue_unadjusted(bcx, expr); - match dest { - Ignore => datumblock.bcx, - SaveIn(lldest) => datumblock.store_to(INIT, lldest) - } - } - ty::RvalueDatumExpr => { - let datumblock = trans_rvalue_datum_unadjusted(bcx, expr); - match dest { - Ignore => datumblock.drop_val(), - - // When processing an rvalue, the value will be newly - // allocated, so we always `move_to` so as not to - // unnecessarily inc ref counts and so forth: - SaveIn(lldest) => datumblock.move_to(INIT, lldest) - } - } - ty::RvalueDpsExpr => { - trans_rvalue_dps_unadjusted(bcx, expr, dest) - } - ty::RvalueStmtExpr => { - trans_rvalue_stmt_unadjusted(bcx, expr) - } - }; -} - -fn trans_lvalue<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> DatumBlock<'a> { +pub fn trans_to_lvalue<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + name: &str) + -> DatumBlock<'a, Lvalue> { /*! + * Translates an expression in "lvalue" mode -- meaning that it + * returns a reference to the memory that the expr represents. + * + * If this expression is an rvalue, this implies introducing a + * temporary. In other words, something like `x().f` is + * translated into roughly the equivalent of * - * Translates an lvalue expression, always yielding a by-ref - * datum. Generally speaking you should call trans_to_datum() - * instead, but sometimes we call trans_lvalue() directly as a - * means of asserting that a particular expression is an lvalue. */ + * { tmp = x(); tmp.f } + */ - let adjustment_opt = { - let adjustments = bcx.tcx().adjustments.borrow(); - adjustments.get().find_copy(&expr.id) - }; - match adjustment_opt { - None => trans_lvalue_unadjusted(bcx, expr), - Some(_) => { - bcx.sess().span_bug( - expr.span, - format!("trans_lvalue() called on an expression \ - with adjustments")); - } - } + let mut bcx = bcx; + let datum = unpack_datum!(bcx, trans(bcx, expr)); + return datum.to_lvalue_datum(bcx, name, expr.id); } -fn trans_to_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) - -> DatumBlock<'a> { +fn trans_unadjusted<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr) + -> DatumBlock<'a, Expr> { /*! - * Translates an expression into a datum. If this expression - * is an rvalue, this will result in a temporary value being - * created. If you plan to store the value somewhere else, - * you should prefer `trans_into()` instead. + * A version of `trans` that ignores adjustments. You almost + * certainly do not want to call this directly. */ let mut bcx = bcx; - debug!("trans_to_datum_unadjusted(expr={})", bcx.expr_to_str(expr)); + debug!("trans_unadjusted(expr={})", bcx.expr_to_str(expr)); let _indenter = indenter(); debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) { - ty::LvalueExpr => { - return trans_lvalue_unadjusted(bcx, expr); - } - - ty::RvalueDatumExpr => { + return match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) { + ty::LvalueExpr | ty::RvalueDatumExpr => { let datum = unpack_datum!(bcx, { - trans_rvalue_datum_unadjusted(bcx, expr) + trans_datum_unadjusted(bcx, expr) }); - datum.add_clean(bcx); - return DatumBlock {bcx: bcx, datum: datum}; + + DatumBlock {bcx: bcx, datum: datum} } ty::RvalueStmtExpr => { bcx = trans_rvalue_stmt_unadjusted(bcx, expr); - return nil(bcx, expr_ty(bcx, expr)); + nil(bcx, expr_ty(bcx, expr)) } ty::RvalueDpsExpr => { let ty = expr_ty(bcx, expr); if ty::type_is_voidish(bcx.tcx(), ty) { bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore); - return nil(bcx, ty); + nil(bcx, ty) } else { - let scratch = scratch_datum(bcx, ty, "", false); + let scratch = rvalue_scratch_datum(bcx, ty, ""); bcx = trans_rvalue_dps_unadjusted( bcx, expr, SaveIn(scratch.val)); @@ -556,38 +491,55 @@ fn trans_to_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) // Still, in practice it seems to increase // performance, since we have fewer problems with // morestack churn. - let scratch = scratch.to_appropriate_datum(bcx); + let scratch = unpack_datum!( + bcx, scratch.to_appropriate_datum(bcx)); - scratch.add_clean(bcx); - return DatumBlock {bcx: bcx, datum: scratch}; + DatumBlock(bcx, scratch.to_expr_datum()) } } - } + }; - fn nil<'a>(bcx: &'a Block<'a>, ty: ty::t) -> DatumBlock<'a> { + fn nil<'a>(bcx: &'a Block<'a>, ty: ty::t) -> DatumBlock<'a, Expr> { let datum = immediate_rvalue(C_nil(), ty); - DatumBlock { - bcx: bcx, - datum: datum, - } + DatumBlock(bcx, datum.to_expr_datum()) } } -fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) - -> DatumBlock<'a> { - let _icx = push_ctxt("trans_rvalue_datum_unadjusted"); +fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + let fcx = bcx.fcx; + let _icx = push_ctxt("trans_datum_unadjusted"); match expr.node { + ast::ExprParen(e) => { + trans(bcx, e) + } ast::ExprPath(_) | ast::ExprSelf => { - return trans_def_datum_unadjusted(bcx, expr, bcx.def(expr.id)); + trans_def(bcx, expr, bcx.def(expr.id)) + } + ast::ExprField(base, ident, _) => { + trans_rec_field(bcx, base, ident) + } + ast::ExprIndex(_, base, idx) => { + trans_index(bcx, expr, base, idx) } ast::ExprVstore(contents, ast::ExprVstoreBox) => { - return tvec::trans_uniq_or_managed_vstore(bcx, heap_managed, - expr, contents); + fcx.push_ast_cleanup_scope(contents.id); + let datum = unpack_datum!( + bcx, tvec::trans_uniq_or_managed_vstore(bcx, heap_managed, + expr, contents)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id); + DatumBlock(bcx, datum) } ast::ExprVstore(contents, ast::ExprVstoreUniq) => { - return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange, - expr, contents); + fcx.push_ast_cleanup_scope(contents.id); + let datum = unpack_datum!( + bcx, tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange, + expr, contents)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id); + DatumBlock(bcx, datum) } ast::ExprBox(_, contents) => { // Special case for `~T`. (The other case, for GC, is handled in @@ -598,7 +550,7 @@ fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) return trans_boxed_expr(bcx, box_ty, contents, contents_ty, heap) } ast::ExprLit(lit) => { - return trans_immediate_lit(bcx, expr, *lit); + trans_immediate_lit(bcx, expr, *lit) } ast::ExprBinary(_, op, lhs, rhs) => { // if overloaded, would be RvalueDpsExpr @@ -607,22 +559,24 @@ fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) assert!(!method_map.get().contains_key(&expr.id)); } - return trans_binary(bcx, expr, op, lhs, rhs); + trans_binary(bcx, expr, op, lhs, rhs) + } + ast::ExprUnary(_, ast::UnDeref, base) => { + let basedatum = unpack_datum!(bcx, trans(bcx, base)); + deref_once(bcx, expr, basedatum, 0) } ast::ExprUnary(_, op, x) => { - return trans_unary_datum(bcx, expr, op, x); + trans_unary_datum(bcx, expr, op, x) } ast::ExprAddrOf(_, x) => { - return trans_addr_of(bcx, expr, x); + trans_addr_of(bcx, expr, x) } ast::ExprCast(val, _) => { - return trans_imm_cast(bcx, val, expr.id); - } - ast::ExprParen(e) => { - return trans_rvalue_datum_unadjusted(bcx, e); + // Datum output mode means this is a scalar cast: + trans_imm_cast(bcx, val, expr.id) } ast::ExprLogLevel => { - return trans_log_level(bcx); + trans_log_level(bcx) } _ => { bcx.tcx().sess.span_bug( @@ -634,8 +588,152 @@ fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) } } -fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) - -> &'a Block<'a> { +fn trans_rec_field<'a>(bcx: &'a Block<'a>, + base: &ast::Expr, + field: ast::Ident) + -> DatumBlock<'a, Expr> { + //! Translates `base.field`. + + let mut bcx = bcx; + let _icx = push_ctxt("trans_rec_field"); + + let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field")); + let repr = adt::represent_type(bcx.ccx(), base_datum.ty); + with_field_tys(bcx.tcx(), base_datum.ty, None, |discr, field_tys| { + let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys); + let d = base_datum.get_element( + field_tys[ix].mt.ty, + |srcval| adt::trans_field_ptr(bcx, repr, srcval, discr, ix)); + DatumBlock { datum: d.to_expr_datum(), bcx: bcx } + }) +} + +fn trans_index<'a>(bcx: &'a Block<'a>, + index_expr: &ast::Expr, + base: &ast::Expr, + idx: &ast::Expr) + -> DatumBlock<'a, Expr> { + //! Translates `base[idx]`. + + let _icx = push_ctxt("trans_index"); + let ccx = bcx.ccx(); + let mut bcx = bcx; + + let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "index")); + + // Translate index expression and cast to a suitable LLVM integer. + // Rust is less strict than LLVM in this regard. + let ix_datum = unpack_datum!(bcx, trans(bcx, idx)); + let ix_val = ix_datum.to_llscalarish(bcx); + let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val)); + let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type); + let ix_val = { + if ix_size < int_size { + if ty::type_is_signed(expr_ty(bcx, idx)) { + SExt(bcx, ix_val, ccx.int_type) + } else { ZExt(bcx, ix_val, ccx.int_type) } + } else if ix_size > int_size { + Trunc(bcx, ix_val, ccx.int_type) + } else { + ix_val + } + }; + + let vt = tvec::vec_types(bcx, base_datum.ty); + base::maybe_name_value(bcx.ccx(), vt.llunit_size, "unit_sz"); + + let (bcx, base, len) = + base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id, 0); + + debug!("trans_index: base {}", bcx.val_to_str(base)); + debug!("trans_index: len {}", bcx.val_to_str(len)); + + let bounds_check = ICmp(bcx, lib::llvm::IntUGE, ix_val, len); + let expect = ccx.intrinsics.get_copy(&("llvm.expect.i1")); + let expected = Call(bcx, expect, [bounds_check, C_i1(false)], []); + let bcx = with_cond(bcx, expected, |bcx| { + controlflow::trans_fail_bounds_check(bcx, index_expr.span, ix_val, len) + }); + let elt = InBoundsGEP(bcx, base, [ix_val]); + let elt = PointerCast(bcx, elt, vt.llunit_ty.ptr_to()); + DatumBlock(bcx, Datum(elt, vt.unit_ty, LvalueExpr)) +} + +fn trans_def<'a>(bcx: &'a Block<'a>, + ref_expr: &ast::Expr, + def: ast::Def) + -> DatumBlock<'a, Expr> +{ + //! Translates a reference to a path. + + let _icx = push_ctxt("trans_def_lvalue"); + match def { + ast::DefFn(..) | ast::DefStaticMethod(..) => { + trans_def_fn_unadjusted(bcx, ref_expr, def) + } + ast::DefStatic(did, _) => { + let const_ty = expr_ty(bcx, ref_expr); + + fn get_did(ccx: @CrateContext, did: ast::DefId) + -> ast::DefId { + if did.crate != ast::LOCAL_CRATE { + inline::maybe_instantiate_inline(ccx, did) + } else { + did + } + } + + fn get_val<'a>(bcx: &'a Block<'a>, did: ast::DefId, const_ty: ty::t) + -> ValueRef { + // For external constants, we don't inline. + if did.crate == ast::LOCAL_CRATE { + // The LLVM global has the type of its initializer, + // which may not be equal to the enum's type for + // non-C-like enums. + let val = base::get_item_val(bcx.ccx(), did.node); + let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); + PointerCast(bcx, val, pty) + } else { + { + let extern_const_values = bcx.ccx().extern_const_values.borrow(); + match extern_const_values.get().find(&did) { + None => {} // Continue. + Some(llval) => { + return *llval; + } + } + } + + unsafe { + let llty = type_of::type_of(bcx.ccx(), const_ty); + let symbol = csearch::get_symbol( + bcx.ccx().sess.cstore, + did); + let llval = symbol.with_c_str(|buf| { + llvm::LLVMAddGlobal(bcx.ccx().llmod, + llty.to_ref(), + buf) + }); + let mut extern_const_values = bcx.ccx().extern_const_values.borrow_mut(); + extern_const_values.get().insert(did, llval); + llval + } + } + } + + let did = get_did(bcx.ccx(), did); + let val = get_val(bcx, did, const_ty); + DatumBlock(bcx, Datum(val, const_ty, LvalueExpr)) + } + _ => { + DatumBlock(bcx, trans_local_var(bcx, def).to_expr_datum()) + } + } +} + +fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr) + -> &'a Block<'a> { let mut bcx = bcx; let _icx = push_ctxt("trans_rvalue_stmt"); @@ -644,38 +742,58 @@ fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) } match expr.node { + ast::ExprParen(e) => { + trans_into(bcx, e, Ignore) + } ast::ExprBreak(label_opt) => { - return controlflow::trans_break(bcx, label_opt); + controlflow::trans_break(bcx, expr.id, label_opt) } ast::ExprAgain(label_opt) => { - return controlflow::trans_cont(bcx, label_opt); + controlflow::trans_cont(bcx, expr.id, label_opt) } ast::ExprRet(ex) => { - return controlflow::trans_ret(bcx, ex); + controlflow::trans_ret(bcx, ex) } ast::ExprWhile(cond, body) => { - return controlflow::trans_while(bcx, cond, body); + controlflow::trans_while(bcx, expr.id, cond, body) } - ast::ExprLoop(body, opt_label) => { - // FIXME #6993: map can go away when ast.rs is changed - return controlflow::trans_loop(bcx, body, opt_label.map(|x| x.name)); + ast::ExprLoop(body, _) => { + controlflow::trans_loop(bcx, expr.id, body) } ast::ExprAssign(dst, src) => { - let src_datum = unpack_datum!( - bcx, trans_to_datum(bcx, src)); - let dst_datum = unpack_datum!( - bcx, trans_lvalue(bcx, dst)); - return src_datum.store_to_datum( - bcx, DROP_EXISTING, dst_datum); + let src_datum = unpack_datum!(bcx, trans(bcx, src)); + let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign")); + + if ty::type_needs_drop(bcx.tcx(), dst_datum.ty) { + // If there are destructors involved, make sure we + // are copying from an rvalue, since that cannot possible + // alias an lvalue. We are concerned about code like: + // + // a = a + // + // but also + // + // a = a.b + // + // where e.g. a : Option and a.b : + // Option. In that case, freeing `a` before the + // assignment may also free `a.b`! + // + // We could avoid this intermediary with some analysis + // to determine whether `dst` may possibly own `src`. + let src_datum = unpack_datum!( + bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign")); + bcx = glue::drop_ty(bcx, dst_datum.val, dst_datum.ty); + src_datum.store_to(bcx, dst_datum.val) + } else { + src_datum.store_to(bcx, dst_datum.val) + } } ast::ExprAssignOp(callee_id, op, dst, src) => { - return trans_assign_op(bcx, expr, callee_id, op, dst, src); - } - ast::ExprParen(a) => { - return trans_rvalue_stmt_unadjusted(bcx, a); + trans_assign_op(bcx, expr, callee_id, op, dst, src) } ast::ExprInlineAsm(ref a) => { - return asm::trans_inline_asm(bcx, a); + asm::trans_inline_asm(bcx, a) } _ => { bcx.tcx().sess.span_bug( @@ -684,38 +802,34 @@ fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) fall-through case: {:?}", expr.node)); } - }; + } } -fn trans_rvalue_dps_unadjusted<'a>( - bcx: &'a Block<'a>, - expr: &ast::Expr, - dest: Dest) - -> &'a Block<'a> { +fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + dest: Dest) + -> &'a Block<'a> { let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); + let mut bcx = bcx; let tcx = bcx.tcx(); + let fcx = bcx.fcx; match expr.node { ast::ExprParen(e) => { - return trans_rvalue_dps_unadjusted(bcx, e, dest); + return trans_into(bcx, e, dest); } ast::ExprPath(_) | ast::ExprSelf => { return trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest); } ast::ExprIf(cond, thn, els) => { - return controlflow::trans_if(bcx, cond, thn, els, dest); + return controlflow::trans_if(bcx, expr.id, cond, thn, els, dest); } ast::ExprMatch(discr, ref arms) => { return _match::trans_match(bcx, expr, discr, *arms, dest); } ast::ExprBlock(blk) => { - return base::with_scope(bcx, - blk.info(), - "block-expr body", - |bcx| { - controlflow::trans_block(bcx, blk, dest) - }); + controlflow::trans_block(bcx, blk, dest) } ast::ExprStruct(_, ref fields, base) => { return trans_rec_or_struct(bcx, (*fields), base, expr.span, expr.id, dest); @@ -742,7 +856,9 @@ fn trans_rvalue_dps_unadjusted<'a>( } ast::ExprVstore(contents, ast::ExprVstoreSlice) | ast::ExprVstore(contents, ast::ExprVstoreMutSlice) => { - return tvec::trans_slice_vstore(bcx, expr, contents, dest); + fcx.push_ast_cleanup_scope(contents.id); + bcx = tvec::trans_slice_vstore(bcx, expr, contents, dest); + return fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id); } ast::ExprVec(..) | ast::ExprRepeat(..) => { return tvec::trans_fixed_vstore(bcx, expr, expr, dest); @@ -803,10 +919,11 @@ fn trans_rvalue_dps_unadjusted<'a>( dest); } ast::ExprCast(val, _) => { + // DPS output mode means this is a trait cast: match ty::get(node_id_type(bcx, expr.id)).sty { ty::ty_trait(..) => { - return meth::trans_trait_cast(bcx, val, expr.id, - dest, None); + let datum = unpack_datum!(bcx, trans(bcx, val)); + return meth::trans_trait_cast(bcx, datum, expr.id, dest); } _ => { bcx.tcx().sess.span_bug(expr.span, @@ -885,11 +1002,10 @@ fn trans_def_dps_unadjusted<'a>( } } -fn trans_def_datum_unadjusted<'a>( - bcx: &'a Block<'a>, - ref_expr: &ast::Expr, - def: ast::Def) - -> DatumBlock<'a> { +fn trans_def_fn_unadjusted<'a>(bcx: &'a Block<'a>, + ref_expr: &ast::Expr, + def: ast::Def) -> DatumBlock<'a, Expr> +{ let _icx = push_ctxt("trans_def_datum_unadjusted"); let fn_data = match def { @@ -905,224 +1021,24 @@ fn trans_def_datum_unadjusted<'a>( } _ => { bcx.tcx().sess.span_bug(ref_expr.span, format!( - "Non-DPS def {:?} referened by {}", - def, bcx.node_id_to_str(ref_expr.id))); + "trans_def_fn_unadjusted invoked on: {:?} for {}", + def, + ref_expr.repr(bcx.tcx()))); } }; let fn_ty = expr_ty(bcx, ref_expr); - DatumBlock { - bcx: bcx, - datum: Datum { - val: fn_data.llfn, - ty: fn_ty, - mode: ByValue - } - } + DatumBlock(bcx, Datum(fn_data.llfn, fn_ty, RvalueExpr(Rvalue(ByValue)))) } -fn trans_lvalue_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) - -> DatumBlock<'a> { +pub fn trans_local_var<'a>(bcx: &'a Block<'a>, + def: ast::Def) + -> Datum { /*! - * - * Translates an lvalue expression, always yielding a by-ref - * datum. Does not apply any adjustments. */ - - let _icx = push_ctxt("trans_lval"); - let mut bcx = bcx; - - debug!("trans_lvalue(expr={})", bcx.expr_to_str(expr)); - let _indenter = indenter(); - - return match expr.node { - ast::ExprParen(e) => { - trans_lvalue_unadjusted(bcx, e) - } - ast::ExprPath(_) | ast::ExprSelf => { - trans_def_lvalue(bcx, expr, bcx.def(expr.id)) - } - ast::ExprField(base, ident, _) => { - trans_rec_field(bcx, base, ident) - } - ast::ExprIndex(_, base, idx) => { - trans_index(bcx, expr, base, idx) - } - ast::ExprUnary(_, ast::UnDeref, base) => { - let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base)); - basedatum.deref(bcx, expr, 0) - } - _ => { - bcx.tcx().sess.span_bug( - expr.span, - format!("trans_lvalue reached fall-through case: {:?}", - expr.node)); - } - }; - - fn trans_rec_field<'a>( - bcx: &'a Block<'a>, - base: &ast::Expr, - field: ast::Ident) - -> DatumBlock<'a> { - //! Translates `base.field`. - - let mut bcx = bcx; - let _icx = push_ctxt("trans_rec_field"); - - let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base)); - let repr = adt::represent_type(bcx.ccx(), base_datum.ty); - with_field_tys(bcx.tcx(), base_datum.ty, None, |discr, field_tys| { - let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys); - DatumBlock { - datum: base_datum.get_element(bcx, - field_tys[ix].mt.ty, - ZeroMem, - |srcval| { - adt::trans_field_ptr(bcx, repr, srcval, discr, ix) - }), - bcx: bcx - } - }) - } - - fn trans_index<'a>( - bcx: &'a Block<'a>, - index_expr: &ast::Expr, - base: &ast::Expr, - idx: &ast::Expr) - -> DatumBlock<'a> { - //! Translates `base[idx]`. - - let _icx = push_ctxt("trans_index"); - let ccx = bcx.ccx(); - let mut bcx = bcx; - - let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base)); - - // Translate index expression and cast to a suitable LLVM integer. - // Rust is less strict than LLVM in this regard. - let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result(); - let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val)); - let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type); - let ix_val = { - if ix_size < int_size { - if ty::type_is_signed(expr_ty(bcx, idx)) { - SExt(bcx, ix_val, ccx.int_type) - } else { ZExt(bcx, ix_val, ccx.int_type) } - } else if ix_size > int_size { - Trunc(bcx, ix_val, ccx.int_type) - } else { - ix_val - } - }; - - let vt = tvec::vec_types(bcx, base_datum.ty); - base::maybe_name_value(bcx.ccx(), vt.llunit_size, "unit_sz"); - - let (bcx, base, len) = - base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id, 0); - - debug!("trans_index: base {}", bcx.val_to_str(base)); - debug!("trans_index: len {}", bcx.val_to_str(len)); - - let bounds_check = ICmp(bcx, lib::llvm::IntUGE, ix_val, len); - let expect = ccx.intrinsics.get_copy(&("llvm.expect.i1")); - let expected = Call(bcx, expect, [bounds_check, C_i1(false)], []); - let bcx = with_cond(bcx, expected, |bcx| { - controlflow::trans_fail_bounds_check(bcx, index_expr.span, ix_val, len) - }); - let elt = InBoundsGEP(bcx, base, [ix_val]); - let elt = PointerCast(bcx, elt, vt.llunit_ty.ptr_to()); - return DatumBlock { - bcx: bcx, - datum: Datum {val: elt, - ty: vt.unit_ty, - mode: ByRef(ZeroMem)} - }; - } - - fn trans_def_lvalue<'a>( - bcx: &'a Block<'a>, - ref_expr: &ast::Expr, - def: ast::Def) - -> DatumBlock<'a> { - //! Translates a reference to a path. - - let _icx = push_ctxt("trans_def_lvalue"); - match def { - ast::DefStatic(did, _) => { - let const_ty = expr_ty(bcx, ref_expr); - - fn get_did(ccx: @CrateContext, did: ast::DefId) - -> ast::DefId { - if did.crate != ast::LOCAL_CRATE { - inline::maybe_instantiate_inline(ccx, did) - } else { - did - } - } - - fn get_val(bcx: &Block, did: ast::DefId, const_ty: ty::t) - -> ValueRef { - // For external constants, we don't inline. - if did.crate == ast::LOCAL_CRATE { - // The LLVM global has the type of its initializer, - // which may not be equal to the enum's type for - // non-C-like enums. - let val = base::get_item_val(bcx.ccx(), did.node); - let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); - PointerCast(bcx, val, pty) - } else { - { - let extern_const_values = bcx.ccx() - .extern_const_values - .borrow(); - match extern_const_values.get().find(&did) { - None => {} // Continue. - Some(llval) => { - return *llval; - } - } - } - - unsafe { - let llty = type_of::type_of(bcx.ccx(), const_ty); - let symbol = csearch::get_symbol( - bcx.ccx().sess.cstore, - did); - let llval = symbol.with_c_str(|buf| { - llvm::LLVMAddGlobal(bcx.ccx().llmod, - llty.to_ref(), - buf) - }); - let mut extern_const_values = - bcx.ccx().extern_const_values.borrow_mut(); - extern_const_values.get().insert(did, llval); - llval - } - } - } - - let did = get_did(bcx.ccx(), did); - let val = get_val(bcx, did, const_ty); - DatumBlock { - bcx: bcx, - datum: Datum {val: val, - ty: const_ty, - mode: ByRef(ZeroMem)} - } - } - _ => { - DatumBlock { - bcx: bcx, - datum: trans_local_var(bcx, def) - } - } - } - } -} + * Translates a reference to a local variable or argument. + * This always results in an lvalue datum. + */ -pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum { let _icx = push_ctxt("trans_local_var"); return match def { @@ -1131,13 +1047,7 @@ pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum { let local_ty = node_id_type(bcx, nid); let llupvars = bcx.fcx.llupvars.borrow(); match llupvars.get().find(&nid) { - Some(&val) => { - Datum { - val: val, - ty: local_ty, - mode: ByRef(ZeroMem) - } - } + Some(&val) => Datum(val, local_ty, Lvalue), None => { bcx.sess().bug(format!( "trans_local_var: no llval for upvar {:?} found", nid)); @@ -1173,9 +1083,10 @@ pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum { } }; - fn take_local(bcx: &Block, - table: &HashMap, - nid: ast::NodeId) -> Datum { + fn take_local<'a>(bcx: &'a Block<'a>, + table: &HashMap>, + nid: ast::NodeId) + -> Datum { let datum = match table.find(&nid) { Some(&v) => v, None => { @@ -1189,15 +1100,18 @@ pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum { } } -// The optional node ID here is the node ID of the path identifying the enum -// variant in use. If none, this cannot possibly an enum variant (so, if it -// is and `node_id_opt` is none, this function fails). -pub fn with_field_tys( - tcx: ty::ctxt, - ty: ty::t, - node_id_opt: Option, - op: |ty::Disr, (&[ty::field])| -> R) - -> R { +pub fn with_field_tys(tcx: ty::ctxt, + ty: ty::t, + node_id_opt: Option, + op: |ty::Disr, (&[ty::field])| -> R) + -> R { + /*! + * Helper for enumerating the field types of structs, enums, or records. + * The optional node ID here is the node ID of the path identifying the enum + * variant in use. If none, this cannot possibly an enum variant (so, if it + * is and `node_id_opt` is none, this function fails). + */ + match ty::get(ty).sty { ty::ty_struct(did, ref substs) => { op(0, struct_fields(tcx, did, substs)) @@ -1330,6 +1244,7 @@ fn trans_adt<'a>( dest: Dest) -> &'a Block<'a> { let _icx = push_ctxt("trans_adt"); + let fcx = bcx.fcx; let mut bcx = bcx; let addr = match dest { Ignore => { @@ -1344,44 +1259,49 @@ fn trans_adt<'a>( } SaveIn(pos) => pos }; - let mut temp_cleanups = ~[]; + + // This scope holds intermediates that must be cleaned should + // failure occur before the ADT as a whole is ready. + let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); + adt::trans_start_init(bcx, repr, addr, discr); + for &(i, e) in fields.iter() { let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i); let e_ty = expr_ty_adjusted(bcx, e); bcx = trans_into(bcx, e, SaveIn(dest)); - add_clean_temp_mem(bcx, dest, e_ty); - temp_cleanups.push(dest); + fcx.schedule_drop_mem(cleanup::CustomScope(custom_cleanup_scope), + dest, e_ty); } + for base in optbase.iter() { // FIXME #6573: is it sound to use the destination's repr on the base? // And, would it ever be reasonable to be here with discr != 0? - let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base.expr)); + let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base.expr, "base")); for &(i, t) in base.fields.iter() { - let datum = base_datum.get_element(bcx, t, ZeroMem, |srcval| { - adt::trans_field_ptr(bcx, repr, srcval, discr, i) - }); + let datum = base_datum.get_element( + t, + |srcval| adt::trans_field_ptr(bcx, repr, srcval, discr, i)); let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i); - bcx = datum.store_to(bcx, INIT, dest); + bcx = datum.store_to(bcx, dest); } } - for cleanup in temp_cleanups.iter() { - revoke_clean(bcx, *cleanup); - } + fcx.pop_custom_cleanup_scope(custom_cleanup_scope); + return bcx; } -fn trans_immediate_lit<'a>( - bcx: &'a Block<'a>, - expr: &ast::Expr, - lit: ast::Lit) - -> DatumBlock<'a> { +fn trans_immediate_lit<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + lit: ast::Lit) + -> DatumBlock<'a, Expr> { // must not be a string constant, that is a RvalueDpsExpr let _icx = push_ctxt("trans_immediate_lit"); let ty = expr_ty(bcx, expr); - immediate_rvalue_bcx(bcx, consts::const_lit(bcx.ccx(), expr, lit), ty) + let v = consts::const_lit(bcx.ccx(), expr, lit); + immediate_rvalue_bcx(bcx, v, ty).to_expr_datumblock() } fn trans_unary_datum<'a>( @@ -1389,7 +1309,8 @@ fn trans_unary_datum<'a>( un_expr: &ast::Expr, op: ast::UnOp, sub_expr: &ast::Expr) - -> DatumBlock<'a> { + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; let _icx = push_ctxt("trans_unary_datum"); // if deref, would be LvalueExpr @@ -1406,25 +1327,23 @@ fn trans_unary_datum<'a>( return match op { ast::UnNot => { - let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result(); - - // If this is a boolean type, we must not use the LLVM Not - // instruction, as that is a *bitwise* not and we want *logical* - // not on our 8-bit boolean values. - let llresult = match ty::get(un_ty).sty { - ty::ty_bool => { - let llcond = ICmp(bcx, - lib::llvm::IntEQ, - val, - C_bool(false)); - Select(bcx, llcond, C_bool(true), C_bool(false)) - } - _ => Not(bcx, val) + let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); + let llresult = if ty::type_is_bool(un_ty) { + let val = datum.to_llscalarish(bcx); + let llcond = ICmp(bcx, + lib::llvm::IntEQ, + val, + C_bool(false)); + Select(bcx, llcond, C_bool(true), C_bool(false)) + } else { + // Note: `Not` is bitwise, not suitable for logical not. + Not(bcx, datum.to_llscalarish(bcx)) }; - immediate_rvalue_bcx(bcx, llresult, un_ty) + immediate_rvalue_bcx(bcx, llresult, un_ty).to_expr_datumblock() } ast::UnNeg => { - let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result(); + let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); + let val = datum.to_llscalarish(bcx); let llneg = { if ty::type_is_fp(un_ty) { FNeg(bcx, val) @@ -1432,7 +1351,7 @@ fn trans_unary_datum<'a>( Neg(bcx, val) } }; - immediate_rvalue_bcx(bcx, llneg, un_ty) + immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock() } ast::UnBox => { trans_boxed_expr(bcx, un_ty, sub_expr, sub_ty, heap_managed) @@ -1448,50 +1367,49 @@ fn trans_unary_datum<'a>( }; } -fn trans_boxed_expr<'a>( - bcx: &'a Block<'a>, - box_ty: ty::t, - contents: &ast::Expr, - contents_ty: ty::t, - heap: heap) - -> DatumBlock<'a> { +fn trans_boxed_expr<'a>(bcx: &'a Block<'a>, + box_ty: ty::t, + contents: &ast::Expr, + contents_ty: ty::t, + heap: heap) + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_boxed_expr"); + let fcx = bcx.fcx; if heap == heap_exchange { let llty = type_of::type_of(bcx.ccx(), contents_ty); let size = llsize_of(bcx.ccx(), llty); let Result { bcx: bcx, val: val } = malloc_raw_dyn(bcx, contents_ty, heap_exchange, size); - add_clean_free(bcx, val, heap_exchange); + let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); + fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope), + val, heap_exchange); let bcx = trans_into(bcx, contents, SaveIn(val)); - revoke_clean(bcx, val); - return immediate_rvalue_bcx(bcx, val, box_ty); + fcx.pop_custom_cleanup_scope(custom_cleanup_scope); + immediate_rvalue_bcx(bcx, val, box_ty).to_expr_datumblock() } else { - let base::MallocResult { - bcx, - smart_ptr: bx, - body - } = base::malloc_general(bcx, contents_ty, heap); - add_clean_free(bcx, bx, heap); + let base::MallocResult { bcx, smart_ptr: bx, body } = + base::malloc_general(bcx, contents_ty, heap); + let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); + fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope), + bx, heap); let bcx = trans_into(bcx, contents, SaveIn(body)); - revoke_clean(bcx, bx); - return immediate_rvalue_bcx(bcx, bx, box_ty); + fcx.pop_custom_cleanup_scope(custom_cleanup_scope); + immediate_rvalue_bcx(bcx, bx, box_ty).to_expr_datumblock() } } -fn trans_addr_of<'a>( - bcx: &'a Block<'a>, - expr: &ast::Expr, - subexpr: &ast::Expr) - -> DatumBlock<'a> { +fn trans_addr_of<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + subexpr: &ast::Expr) + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_addr_of"); let mut bcx = bcx; - let sub_datum = unpack_datum!(bcx, trans_to_datum(bcx, subexpr)); - let llval = sub_datum.to_ref_llval(bcx); - return immediate_rvalue_bcx(bcx, llval, expr_ty(bcx, expr)); + let sub_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, subexpr, "addr_of")); + let ty = expr_ty(bcx, expr); + return immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock(); } -pub fn trans_gc<'a>( - mut bcx: &'a Block<'a>, +fn trans_gc<'a>(mut bcx: &'a Block<'a>, expr: &ast::Expr, contents: &ast::Expr, dest: Dest) @@ -1514,13 +1432,12 @@ pub fn trans_gc<'a>( let repr = adt::represent_type(bcx.ccx(), expr_ty); adt::trans_start_init(bcx, repr, addr, 0); let field_dest = adt::trans_field_ptr(bcx, repr, addr, 0, 0); - let contents_datum_block = trans_boxed_expr(bcx, - box_ty, - contents, - contents_ty, - heap_managed); - bcx = contents_datum_block.bcx; - bcx = contents_datum_block.datum.move_to(bcx, INIT, field_dest); + let contents_datum = unpack_datum!(bcx, trans_boxed_expr(bcx, + box_ty, + contents, + contents_ty, + heap_managed)); + bcx = contents_datum.store_to(bcx, field_dest); // Next, wrap it up in the struct. bcx @@ -1533,17 +1450,13 @@ fn trans_eager_binop<'a>( binop_expr: &ast::Expr, binop_ty: ty::t, op: ast::BinOp, - lhs_datum: &Datum, - rhs_datum: &Datum) - -> DatumBlock<'a> { + lhs_t: ty::t, + lhs: ValueRef, + rhs_t: ty::t, + rhs: ValueRef) + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_eager_binop"); - let lhs = lhs_datum.to_appropriate_llval(bcx); - let lhs_t = lhs_datum.ty; - - let rhs = rhs_datum.to_appropriate_llval(bcx); - let rhs_t = rhs_datum.ty; - let mut intype = { if ty::type_is_bot(lhs_t) { rhs_t } else { lhs_t } @@ -1626,7 +1539,7 @@ fn trans_eager_binop<'a>( } }; - return immediate_rvalue_bcx(bcx, val, binop_ty); + immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock() } // refinement types would obviate the need for this @@ -1641,23 +1554,20 @@ fn trans_lazy_binop<'a>( op: lazy_binop_ty, a: &ast::Expr, b: &ast::Expr) - -> DatumBlock<'a> { + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_lazy_binop"); let binop_ty = expr_ty(bcx, binop_expr); - let bcx = bcx; + let fcx = bcx.fcx; - let Result {bcx: past_lhs, val: lhs} = { - base::with_scope_result(bcx, a.info(), "lhs", |bcx| { - trans_to_datum(bcx, a).to_result() - }) - }; + let DatumBlock {bcx: past_lhs, datum: lhs} = trans(bcx, a); + let lhs = lhs.to_llscalarish(past_lhs); if past_lhs.unreachable.get() { - return immediate_rvalue_bcx(past_lhs, lhs, binop_ty); + return immediate_rvalue_bcx(past_lhs, lhs, binop_ty).to_expr_datumblock(); } - let join = base::sub_block(bcx, "join"); - let before_rhs = base::sub_block(bcx, "rhs"); + let join = fcx.new_id_block("join", binop_expr.id); + let before_rhs = fcx.new_id_block("before_rhs", b.id); let lhs_i1 = bool_to_i1(past_lhs, lhs); match op { @@ -1665,21 +1575,18 @@ fn trans_lazy_binop<'a>( lazy_or => CondBr(past_lhs, lhs_i1, join.llbb, before_rhs.llbb) } - let Result {bcx: past_rhs, val: rhs} = { - base::with_scope_result(before_rhs, b.info(), "rhs", |bcx| { - trans_to_datum(bcx, b).to_result() - }) - }; + let DatumBlock {bcx: past_rhs, datum: rhs} = trans(before_rhs, b); + let rhs = rhs.to_llscalarish(past_rhs); if past_rhs.unreachable.get() { - return immediate_rvalue_bcx(join, lhs, binop_ty); + return immediate_rvalue_bcx(join, lhs, binop_ty).to_expr_datumblock(); } Br(past_rhs, join.llbb); let phi = Phi(join, Type::bool(), [lhs, rhs], [past_lhs.llbb, - past_rhs.llbb]); + past_rhs.llbb]); - return immediate_rvalue_bcx(join, phi, binop_ty); + return immediate_rvalue_bcx(join, phi, binop_ty).to_expr_datumblock(); } fn trans_binary<'a>( @@ -1688,8 +1595,9 @@ fn trans_binary<'a>( op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) - -> DatumBlock<'a> { + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_binary"); + let ccx = bcx.ccx(); match op { ast::BiAnd => { @@ -1700,11 +1608,23 @@ fn trans_binary<'a>( } _ => { let mut bcx = bcx; - let lhs_datum = unpack_datum!(bcx, trans_to_datum(bcx, lhs)); - let rhs_datum = unpack_datum!(bcx, trans_to_datum(bcx, rhs)); + let lhs_datum = unpack_datum!(bcx, trans(bcx, lhs)); + let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs)); let binop_ty = expr_ty(bcx, binop_expr); + + debug!("trans_binary (expr {}): lhs_datum={}", + binop_expr.id, + lhs_datum.to_str(ccx)); + let lhs_ty = lhs_datum.ty; + let lhs = lhs_datum.to_llscalarish(bcx); + + debug!("trans_binary (expr {}): rhs_datum={}", + binop_expr.id, + rhs_datum.to_str(ccx)); + let rhs_ty = rhs_datum.ty; + let rhs = rhs_datum.to_llscalarish(bcx); trans_eager_binop(bcx, binop_expr, binop_ty, op, - &lhs_datum, &rhs_datum) + lhs_ty, lhs, rhs_ty, rhs) } } } @@ -1724,14 +1644,15 @@ fn trans_overloaded_op<'a>( }; let fty = node_id_type(bcx, callee_id); callee::trans_call_inner(bcx, - expr.info(), + Some(expr_info(expr)), fty, ret_ty, - |bcx| { + |bcx, arg_cleanup_scope| { meth::trans_method_callee(bcx, callee_id, rcvr, - origin) + origin, + arg_cleanup_scope) }, callee::ArgExprs(args), Some(dest), @@ -1799,85 +1720,90 @@ pub fn cast_type_kind(t: ty::t) -> cast_kind { } } -fn trans_imm_cast<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, id: ast::NodeId) - -> DatumBlock<'a> { +fn trans_imm_cast<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + id: ast::NodeId) + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_cast"); + let mut bcx = bcx; let ccx = bcx.ccx(); - let t_out = node_id_type(bcx, id); - - let mut bcx = bcx; - let llexpr = unpack_result!(bcx, trans_to_datum(bcx, expr).to_result()); - let ll_t_in = val_ty(llexpr); let t_in = expr_ty(bcx, expr); - let ll_t_out = type_of::type_of(ccx, t_out); - + let t_out = node_id_type(bcx, id); let k_in = cast_type_kind(t_in); let k_out = cast_type_kind(t_out); let s_in = k_in == cast_integral && ty::type_is_signed(t_in); + let ll_t_in = type_of::type_of(ccx, t_in); + let ll_t_out = type_of::type_of(ccx, t_out); - let newval = - match (k_in, k_out) { - (cast_integral, cast_integral) => { - int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) - } - (cast_float, cast_float) => { - float_cast(bcx, ll_t_out, ll_t_in, llexpr) - } - (cast_integral, cast_float) => { - if s_in { - SIToFP(bcx, llexpr, ll_t_out) - } else { UIToFP(bcx, llexpr, ll_t_out) } - } - (cast_float, cast_integral) => { - if ty::type_is_signed(t_out) { - FPToSI(bcx, llexpr, ll_t_out) - } else { FPToUI(bcx, llexpr, ll_t_out) } - } - (cast_integral, cast_pointer) => { - IntToPtr(bcx, llexpr, ll_t_out) - } - (cast_pointer, cast_integral) => { - PtrToInt(bcx, llexpr, ll_t_out) - } - (cast_pointer, cast_pointer) => { - PointerCast(bcx, llexpr, ll_t_out) - } - (cast_enum, cast_integral) | - (cast_enum, cast_float) => { - let bcx = bcx; - let repr = adt::represent_type(ccx, t_in); - let llexpr_ptr; - if type_is_immediate(ccx, t_in) { - llexpr_ptr = Alloca(bcx, ll_t_in, ""); - Store(bcx, llexpr, llexpr_ptr); - } else { - llexpr_ptr = llexpr; - } - let lldiscrim_a = adt::trans_get_discr(bcx, repr, llexpr_ptr, Some(Type::i64())); - match k_out { - cast_integral => int_cast(bcx, ll_t_out, - val_ty(lldiscrim_a), - lldiscrim_a, true), - cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out), - _ => ccx.sess.bug(format!("translating unsupported cast: \ - {} ({:?}) -> {} ({:?})", - t_in.repr(ccx.tcx), k_in, - t_out.repr(ccx.tcx), k_out)) - } + // Convert the value to be cast into a ValueRef, either by-ref or + // by-value as appropriate given its type: + let datum = unpack_datum!(bcx, trans(bcx, expr)); + let newval = match (k_in, k_out) { + (cast_integral, cast_integral) => { + let llexpr = datum.to_llscalarish(bcx); + int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) + } + (cast_float, cast_float) => { + let llexpr = datum.to_llscalarish(bcx); + float_cast(bcx, ll_t_out, ll_t_in, llexpr) + } + (cast_integral, cast_float) => { + let llexpr = datum.to_llscalarish(bcx); + if s_in { + SIToFP(bcx, llexpr, ll_t_out) + } else { UIToFP(bcx, llexpr, ll_t_out) } + } + (cast_float, cast_integral) => { + let llexpr = datum.to_llscalarish(bcx); + if ty::type_is_signed(t_out) { + FPToSI(bcx, llexpr, ll_t_out) + } else { FPToUI(bcx, llexpr, ll_t_out) } + } + (cast_integral, cast_pointer) => { + let llexpr = datum.to_llscalarish(bcx); + IntToPtr(bcx, llexpr, ll_t_out) + } + (cast_pointer, cast_integral) => { + let llexpr = datum.to_llscalarish(bcx); + PtrToInt(bcx, llexpr, ll_t_out) + } + (cast_pointer, cast_pointer) => { + let llexpr = datum.to_llscalarish(bcx); + PointerCast(bcx, llexpr, ll_t_out) + } + (cast_enum, cast_integral) | + (cast_enum, cast_float) => { + let mut bcx = bcx; + let repr = adt::represent_type(ccx, t_in); + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id)); + let llexpr_ptr = datum.to_llref(); + let lldiscrim_a = + adt::trans_get_discr(bcx, repr, llexpr_ptr, Some(Type::i64())); + match k_out { + cast_integral => int_cast(bcx, ll_t_out, + val_ty(lldiscrim_a), + lldiscrim_a, true), + cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out), + _ => ccx.sess.bug(format!("translating unsupported cast: \ + {} ({:?}) -> {} ({:?})", + t_in.repr(ccx.tcx), k_in, + t_out.repr(ccx.tcx), k_out)) } - _ => ccx.sess.bug(format!("translating unsupported cast: \ - {} ({:?}) -> {} ({:?})", - t_in.repr(ccx.tcx), k_in, - t_out.repr(ccx.tcx), k_out)) - }; - return immediate_rvalue_bcx(bcx, newval, t_out); + } + _ => ccx.sess.bug(format!("translating unsupported cast: \ + {} ({:?}) -> {} ({:?})", + t_in.repr(ccx.tcx), k_in, + t_out.repr(ccx.tcx), k_out)) + }; + return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock(); } fn trans_assign_op<'a>( bcx: &'a Block<'a>, expr: &ast::Expr, - callee_id: ast::NodeId, + _callee_id: ast::NodeId, op: ast::BinOp, dst: &ast::Expr, src: @ast::Expr) @@ -1887,40 +1813,32 @@ fn trans_assign_op<'a>( debug!("trans_assign_op(expr={})", bcx.expr_to_str(expr)); - // Evaluate LHS (destination), which should be an lvalue - let dst_datum = unpack_datum!(bcx, trans_lvalue_unadjusted(bcx, dst)); + // User-defined operator methods cannot be used with `+=` etc right now + assert!({ + let method_map = bcx.ccx().maps.method_map.borrow(); + !method_map.get().find(&expr.id).is_some() + }); - // A user-defined operator method - let found = { - let method_map = bcx.ccx().maps.method_map.borrow(); - method_map.get().find(&expr.id).is_some() - }; - if found { - // FIXME(#2528) evaluates the receiver twice!! - let scratch = scratch_datum(bcx, dst_datum.ty, "__assign_op", false); - let bcx = trans_overloaded_op(bcx, - expr, - callee_id, - dst, - ~[src], - dst_datum.ty, - SaveIn(scratch.val)); - return scratch.move_to_datum(bcx, DROP_EXISTING, dst_datum); - } + // Evaluate LHS (destination), which should be an lvalue + let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op")); + assert!(!ty::type_needs_drop(bcx.tcx(), dst_datum.ty)); + let dst_ty = dst_datum.ty; + let dst = Load(bcx, dst_datum.val); - // Evaluate RHS (source) - let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src)); + // Evaluate RHS + let rhs_datum = unpack_datum!(bcx, trans(bcx, src)); + let rhs_ty = rhs_datum.ty; + let rhs = rhs_datum.to_llscalarish(bcx); // Perform computation and store the result - let result_datum = - unpack_datum!(bcx, - trans_eager_binop( - bcx, expr, dst_datum.ty, op, - &dst_datum, &src_datum)); - return result_datum.copy_to_datum(bcx, DROP_EXISTING, dst_datum); + let result_datum = unpack_datum!( + bcx, trans_eager_binop(bcx, expr, dst_datum.ty, op, + dst_ty, dst, rhs_ty, rhs)); + return result_datum.store_to(bcx, dst_datum.val); } -pub fn trans_log_level<'a>(bcx: &'a Block<'a>) -> DatumBlock<'a> { +fn trans_log_level<'a>(bcx: &'a Block<'a>) + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_log_level"); let ccx = bcx.ccx(); @@ -1974,6 +1892,193 @@ pub fn trans_log_level<'a>(bcx: &'a Block<'a>) -> DatumBlock<'a> { } }; - return immediate_rvalue_bcx(bcx, Load(bcx, global), ty::mk_u32()); + immediate_rvalue_bcx(bcx, Load(bcx, global), ty::mk_u32()).to_expr_datumblock() +} + +fn deref_multiple<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum, + times: uint) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + let mut datum = datum; + for i in range(1, times+1) { + datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, i)); + } + DatumBlock { bcx: bcx, datum: datum } +} + +fn deref_once<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum, + derefs: uint) + -> DatumBlock<'a, Expr> { + let ccx = bcx.ccx(); + let bcx = write_guard::root_and_write_guard(&datum, bcx, expr.span, + expr.id, derefs); + + debug!("deref_once(expr={}, datum={}, derefs={})", + expr.repr(bcx.tcx()), + datum.to_str(ccx), + derefs); + + let mut bcx = bcx; + + let r = match ty::get(datum.ty).sty { + ty::ty_uniq(content_ty) => { + deref_owned_pointer(bcx, expr, datum, content_ty) + } + + ty::ty_box(content_ty) => { + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); + let llptrref = datum.to_llref(); + let llptr = Load(bcx, llptrref); + let llbody = GEPi(bcx, llptr, [0u, abi::box_field_body]); + DatumBlock(bcx, Datum(llbody, content_ty, LvalueExpr)) + } + + ty::ty_ptr(ty::mt { ty: content_ty, .. }) | + ty::ty_rptr(_, ty::mt { ty: content_ty, .. }) => { + assert!(!ty::type_needs_drop(bcx.tcx(), datum.ty)); + + let ptr = datum.to_llscalarish(bcx); + + // Always generate an lvalue datum, even if datum.mode is + // an rvalue. This is because datum.mode is only an + // rvalue for non-owning pointers like &T or *T, in which + // case cleanup *is* scheduled elsewhere, by the true + // owner (or, in the case of *T, by the user). + DatumBlock(bcx, Datum(ptr, content_ty, LvalueExpr)) + } + + ty::ty_enum(..) | + ty::ty_struct(..) => { + // Subtle efficiency note: In the case where we have a + // newtype struct where the struct itself does not have a + // dtor, but the contents do, we could avoid forcing the + // data into Lvalue and instead return an Rvalue. But it + // doesn't seem worth the trouble. + let datum = unpack_datum!(bcx, ensure_cleanup(bcx, expr, datum)); + + // Unlike the pointer case above, we generate an + // rvalue datum if we are given an rvalue. There are + // two reasons that this makes sense here: + // + // 1. dereferencing a struct does not actually perform a + // pointer load and hence the resulting value is not + // naturally by reference, as would be required by an + // lvalue result. + // + // 2. the struct always owns its contents, and hence and does not + // itself have a dtor (else it would be in lvalue mode). + let repr = adt::represent_type(ccx, datum.ty); + let ty = adt::deref_ty(ccx, repr); + let Datum { val, kind, .. } = datum; + let r = match kind { + LvalueExpr => { + Datum { + val: adt::trans_field_ptr(bcx, repr, val, 0, 0), + ty: ty, + kind: LvalueExpr + } + } + RvalueExpr(Rvalue { mode: ByRef }) => { + Datum { + val: adt::trans_field_ptr(bcx, repr, val, 0, 0), + ty: ty, + kind: RvalueExpr(Rvalue(ByValue)) + } + } + RvalueExpr(Rvalue { mode: ByValue }) => { + Datum { + val: ExtractValue(bcx, val, 0), + ty: ty, + kind: RvalueExpr(Rvalue(ByValue)) + } + } + }; + DatumBlock(bcx, r) + } + + _ => { + bcx.tcx().sess.span_bug( + expr.span, + format!("deref invoked on expr of illegal type {}", + datum.ty.repr(bcx.tcx()))); + } + }; + + debug!("deref_once(expr={}, derefs={}, result={})", + expr.id, derefs, r.datum.to_str(ccx)); + + return r; + + fn ensure_cleanup<'a>(mut bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'a, Expr> { + /*! + * If the datum contains data that needs to be dropped, + * convert it to an lvalue, thus ensuring that cleanup + * is scheduled. + */ + + if ty::type_needs_drop(bcx.tcx(), datum.ty) { + let lv_datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); + DatumBlock(bcx, lv_datum.to_expr_datum()) + } else { + DatumBlock(bcx, datum) + } + } + + fn deref_owned_pointer<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum, + content_ty: ty::t) + -> DatumBlock<'a, Expr> { + /*! + * We microoptimize derefs of owned pointers a bit here. + * Basically, the idea is to make the deref of an rvalue + * result in an rvalue. This helps to avoid intermediate stack + * slots in the resulting LLVM. The idea here is that, if the + * `~T` pointer is an rvalue, then we can schedule a *shallow* + * free of the `~T` pointer, and then return a ByRef rvalue + * into the pointer. Because the free is shallow, it is legit + * to return an rvalue, because we know that the contents are + * not yet scheduled to be freed. The language rules ensure that the + * contents will be used (or moved) before the free occurs. + */ + + match datum.kind { + RvalueExpr(Rvalue { mode: ByRef }) => { + let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); + let ptr = Load(bcx, datum.val); + bcx.fcx.schedule_free_value(scope, ptr, heap_exchange); + } + RvalueExpr(Rvalue { mode: ByValue }) => { + let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); + bcx.fcx.schedule_free_value(scope, datum.val, heap_exchange); + } + LvalueExpr => { } + } + + // If we had an rvalue in, we produce an rvalue out. + let (llptr, kind) = match datum.kind { + LvalueExpr => { + (Load(bcx, datum.val), LvalueExpr) + } + RvalueExpr(Rvalue { mode: ByRef }) => { + (Load(bcx, datum.val), RvalueExpr(Rvalue(ByRef))) + } + RvalueExpr(Rvalue { mode: ByValue }) => { + (datum.val, RvalueExpr(Rvalue(ByRef))) + } + }; + + let datum = Datum { ty: content_ty, val: llptr, kind: kind }; + DatumBlock { bcx: bcx, datum: datum } + } } diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 2b55c0aac0ca2..ea9f608ff5671 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -290,12 +290,12 @@ pub fn trans_native_call<'a>( // A function pointer is called without the declaration available, so we have to apply // any attributes with ABI implications directly to the call instruction. Right now, the // only attribute we need to worry about is `sret`. - let attrs; - if fn_type.ret_ty.is_indirect() { - attrs = &[(1, StructRetAttribute)]; + let sret_attr = [(1, StructRetAttribute)]; + let attrs = if fn_type.ret_ty.is_indirect() { + sret_attr.as_slice() } else { - attrs = &[]; - } + &[] + }; let llforeign_retval = CallWithConv(bcx, llfn, llargs_foreign, cc, attrs); // If the function we just called does not use an outpointer, @@ -491,7 +491,6 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @CrateContext, None, None, id, - None, []); return llfndecl; } diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 9b3243fa3ef19..ae03d48dbf0dc 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -21,9 +21,10 @@ use middle::lang_items::{FreeFnLangItem, ExchangeFreeFnLangItem}; use middle::trans::adt; use middle::trans::base::*; use middle::trans::callee; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::closure; use middle::trans::common::*; -use middle::trans::datum::immediate_rvalue; use middle::trans::build::*; use middle::trans::expr; use middle::trans::machine::*; @@ -269,25 +270,23 @@ fn call_tydesc_glue<'a>(cx: &'a Block<'a>, v: ValueRef, t: ty::t, field: uint) fn make_visit_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) -> &'a Block<'a> { let _icx = push_ctxt("make_visit_glue"); - with_scope(bcx, None, "visitor cleanup", |bcx| { - let mut bcx = bcx; - let (visitor_trait, object_ty) = match ty::visitor_object_ty(bcx.tcx(), - ty::ReStatic) { - Ok(pair) => pair, - Err(s) => { - bcx.tcx().sess.fatal(s); - } - }; - let v = PointerCast(bcx, v, type_of(bcx.ccx(), object_ty).ptr_to()); - bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, visitor_trait.def_id); - // The visitor is a boxed object and needs to be dropped - add_clean(bcx, v, object_ty); - bcx - }) + let mut bcx = bcx; + let (visitor_trait, object_ty) = match ty::visitor_object_ty(bcx.tcx(), + ty::ReStatic) { + Ok(pair) => pair, + Err(s) => { + bcx.tcx().sess.fatal(s); + } + }; + let v = PointerCast(bcx, v, type_of(bcx.ccx(), object_ty).ptr_to()); + bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, visitor_trait.def_id); + bcx } -pub fn make_free_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) - -> &'a Block<'a> { +pub fn make_free_glue<'a>(bcx: &'a Block<'a>, + v: ValueRef, + t: ty::t) + -> &'a Block<'a> { // NB: v0 is an *alias* of type t here, not a direct value. let _icx = push_ctxt("make_free_glue"); match ty::get(t).sty { @@ -297,14 +296,13 @@ pub fn make_free_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) let bcx = drop_ty(bcx, body, body_ty); trans_free(bcx, v) } - ty::ty_uniq(..) => { - let box_datum = immediate_rvalue(Load(bcx, v), t); - let not_null = IsNotNull(bcx, box_datum.val); + ty::ty_uniq(content_ty) => { + let llbox = Load(bcx, v); + let not_null = IsNotNull(bcx, llbox); with_cond(bcx, not_null, |bcx| { - let body_datum = box_datum.box_body(bcx); - let bcx = drop_ty(bcx, body_datum.to_ref_llval(bcx), body_datum.ty); - trans_exchange_free(bcx, box_datum.val) - }) + let bcx = drop_ty(bcx, llbox, content_ty); + trans_exchange_free(bcx, llbox) + }) } ty::ty_vec(_, ty::vstore_uniq) | ty::ty_str(ty::vstore_uniq) | ty::ty_vec(_, ty::vstore_box) | ty::ty_str(ty::vstore_box) => { @@ -362,21 +360,24 @@ pub fn trans_struct_drop<'a>( // Be sure to put all of the fields into a scope so we can use an invoke // instruction to call the user destructor but still call the field // destructors if the user destructor fails. - with_scope(bcx, None, "field drops", |bcx| { - let self_arg = PointerCast(bcx, v0, params[0]); - let args = ~[self_arg]; - - // Add all the fields as a value which needs to be cleaned at the end of - // this scope. - let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs); - for (i, fld) in field_tys.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i); - add_clean(bcx, llfld_a, fld.mt.ty); - } + let field_scope = bcx.fcx.push_custom_cleanup_scope(); + + let self_arg = PointerCast(bcx, v0, params[0]); + let args = ~[self_arg]; + + // Add all the fields as a value which needs to be cleaned at the end of + // this scope. + let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs); + for (i, fld) in field_tys.iter().enumerate() { + let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i); + bcx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope), + llfld_a, + fld.mt.ty); + } - let (_, bcx) = invoke(bcx, dtor_addr, args, [], None); - bcx - }) + let (_, bcx) = invoke(bcx, dtor_addr, args, [], None); + + bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, field_scope) } pub fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) @@ -451,11 +452,13 @@ pub fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) fn decr_refcnt_maybe_free<'a>(bcx: &'a Block<'a>, box_ptr_ptr: ValueRef, t: Option) -> &'a Block<'a> { let _icx = push_ctxt("decr_refcnt_maybe_free"); + let fcx = bcx.fcx; let ccx = bcx.ccx(); - let decr_bcx = sub_block(bcx, "decr"); - let free_bcx = sub_block(decr_bcx, "free"); - let next_bcx = sub_block(bcx, "next"); + let decr_bcx = fcx.new_temp_block("decr"); + let free_bcx = fcx.new_temp_block("free"); + let next_bcx = fcx.new_temp_block("next"); + let box_ptr = Load(bcx, box_ptr_ptr); let llnotnull = IsNotNull(bcx, box_ptr); CondBr(bcx, llnotnull, decr_bcx.llbb, next_bcx.llbb); @@ -593,7 +596,7 @@ fn make_generic_glue(ccx: @CrateContext, t: ty::t, llfn: ValueRef, let _s = StatRecorder::new(ccx, glue_name); let fcx = new_fn_ctxt(ccx, ~[], llfn, ty::mk_nil(), None); - init_function(&fcx, false, ty::mk_nil(), None, None); + init_function(&fcx, false, ty::mk_nil(), None); lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); ccx.stats.n_glues_created.set(ccx.stats.n_glues_created.get() + 1u); diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index 7d0e5a435441b..98e3593a9f15f 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -178,7 +178,6 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::DefId) self_kind, None, mth.id, - Some(&*mth), []); } local_def(mth.id) diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index ff5c22e726a37..81ee292c835ed 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -153,14 +153,14 @@ pub fn trans_intrinsic(ccx: @CrateContext, let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, item.id)); - let fcx = new_fn_ctxt_w_id(ccx, - path, - decl, - item.id, - output_type, - Some(substs), - Some(item.span)); - init_function(&fcx, true, output_type, Some(substs), None); + let fcx = new_fn_ctxt_detailed(ccx, + path, + decl, + item.id, + output_type, + Some(substs), + Some(item.span)); + init_function(&fcx, true, output_type, Some(substs)); set_always_inline(fcx.llfn); @@ -254,27 +254,18 @@ pub fn trans_intrinsic(ccx: @CrateContext, let lltp_ty = type_of::type_of(ccx, tp_ty); Ret(bcx, C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty))); } - "move_val" => { + "move_val_init" => { // Create a datum reflecting the value being moved. // Use `appropriate_mode` so that the datum is by ref // if the value is non-immediate. Note that, with // intrinsics, there are no argument cleanups to - // concern ourselves with. - let tp_ty = substs.tys[0]; - let mode = appropriate_mode(ccx, tp_ty); - let src = Datum {val: get_param(decl, first_real_arg + 1u), - ty: tp_ty, mode: mode}; - bcx = src.move_to(bcx, DROP_EXISTING, - get_param(decl, first_real_arg)); - RetVoid(bcx); - } - "move_val_init" => { - // See comments for `"move_val"`. + // concern ourselves with, so we can use an rvalue datum. let tp_ty = substs.tys[0]; - let mode = appropriate_mode(ccx, tp_ty); + let mode = appropriate_rvalue_mode(ccx, tp_ty); let src = Datum {val: get_param(decl, first_real_arg + 1u), - ty: tp_ty, mode: mode}; - bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg)); + ty: tp_ty, + kind: Rvalue(mode)}; + bcx = src.store_to(bcx, get_param(decl, first_real_arg)); RetVoid(bcx); } "min_align_of" => { diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index f8aef90838118..b9f4a9077b06e 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -18,6 +18,7 @@ use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee::*; use middle::trans::callee; +use middle::trans::cleanup; use middle::trans::common::*; use middle::trans::datum::*; use middle::trans::expr::{SaveIn, Ignore}; @@ -132,7 +133,6 @@ pub fn trans_method(ccx: @CrateContext, self_ty, param_substs, method.id, - Some(method), []); llfn } @@ -141,7 +141,8 @@ pub fn trans_method_callee<'a>( bcx: &'a Block<'a>, callee_id: ast::NodeId, this: &ast::Expr, - mentry: typeck::method_map_entry) + mentry: typeck::method_map_entry, + arg_cleanup_scope: cleanup::ScopeId) -> Callee<'a> { let _icx = push_ctxt("impl::trans_method_callee"); @@ -153,9 +154,8 @@ pub fn trans_method_callee<'a>( match mentry.origin { typeck::method_static(did) => { let self_ty = monomorphize_type(bcx, mentry.self_ty); - let mut temp_cleanups = ~[]; let Result {bcx, val} = trans_arg_expr(bcx, self_ty, this, - &mut temp_cleanups, + arg_cleanup_scope, DontAutorefArg); // HACK should not need the pointer cast, eventually trans_fn_ref // should return a function type with the right type for self. @@ -168,7 +168,6 @@ pub fn trans_method_callee<'a>( data: Method(MethodData { llfn: llfn_val, llself: val, - temp_cleanup: temp_cleanups.head_opt().map(|v| *v) }) } } @@ -186,7 +185,8 @@ pub fn trans_method_callee<'a>( let vtbl = find_vtable(bcx.tcx(), substs, p, b); trans_monomorphized_callee(bcx, callee_id, this, mentry, - trait_id, off, vtbl) + trait_id, off, vtbl, + arg_cleanup_scope) } // how to get rid of this? None => fail!("trans_method_callee: missing param_substs") @@ -197,7 +197,8 @@ pub fn trans_method_callee<'a>( trans_trait_callee(bcx, callee_id, mt.real_index, - this) + this, + arg_cleanup_scope) } } } @@ -319,7 +320,8 @@ pub fn trans_monomorphized_callee<'a>( mentry: typeck::method_map_entry, trait_id: ast::DefId, n_method: uint, - vtbl: typeck::vtable_origin) + vtbl: typeck::vtable_origin, + arg_cleanup_scope: cleanup::ScopeId) -> Callee<'a> { let _icx = push_ctxt("impl::trans_monomorphized_callee"); return match vtbl { @@ -330,9 +332,8 @@ pub fn trans_monomorphized_callee<'a>( // obtain the `self` value: let self_ty = monomorphize_type(bcx, mentry.self_ty); - let mut temp_cleanups = ~[]; let Result {bcx, val} = trans_arg_expr(bcx, self_ty, base, - &mut temp_cleanups, + arg_cleanup_scope, DontAutorefArg); // create a concatenated set of substitutions which includes @@ -361,7 +362,6 @@ pub fn trans_monomorphized_callee<'a>( data: Method(MethodData { llfn: llfn_val, llself: val, - temp_cleanup: temp_cleanups.head_opt().map(|v| *v) }) } } @@ -425,7 +425,8 @@ pub fn trans_trait_callee<'a>( bcx: &'a Block<'a>, callee_id: ast::NodeId, n_method: uint, - self_expr: &ast::Expr) + self_expr: &ast::Expr, + arg_cleanup_scope: cleanup::ScopeId) -> Callee<'a> { /*! * Create a method callee where the method is coming from a trait @@ -443,34 +444,35 @@ pub fn trans_trait_callee<'a>( let self_ty = expr_ty_adjusted(bcx, self_expr); let self_scratch = match ty::get(self_ty).sty { ty::ty_trait(_, _, ty::RegionTraitStore(..), _, _) => { - unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr)) + unpack_datum!(bcx, expr::trans(bcx, self_expr)) } _ => { - let d = scratch_datum(bcx, self_ty, "__trait_callee", false); - bcx = expr::trans_into(bcx, self_expr, expr::SaveIn(d.val)); // Arrange a temporary cleanup for the object in case something // should go wrong before the method is actually *invoked*. - d.add_clean(bcx); - d + let datum = unpack_datum!( + bcx, + lvalue_scratch_datum( + bcx, self_ty, "__trait_callee", false, arg_cleanup_scope, (), + |(), bcx, llval| expr::trans_into(bcx, self_expr, + expr::SaveIn(llval)))); + datum.to_expr_datum() } }; - let callee_ty = node_id_type(bcx, callee_id); + assert!(self_scratch.kind.is_by_ref()); // FIXME why special case above?? trans_trait_callee_from_llval(bcx, callee_ty, n_method, - self_scratch.val, - Some(self_scratch.val)) + self_scratch.val) } pub fn trans_trait_callee_from_llval<'a>( bcx: &'a Block<'a>, callee_ty: ty::t, n_method: uint, - llpair: ValueRef, - temp_cleanup: Option) - -> Callee<'a> { + llpair: ValueRef) + -> Callee<'a> { /*! * Same as `trans_trait_callee()` above, except that it is given * a by-ref pointer to the object pair. @@ -501,7 +503,6 @@ pub fn trans_trait_callee_from_llval<'a>( data: Method(MethodData { llfn: mptr, llself: llself, - temp_cleanup: temp_cleanup }) }; } @@ -632,41 +633,38 @@ fn emit_vtable_methods(bcx: &Block, }) } -pub fn trans_trait_cast<'a>( - bcx: &'a Block<'a>, - val: &ast::Expr, - id: ast::NodeId, - dest: expr::Dest, - obj: Option) - -> &'a Block<'a> { +pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>, + datum: Datum, + id: ast::NodeId, + dest: expr::Dest) + -> &'a Block<'a> { + /*! + * Generates the code to convert from a pointer (`~T`, `&T`, etc) + * into an object (`~Trait`, `&Trait`, etc). This means creating a + * pair where the first word is the pointer and the second word is + * an appropriate vtable. + */ + let mut bcx = bcx; let _icx = push_ctxt("impl::trans_cast"); let lldest = match dest { Ignore => { - return expr::trans_into(bcx, val, Ignore); + return datum.clean(bcx, "trait_cast", id); } SaveIn(dest) => dest }; let ccx = bcx.ccx(); - let v_ty = expr_ty(bcx, val); + let v_ty = datum.ty; + let llbox_ty = type_of(bcx.ccx(), datum.ty); + // Store the pointer into the first half of pair. let mut llboxdest = GEPi(bcx, lldest, [0u, abi::trt_field_box]); - // Just store the pointer into the pair. (Region/borrowed - // and boxed trait objects are represented as pairs, and - // have no type descriptor field.) - llboxdest = PointerCast(bcx, - llboxdest, - type_of(bcx.ccx(), v_ty).ptr_to()); - bcx = match obj { - Some(datum) => { - datum.store_to_dest(bcx, SaveIn(llboxdest)) - } - None => expr::trans_into(bcx, val, SaveIn(llboxdest)) - }; + llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to()); + bcx = datum.store_to(bcx, llboxdest); - // Store the vtable into the pair or triple. + // Store the vtable into the second half of pair. // This is structured a bit funny because of dynamic borrow failures. let origins = { let res = { @@ -677,9 +675,9 @@ pub fn trans_trait_cast<'a>( res[0] }; let vtable = get_vtable(bcx, v_ty, origins); - Store(bcx, vtable, PointerCast(bcx, - GEPi(bcx, lldest, [0u, abi::trt_field_vtable]), - val_ty(vtable).ptr_to())); + let llvtabledest = GEPi(bcx, lldest, [0u, abi::trt_field_vtable]); + let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to()); + Store(bcx, vtable, llvtabledest); bcx } diff --git a/src/librustc/middle/trans/mod.rs b/src/librustc/middle/trans/mod.rs index e534e087cb835..7ac491edfeb2a 100644 --- a/src/librustc/middle/trans/mod.rs +++ b/src/librustc/middle/trans/mod.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub mod doc; pub mod macros; pub mod inline; pub mod monomorphize; @@ -44,3 +45,4 @@ pub mod type_; pub mod value; pub mod basic_block; pub mod llrepr; +pub mod cleanup; diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index a05ca4296b130..a128a953eacff 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -238,7 +238,6 @@ pub fn monomorphic_fn(ccx: @CrateContext, None, Some(psubsts), fn_id.node, - None, []); d } diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 2a8c23a6c32a4..c13bb139da759 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -61,7 +61,7 @@ impl<'a> Reflector<'a> { let bcx = self.bcx; let str_vstore = ty::vstore_slice(ty::ReStatic); let str_ty = ty::mk_str(bcx.tcx(), str_vstore); - let scratch = scratch_datum(bcx, str_ty, "", false); + let scratch = rvalue_scratch_datum(bcx, str_ty, ""); let len = C_uint(bcx.ccx(), s.len()); let c_str = PointerCast(bcx, C_cstr(bcx.ccx(), s), Type::i8p()); Store(bcx, c_str, GEPi(bcx, scratch.val, [ 0, 0 ])); @@ -90,6 +90,7 @@ impl<'a> Reflector<'a> { } pub fn visit(&mut self, ty_name: &str, args: &[ValueRef]) { + let fcx = self.bcx.fcx; let tcx = self.bcx.tcx(); let mth_idx = ty::method_idx( tcx.sess.ident_of(~"visit_" + ty_name), @@ -106,14 +107,13 @@ impl<'a> Reflector<'a> { let bool_ty = ty::mk_bool(); let result = unpack_result!(bcx, callee::trans_call_inner( self.bcx, None, mth_ty, bool_ty, - |bcx| meth::trans_trait_callee_from_llval(bcx, - mth_ty, - mth_idx, - v, - None), + |bcx, _| meth::trans_trait_callee_from_llval(bcx, + mth_ty, + mth_idx, + v), ArgVals(args), None, DontAutorefArg)); let result = bool_to_i1(bcx, result); - let next_bcx = sub_block(bcx, "next"); + let next_bcx = fcx.new_temp_block("next"); CondBr(bcx, result, next_bcx.llbb, self.final_bcx.llbb); self.bcx = next_bcx } @@ -298,7 +298,7 @@ impl<'a> Reflector<'a> { llfdecl, ty::mk_u64(), None); - init_function(&fcx, false, ty::mk_u64(), None, None); + init_function(&fcx, false, ty::mk_u64(), None); let arg = unsafe { // @@ -308,13 +308,13 @@ impl<'a> Reflector<'a> { // llvm::LLVMGetParam(llfdecl, fcx.arg_pos(0u) as c_uint) }; - let mut bcx = fcx.entry_bcx.get().unwrap(); + let bcx = fcx.entry_bcx.get().unwrap(); let arg = BitCast(bcx, arg, llptrty); let ret = adt::trans_get_discr(bcx, repr, arg, Some(Type::i64())); Store(bcx, ret, fcx.llretptr.get().unwrap()); match fcx.llreturn.get() { - Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn), - None => bcx = cleanup_block(bcx, Some(bcx.llbb)) + Some(llreturn) => Br(bcx, llreturn), + None => {} }; finish_fn(&fcx, bcx); llfdecl @@ -389,7 +389,8 @@ pub fn emit_calls_to_trait_visit_ty<'a>( visitor_val: ValueRef, visitor_trait_id: DefId) -> &'a Block<'a> { - let final = sub_block(bcx, "final"); + let fcx = bcx.fcx; + let final = fcx.new_temp_block("final"); let tydesc_ty = ty::get_tydesc_ty(bcx.ccx().tcx).unwrap(); let tydesc_ty = type_of(bcx.ccx(), tydesc_ty); let mut r = Reflector { diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index 100f28af97dad..c119bed189df1 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -17,6 +17,8 @@ use middle::trans::base::*; use middle::trans::base; use middle::trans::build::*; use middle::trans::callee; +use middle::trans::cleanup; +use middle::trans::cleanup::CleanupMethods; use middle::trans::common::*; use middle::trans::datum::*; use middle::trans::expr::{Dest, Ignore, SaveIn}; @@ -26,7 +28,6 @@ use middle::trans::machine::{llsize_of, nonzero_llsize_of, llsize_of_alloc}; use middle::trans::type_::Type; use middle::trans::type_of; use middle::ty; -use util::common::indenter; use util::ppaux::ty_to_str; use syntax::ast; @@ -193,7 +194,6 @@ pub fn trans_fixed_vstore<'a>( debug!("trans_fixed_vstore(vstore_expr={}, dest={:?})", bcx.expr_to_str(vstore_expr), dest.to_str(bcx.ccx())); - let _indenter = indenter(); let vt = vec_types_from_expr(bcx, vstore_expr); @@ -214,17 +214,18 @@ pub fn trans_slice_vstore<'a>( content_expr: &ast::Expr, dest: expr::Dest) -> &'a Block<'a> { - //! - // - // &[...] allocates memory on the stack and writes the values into it, - // returning a slice (pair of ptr, len). &"..." is similar except that - // the memory can be statically allocated. - - let ccx = bcx.ccx(); + /*! + * &[...] allocates memory on the stack and writes the values into it, + * returning a slice (pair of ptr, len). &"..." is similar except that + * the memory can be statically allocated. + */ + + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let mut bcx = bcx; debug!("trans_slice_vstore(vstore_expr={}, dest={})", bcx.expr_to_str(vstore_expr), dest.to_str(ccx)); - let _indenter = indenter(); // Handle the &"..." case: match content_expr.node { @@ -244,21 +245,29 @@ pub fn trans_slice_vstore<'a>( let count = elements_required(bcx, content_expr); debug!("vt={}, count={:?}", vt.to_str(ccx), count); - // Make a fixed-length backing array and allocate it on the stack. let llcount = C_uint(ccx, count); - let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); - - // Arrange for the backing array to be cleaned up. - let fixed_ty = ty::mk_vec(bcx.tcx(), - ty::mt {ty: vt.unit_ty, mutbl: ast::MutMutable}, - ty::vstore_fixed(count)); - let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to(); - let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty); - add_clean(bcx, llfixed_casted, fixed_ty); - - // Generate the content into the backing array. - let bcx = write_content(bcx, &vt, vstore_expr, + let llfixed; + if count == 0 { + // Zero-length array: just use NULL as the data pointer + llfixed = C_null(vt.llunit_ty.ptr_to()); + } else { + // Make a fixed-length backing array and allocate it on the stack. + llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); + + // Arrange for the backing array to be cleaned up. + let fixed_ty = ty::mk_vec(bcx.tcx(), + ty::mt {ty: vt.unit_ty, + mutbl: ast::MutMutable}, + ty::vstore_fixed(count)); + let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to(); + let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty); + let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id); + fcx.schedule_drop_mem(cleanup_scope, llfixed_casted, fixed_ty); + + // Generate the content into the backing array. + bcx = write_content(bcx, &vt, vstore_expr, content_expr, SaveIn(llfixed)); + } // Finally, create the slice pair itself. match dest { @@ -278,16 +287,15 @@ pub fn trans_lit_str<'a>( str_lit: @str, dest: Dest) -> &'a Block<'a> { - //! - // - // Literal strings translate to slices into static memory. This is - // different from trans_slice_vstore() above because it does need to copy - // the content anywhere. + /*! + * Literal strings translate to slices into static memory. This is + * different from trans_slice_vstore() above because it does need to copy + * the content anywhere. + */ debug!("trans_lit_str(lit_expr={}, dest={})", bcx.expr_to_str(lit_expr), dest.to_str(bcx.ccx())); - let _indenter = indenter(); match dest { Ignore => bcx, @@ -308,20 +316,19 @@ pub fn trans_lit_str<'a>( } -pub fn trans_uniq_or_managed_vstore<'a>( - bcx: &'a Block<'a>, - heap: heap, - vstore_expr: &ast::Expr, - content_expr: &ast::Expr) - -> DatumBlock<'a> { - //! - // - // @[...] or ~[...] (also @"..." or ~"...") allocate boxes in the - // appropriate heap and write the array elements into them. +pub fn trans_uniq_or_managed_vstore<'a>(bcx: &'a Block<'a>, + heap: heap, + vstore_expr: &ast::Expr, + content_expr: &ast::Expr) + -> DatumBlock<'a, Expr> { + /*! + * @[...] or ~[...] (also @"..." or ~"...") allocate boxes in the + * appropriate heap and write the array elements into them. + */ debug!("trans_uniq_or_managed_vstore(vstore_expr={}, heap={:?})", bcx.expr_to_str(vstore_expr), heap); - let _indenter = indenter(); + let fcx = bcx.fcx; // Handle ~"". match heap { @@ -334,7 +341,7 @@ pub fn trans_uniq_or_managed_vstore<'a>( let llptrval = PointerCast(bcx, llptrval, Type::i8p()); let llsizeval = C_uint(bcx.ccx(), s.len()); let typ = ty::mk_str(bcx.tcx(), ty::vstore_uniq); - let lldestval = scratch_datum(bcx, typ, "", false); + let lldestval = rvalue_scratch_datum(bcx, typ, ""); let alloc_fn = langcall(bcx, Some(lit.span), "", @@ -343,11 +350,8 @@ pub fn trans_uniq_or_managed_vstore<'a>( bcx, alloc_fn, [ llptrval, llsizeval ], - Some(expr::SaveIn(lldestval.to_ref_llval(bcx)))).bcx; - return DatumBlock { - bcx: bcx, - datum: lldestval - }; + Some(expr::SaveIn(lldestval.val))).bcx; + return DatumBlock(bcx, lldestval).to_expr_datumblock(); } _ => {} } @@ -364,7 +368,11 @@ pub fn trans_uniq_or_managed_vstore<'a>( let Result {bcx, val} = alloc_vec(bcx, vt.unit_ty, count, heap); - add_clean_free(bcx, val, heap); + // Create a temporary scope lest execution should fail while + // constructing the vector. + let temp_scope = fcx.push_custom_cleanup_scope(); + fcx.schedule_free_value(cleanup::CustomScope(temp_scope), val, heap); + let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val, vt.vec_ty)); debug!("alloc_vec() returned val={}, dataptr={}", @@ -373,9 +381,9 @@ pub fn trans_uniq_or_managed_vstore<'a>( let bcx = write_content(bcx, &vt, vstore_expr, content_expr, SaveIn(dataptr)); - revoke_clean(bcx, val); + fcx.pop_custom_cleanup_scope(temp_scope); - return immediate_rvalue_bcx(bcx, val, vt.vec_ty); + return immediate_rvalue_bcx(bcx, val, vt.vec_ty).to_expr_datumblock(); } pub fn write_content<'a>( @@ -386,13 +394,13 @@ pub fn write_content<'a>( dest: Dest) -> &'a Block<'a> { let _icx = push_ctxt("tvec::write_content"); + let fcx = bcx.fcx; let mut bcx = bcx; debug!("write_content(vt={}, dest={}, vstore_expr={:?})", vt.to_str(bcx.ccx()), dest.to_str(bcx.ccx()), bcx.expr_to_str(vstore_expr)); - let _indenter = indenter(); match content_expr.node { ast::ExprLit(lit) => { @@ -430,19 +438,19 @@ pub fn write_content<'a>( } SaveIn(lldest) => { - let mut temp_cleanups = ~[]; + let temp_scope = fcx.push_custom_cleanup_scope(); for (i, element) in elements.iter().enumerate() { let lleltptr = GEPi(bcx, lldest, [i]); debug!("writing index {:?} with lleltptr={:?}", i, bcx.val_to_str(lleltptr)); bcx = expr::trans_into(bcx, *element, SaveIn(lleltptr)); - add_clean_temp_mem(bcx, lleltptr, vt.unit_ty); - temp_cleanups.push(lleltptr); - } - for cleanup in temp_cleanups.iter() { - revoke_clean(bcx, *cleanup); + fcx.schedule_drop_mem( + cleanup::CustomScope(temp_scope), + lleltptr, + vt.unit_ty); } + fcx.pop_custom_cleanup_scope(temp_scope); } } return bcx; @@ -463,14 +471,16 @@ pub fn write_content<'a>( // this can only happen as a result of OOM. So we just skip out on the // cleanup since things would *probably* be broken at that point anyways. - let elem = unpack_datum!(bcx, { - expr::trans_to_datum(bcx, element) - }); + let elem = unpack_datum!(bcx, expr::trans(bcx, element)); + assert!(!ty::type_moves_by_default(bcx.tcx(), elem.ty)); - iter_vec_loop(bcx, lldest, vt, + let bcx = iter_vec_loop(bcx, lldest, vt, C_uint(bcx.ccx(), count), |set_bcx, lleltptr, _| { - elem.copy_to(set_bcx, INIT, lleltptr) - }) + elem.shallow_copy_and_take(set_bcx, lleltptr) + }); + + elem.add_clean_if_rvalue(bcx, element.id); + bcx } } } @@ -522,15 +532,16 @@ pub fn elements_required(bcx: &Block, content_expr: &ast::Expr) -> uint { } } -pub fn get_base_and_byte_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t) +pub fn get_base_and_byte_len(bcx: &Block, + llval: ValueRef, + vec_ty: ty::t) -> (ValueRef, ValueRef) { - //! - // - // Converts a vector into the slice pair. The vector should be stored in - // `llval` which should be either immediate or by-ref as appropriate for - // the vector type. If you have a datum, you would probably prefer to - // call `Datum::get_base_and_byte_len()` which will handle any conversions for - // you. + /*! + * Converts a vector into the slice pair. The vector should be + * stored in `llval` which should be by ref. If you have a datum, + * you would probably prefer to call + * `Datum::get_base_and_byte_len()`. + */ let ccx = bcx.ccx(); let vt = vec_types(bcx, vec_ty); @@ -542,32 +553,38 @@ pub fn get_base_and_byte_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t) match vstore { ty::vstore_fixed(n) => { + assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty)); let base = GEPi(bcx, llval, [0u, 0u]); let len = Mul(bcx, C_uint(ccx, n), vt.llunit_size); (base, len) } ty::vstore_slice(_) => { + assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty)); let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base])); let count = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len])); let len = Mul(bcx, count, vt.llunit_size); (base, len) } ty::vstore_uniq | ty::vstore_box => { + assert!(type_is_immediate(bcx.ccx(), vt.vec_ty)); + let llval = Load(bcx, llval); let body = get_bodyptr(bcx, llval, vec_ty); (get_dataptr(bcx, body), get_fill(bcx, body)) } } } -pub fn get_base_and_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t) +pub fn get_base_and_len(bcx: &Block, + llval: ValueRef, + vec_ty: ty::t) -> (ValueRef, ValueRef) { - //! - // - // Converts a vector into the slice pair. The vector should be stored in - // `llval` which should be either immediate or by-ref as appropriate for - // the vector type. If you have a datum, you would probably prefer to - // call `Datum::get_base_and_len()` which will handle any conversions for - // you. + /*! + * Converts a vector into the slice pair. The vector should be + * stored in `llval` which should be by-reference. If you have a + * datum, you would probably prefer to call + * `Datum::get_base_and_len()` which will handle any conversions + * for you. + */ let ccx = bcx.ccx(); let vt = vec_types(bcx, vec_ty); @@ -579,15 +596,19 @@ pub fn get_base_and_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t) match vstore { ty::vstore_fixed(n) => { + assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty)); let base = GEPi(bcx, llval, [0u, 0u]); (base, C_uint(ccx, n)) } ty::vstore_slice(_) => { + assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty)); let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base])); let count = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len])); (base, count) } ty::vstore_uniq | ty::vstore_box => { + assert!(type_is_immediate(bcx.ccx(), vt.vec_ty)); + let llval = Load(bcx, llval); let body = get_bodyptr(bcx, llval, vec_ty); (get_dataptr(bcx, body), UDiv(bcx, get_fill(bcx, body), vt.llunit_size)) } @@ -606,12 +627,13 @@ pub fn iter_vec_loop<'r, f: iter_vec_block<'r,'b>) -> &'b Block<'b> { let _icx = push_ctxt("tvec::iter_vec_loop"); + let fcx = bcx.fcx; - let next_bcx = sub_block(bcx, "iter_vec_loop: while next"); - let loop_bcx = loop_scope_block(bcx, next_bcx, None, "iter_vec_loop", None); - let cond_bcx = scope_block(loop_bcx, None, "iter_vec_loop: loop cond"); - let body_bcx = scope_block(loop_bcx, None, "iter_vec_loop: body: main"); - let inc_bcx = scope_block(loop_bcx, None, "iter_vec_loop: loop inc"); + let next_bcx = fcx.new_temp_block("expr_repeat: while next"); + let loop_bcx = fcx.new_temp_block("expr_repeat"); + let cond_bcx = fcx.new_temp_block("expr_repeat: loop cond"); + let body_bcx = fcx.new_temp_block("expr_repeat: body: set"); + let inc_bcx = fcx.new_temp_block("expr_repeat: body: inc"); Br(bcx, loop_bcx.llbb); let loop_counter = { @@ -663,6 +685,7 @@ pub fn iter_vec_raw<'r, f: iter_vec_block<'r,'b>) -> &'b Block<'b> { let _icx = push_ctxt("tvec::iter_vec_raw"); + let fcx = bcx.fcx; let vt = vec_types(bcx, vec_ty); if (vt.llunit_alloc_size == 0) { @@ -676,14 +699,14 @@ pub fn iter_vec_raw<'r, let data_end_ptr = pointer_add_byte(bcx, data_ptr, fill); // Now perform the iteration. - let header_bcx = base::sub_block(bcx, "iter_vec_loop_header"); + let header_bcx = fcx.new_temp_block("iter_vec_loop_header"); Br(bcx, header_bcx.llbb); let data_ptr = Phi(header_bcx, val_ty(data_ptr), [data_ptr], [bcx.llbb]); let not_yet_at_end = ICmp(header_bcx, lib::llvm::IntULT, data_ptr, data_end_ptr); - let body_bcx = base::sub_block(header_bcx, "iter_vec_loop_body"); - let next_bcx = base::sub_block(header_bcx, "iter_vec_next"); + let body_bcx = fcx.new_temp_block("iter_vec_loop_body"); + let next_bcx = fcx.new_temp_block("iter_vec_next"); CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb); let body_bcx = f(body_bcx, data_ptr, vt.unit_ty); AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr, @@ -691,7 +714,6 @@ pub fn iter_vec_raw<'r, body_bcx.llbb); Br(body_bcx, header_bcx.llbb); next_bcx - } } diff --git a/src/librustc/middle/trans/write_guard.rs b/src/librustc/middle/trans/write_guard.rs index 0b6a18dd7e169..5b310feb58db5 100644 --- a/src/librustc/middle/trans/write_guard.rs +++ b/src/librustc/middle/trans/write_guard.rs @@ -16,19 +16,17 @@ use middle::borrowck::{RootInfo, root_map_key}; -use middle::trans::base::*; +use middle::trans::cleanup; use middle::trans::common::*; use middle::trans::datum::*; use syntax::codemap::Span; use syntax::ast; -pub fn root_and_write_guard<'a>( - datum: &Datum, - bcx: &'a Block<'a>, - span: Span, - expr_id: ast::NodeId, - derefs: uint) - -> &'a Block<'a> { +pub fn root_and_write_guard<'a, K:KindOps>(datum: &Datum, + bcx: &'a Block<'a>, + span: Span, + expr_id: ast::NodeId, + derefs: uint) -> &'a Block<'a> { let key = root_map_key { id: expr_id, derefs: derefs }; debug!("write_guard::root_and_write_guard(key={:?})", key); @@ -43,13 +41,11 @@ pub fn root_and_write_guard<'a>( } } -fn root<'a>( - datum: &Datum, - bcx: &'a Block<'a>, - _: Span, - root_key: root_map_key, - root_info: RootInfo) - -> &'a Block<'a> { +fn root<'a, K:KindOps>(datum: &Datum, + bcx: &'a Block<'a>, + _span: Span, + root_key: root_map_key, + root_info: RootInfo) -> &'a Block<'a> { //! In some cases, borrowck will decide that an @T/@[]/@str //! value must be rooted for the program to be safe. In that //! case, we will call this function, which will stash a copy @@ -58,17 +54,12 @@ fn root<'a>( debug!("write_guard::root(root_key={:?}, root_info={:?}, datum={:?})", root_key, root_info, datum.to_str(bcx.ccx())); - // First, root the datum. Note that we must zero this value, + // Root the datum. Note that we must zero this value, // because sometimes we root on one path but not another. // See e.g. #4904. - let scratch = scratch_datum(bcx, datum.ty, "__write_guard", true); - datum.copy_to_datum(bcx, INIT, scratch); - let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope); - add_clean_temp_mem_in_scope(cleanup_bcx, - root_info.scope, - scratch.val, - scratch.ty); - - bcx + lvalue_scratch_datum( + bcx, datum.ty, "__write_guard", true, + cleanup::AstScope(root_info.scope), (), + |(), bcx, llval| datum.shallow_copy_and_take(bcx, llval)).bcx } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 1f643cfc80cc3..99cdbacbd5bed 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3983,8 +3983,31 @@ pub fn ast_expr_vstore_to_vstore(fcx: @FnCtxt, ast::ExprVstoreUniq => ty::vstore_uniq, ast::ExprVstoreBox => ty::vstore_box, ast::ExprVstoreSlice | ast::ExprVstoreMutSlice => { - let r = fcx.infcx().next_region_var(infer::AddrOfSlice(e.span)); - ty::vstore_slice(r) + match e.node { + ast::ExprLit(..) | + ast::ExprVec([], _) => { + // string literals and *empty slices* live in static memory + ty::vstore_slice(ty::ReStatic) + } + ast::ExprRepeat(..) | + ast::ExprVec(..) => { + // vector literals are temporaries on the stack + match fcx.tcx().region_maps.temporary_scope(e.id) { + Some(scope) => { + let r = ty::ReScope(scope); + ty::vstore_slice(r) + } + None => { + // this slice occurs in a static somewhere + ty::vstore_slice(ty::ReStatic) + } + } + } + _ => { + fcx.ccx.tcx.sess.span_bug( + e.span, format!("vstore with unexpected contents")) + } + } } } } @@ -4103,7 +4126,7 @@ pub fn check_intrinsic_type(ccx: @CrateCtxt, it: &ast::ForeignItem) { "uninit" => (1u, ~[], param(ccx, 0u)), "forget" => (1u, ~[ param(ccx, 0) ], ty::mk_nil()), "transmute" => (2, ~[ param(ccx, 0) ], param(ccx, 1)), - "move_val" | "move_val_init" => { + "move_val_init" => { (1u, ~[ ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), param(ccx, 0)), diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index ad0482cf4f42e..490a9cb985321 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -55,21 +55,26 @@ pub struct Rcx { repeating_scope: ast::NodeId, } -fn encl_region_of_def(fcx: @FnCtxt, def: ast::Def) -> ty::Region { +fn region_of_def(fcx: @FnCtxt, def: ast::Def) -> ty::Region { + /*! + * Returns the validity region of `def` -- that is, how long + * is `def` valid? + */ + let tcx = fcx.tcx(); match def { DefLocal(node_id, _) | DefArg(node_id, _) | DefSelf(node_id, _) | DefBinding(node_id, _) => { - tcx.region_maps.encl_region(node_id) + tcx.region_maps.var_region(node_id) } DefUpvar(_, subdef, closure_id, body_id) => { match ty::ty_closure_sigil(fcx.node_ty(closure_id)) { - BorrowedSigil => encl_region_of_def(fcx, *subdef), + BorrowedSigil => region_of_def(fcx, *subdef), ManagedSigil | OwnedSigil => ReScope(body_id) } } _ => { - tcx.sess.bug(format!("unexpected def in encl_region_of_def: {:?}", + tcx.sess.bug(format!("unexpected def in region_of_def: {:?}", def)) } } @@ -193,7 +198,6 @@ fn visit_item(_rcx: &mut Rcx, _item: &ast::Item) { } fn visit_block(rcx: &mut Rcx, b: &ast::Block) { - rcx.fcx.tcx().region_maps.record_cleanup_scope(b.id); visit::walk_block(rcx, b, ()); } @@ -239,9 +243,9 @@ fn constrain_bindings_in_pat(pat: &ast::Pat, rcx: &mut Rcx) { // that the lifetime of any regions that appear in a // variable's type enclose at least the variable's scope. - let encl_region = tcx.region_maps.encl_region(id); + let var_region = tcx.region_maps.var_region(id); constrain_regions_in_type_of_node( - rcx, id, encl_region, + rcx, id, var_region, infer::BindingTypeIsNotValidAtDecl(span)); }) } @@ -255,55 +259,6 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { method_map.get().contains_key(&expr.id) }; - // Record cleanup scopes, which are used by borrowck to decide the - // maximum lifetime of a temporary rvalue. These were derived by - // examining where trans creates block scopes, not because this - // reflects some principled decision around temporary lifetimes. - // Ordinarily this would seem like something that should be setup - // in region, but we need to know which uses of operators are - // overloaded. See #3511. - let tcx = rcx.fcx.tcx(); - match expr.node { - // You'd think that x += y where `+=` is overloaded would be a - // cleanup scope. You'd be... kind of right. In fact the - // handling of `+=` and friends in trans for overloaded - // operators is a hopeless mess and I can't figure out how to - // represent it. - ndm - // - // ast::expr_assign_op(..) | - - ast::ExprIndex(..) | - ast::ExprBinary(..) | - ast::ExprUnary(..) if has_method_map => { - tcx.region_maps.record_cleanup_scope(expr.id); - } - ast::ExprBinary(_, ast::BiAnd, lhs, rhs) | - ast::ExprBinary(_, ast::BiOr, lhs, rhs) => { - tcx.region_maps.record_cleanup_scope(lhs.id); - tcx.region_maps.record_cleanup_scope(rhs.id); - } - ast::ExprCall(..) | - ast::ExprMethodCall(..) => { - tcx.region_maps.record_cleanup_scope(expr.id); - } - ast::ExprMatch(_, ref arms) => { - tcx.region_maps.record_cleanup_scope(expr.id); - for arm in arms.iter() { - for guard in arm.guard.iter() { - tcx.region_maps.record_cleanup_scope(guard.id); - } - } - } - ast::ExprLoop(ref body, _) => { - tcx.region_maps.record_cleanup_scope(body.id); - } - ast::ExprWhile(cond, ref body) => { - tcx.region_maps.record_cleanup_scope(cond.id); - tcx.region_maps.record_cleanup_scope(body.id); - } - _ => {} - } - // Check any autoderefs or autorefs that appear. { let adjustments = rcx.fcx.inh.adjustments.borrow(); @@ -701,10 +656,10 @@ fn constrain_free_variables(rcx: &mut Rcx, for freevar in get_freevars(tcx, expr.id).iter() { debug!("freevar def is {:?}", freevar.def); let def = freevar.def; - let en_region = encl_region_of_def(rcx.fcx, def); - debug!("en_region = {}", en_region.repr(tcx)); + let def_region = region_of_def(rcx.fcx, def); + debug!("def_region = {}", def_region.repr(tcx)); rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span), - region, en_region); + region, def_region); } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 213b4ebf257ec..b9916749fe9a1 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -673,6 +673,14 @@ impl Repr for ast::Item { } } +impl Repr for ast::Stmt { + fn repr(&self, tcx: ctxt) -> ~str { + format!("stmt({}: {})", + ast_util::stmt_id(self), + pprust::stmt_to_str(self, tcx.sess.intr())) + } +} + impl Repr for ast::Pat { fn repr(&self, tcx: ctxt) -> ~str { format!("pat({}: {})", diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 74f94ba00f562..b9a36137db29a 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -276,7 +276,8 @@ pub fn run(mut crate: clean::Crate, dst: Path) { write!(w, "var allPaths = \\{"); for (i, (&id, &(ref fqp, short))) in cache.paths.iter().enumerate() { if i > 0 { write!(w, ","); } - write!(w, "'{}':\\{type:'{}',name:'{}'\\}", id, short, *fqp.last()); + write!(w, "'{}':\\{type:'{}',name:'{}'\\}", + id, short, *fqp.last()); } write!(w, "\\};"); w.flush(); diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index c52ff2d088d13..546f555038756 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -521,7 +521,8 @@ mod tests { #[test] fn test_ascii_vec() { - assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59])); + let test = &[40u8, 32u8, 59u8]; + assert_eq!(test.to_ascii(), v2ascii!([40, 32, 59])); assert_eq!("( ;".to_ascii(), v2ascii!([40, 32, 59])); // FIXME: #5475 borrowchk error, owned vectors do not live long enough // if chained-from directly @@ -587,14 +588,18 @@ mod tests { assert_eq!("zoä华".to_ascii_opt(), None); - assert_eq!((&[127u8, 128u8, 255u8]).to_ascii_opt(), None); + let test1 = &[127u8, 128u8, 255u8]; + assert_eq!((test1).to_ascii_opt(), None); let v = [40u8, 32u8, 59u8]; - assert_eq!(v.to_ascii_opt(), Some(v2ascii!(&[40, 32, 59]))); + let v2 = v2ascii!(&[40, 32, 59]); + assert_eq!(v.to_ascii_opt(), Some(v2)); let v = [127u8, 128u8, 255u8]; assert_eq!(v.to_ascii_opt(), None); - assert_eq!("( ;".to_ascii_opt(), Some(v2ascii!(&[40, 32, 59]))); + let v = "( ;"; + let v2 = v2ascii!(&[40, 32, 59]); + assert_eq!(v.to_ascii_opt(), Some(v2)); assert_eq!("zoä华".to_ascii_opt(), None); assert_eq!((~[40u8, 32u8, 59u8]).into_ascii_opt(), Some(v2ascii!(~[40, 32, 59]))); diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index f036131d211a6..a6caa1bfc2952 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -406,7 +406,8 @@ mod test { #[test] fn test_read_char() { - let mut r = BufReader::new(bytes!("Việt")); + let b = bytes!("Việt"); + let mut r = BufReader::new(b); assert_eq!(r.read_char(), Some('V')); assert_eq!(r.read_char(), Some('i')); assert_eq!(r.read_char(), Some('ệ')); @@ -416,7 +417,8 @@ mod test { #[test] fn test_read_bad_char() { - let mut r = BufReader::new(bytes!(0x80)); + let b = bytes!(0x80); + let mut r = BufReader::new(b); assert_eq!(r.read_char(), None); } diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index a8b7e8e00ead0..4c8a640a849c1 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -181,7 +181,7 @@ mod tests { let io = ~[]; let args = ProcessConfig { program: "/bin/sh", - args: [~"-c", ~"true"], + args: &[~"-c", ~"true"], env: None, cwd: None, io: io, @@ -198,7 +198,7 @@ mod tests { let io = ~[]; let args = ProcessConfig { program: "if-this-is-a-binary-then-the-world-has-ended", - args: [], + args: &[], env: None, cwd: None, io: io, @@ -215,7 +215,7 @@ mod tests { let io = ~[]; let args = ProcessConfig { program: "/bin/sh", - args: [~"-c", ~"exit 1"], + args: &[~"-c", ~"exit 1"], env: None, cwd: None, io: io, @@ -231,7 +231,7 @@ mod tests { let io = ~[]; let args = ProcessConfig { program: "/bin/sh", - args: [~"-c", ~"kill -1 $$"], + args: &[~"-c", ~"kill -1 $$"], env: None, cwd: None, io: io, @@ -274,7 +274,7 @@ mod tests { let io = ~[Ignored, CreatePipe(false, true)]; let args = ProcessConfig { program: "/bin/sh", - args: [~"-c", ~"echo foobar"], + args: &[~"-c", ~"echo foobar"], env: None, cwd: None, io: io, @@ -289,7 +289,7 @@ mod tests { let cwd = Some("/"); let args = ProcessConfig { program: "/bin/sh", - args: [~"-c", ~"pwd"], + args: &[~"-c", ~"pwd"], env: None, cwd: cwd, io: io, @@ -304,7 +304,7 @@ mod tests { CreatePipe(false, true)]; let args = ProcessConfig { program: "/bin/sh", - args: [~"-c", ~"read line; echo $line"], + args: &[~"-c", ~"read line; echo $line"], env: None, cwd: None, io: io, diff --git a/src/libstd/option.rs b/src/libstd/option.rs index bdec67e5d9f96..aab98f19e1513 100644 --- a/src/libstd/option.rs +++ b/src/libstd/option.rs @@ -48,6 +48,7 @@ use kinds::Send; use str::OwnedStr; use to_str::ToStr; use util; +use vec; /// The option type #[deriving(Clone, DeepClone, Eq, Ord, TotalEq, TotalOrd, ToStr)] @@ -98,6 +99,24 @@ impl Option { match *self { Some(ref mut x) => Some(x), None => None } } + /// Convert from `Option` to `&[T]` (without copying) + #[inline] + pub fn as_slice<'r>(&'r self) -> &'r [T] { + match *self { + Some(ref x) => vec::ref_slice(x), + None => &[] + } + } + + /// Convert from `Option` to `&[T]` (without copying) + #[inline] + pub fn as_mut_slice<'r>(&'r mut self) -> &'r mut [T] { + match *self { + Some(ref mut x) => vec::mut_ref_slice(x), + None => &mut [] + } + } + ///////////////////////////////////////////////////////////////////////// // Getting to contained values ///////////////////////////////////////////////////////////////////////// diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 7b94de6c094d6..e2ddabc1714b6 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -237,7 +237,10 @@ impl GenericPath for Path { let mut ita = self.components(); let mut itb = other.components(); if bytes!(".") == self.repr { - return itb.next() != Some(bytes!("..")); + return match itb.next() { + None => true, + Some(b) => b != bytes!("..") + }; } loop { match (ita.next(), itb.next()) { @@ -463,7 +466,10 @@ mod tests { macro_rules! b( ($($arg:expr),+) => ( - bytes!($($arg),+) + { + static the_bytes: &'static [u8] = bytes!($($arg),+); + the_bytes + } ) ) @@ -689,7 +695,8 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::new($path); + let arg = $path; + let path = Path::new(arg); assert_eq!(path.$op(), $exp); } ); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 09b00be7e9d61..cf2163265e43f 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1074,7 +1074,10 @@ mod tests { macro_rules! b( ($($arg:expr),+) => ( - bytes!($($arg),+) + { + static the_bytes: &'static [u8] = bytes!($($arg),+); + the_bytes + } ) ) @@ -1372,20 +1375,23 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::new($path); + let path = $path; + let path = Path::new(path); assert_eq!(path.$op(), Some($exp)); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( { - let path = Path::new($path); + let path = $path; + let path = Path::new(path); let left = path.$op(); assert_eq!(left, $exp); } ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::new($path); + let path = $path; + let path = Path::new(path); assert_eq!(path.$op(), $exp); } ) diff --git a/src/libstd/rt/crate_map.rs b/src/libstd/rt/crate_map.rs index 16c1ad25448fc..6ea12659e77f2 100644 --- a/src/libstd/rt/crate_map.rs +++ b/src/libstd/rt/crate_map.rs @@ -130,14 +130,14 @@ mod tests { let child_crate = CrateMap { version: 2, entries: entries, - children: [], + children: &[], event_loop_factory: None, }; let root_crate = CrateMap { version: 2, - entries: [], - children: [&child_crate, &child_crate], + entries: &[], + children: &[&child_crate, &child_crate], event_loop_factory: None, }; @@ -157,29 +157,29 @@ mod tests { let mut level3: u32 = 3; let child_crate2 = CrateMap { version: 2, - entries: [ + entries: &[ ModEntry { name: "c::m1", log_level: &mut level2}, ModEntry { name: "c::m2", log_level: &mut level3}, ], - children: [], + children: &[], event_loop_factory: None, }; let child_crate1 = CrateMap { version: 2, - entries: [ + entries: &[ ModEntry { name: "t::f1", log_level: &mut 1}, ], - children: [&child_crate2], + children: &[&child_crate2], event_loop_factory: None, }; let root_crate = CrateMap { version: 2, - entries: [ + entries: &[ ModEntry { name: "t::f2", log_level: &mut 0}, ], - children: [&child_crate1], + children: &[&child_crate1], event_loop_factory: None, }; diff --git a/src/libstd/unstable/intrinsics.rs b/src/libstd/unstable/intrinsics.rs index 18a1790cd9b61..63c0d628ce2e3 100644 --- a/src/libstd/unstable/intrinsics.rs +++ b/src/libstd/unstable/intrinsics.rs @@ -329,12 +329,6 @@ extern "rust-intrinsic" { /// elements. pub fn size_of() -> uint; - /// Move a value to a memory location containing a value. - /// - /// Drop glue is run on the destination, which must contain a - /// valid Rust value. - pub fn move_val(dst: &mut T, src: T); - /// Move a value to an uninitialized memory location. /// /// Drop glue is not run on the destination. diff --git a/src/libstd/util.rs b/src/libstd/util.rs index 06c7923bfeda1..22b07d6548f22 100644 --- a/src/libstd/util.rs +++ b/src/libstd/util.rs @@ -134,7 +134,7 @@ mod tests { } } -/// Completely miscellaneous language-construct benchmarks. +/// Completely miscellaneous language-constracuct benchmarks. #[cfg(test)] mod bench { diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 797582e57f4ee..fd7640de12694 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -168,7 +168,7 @@ pub fn from_elem(n_elts: uint, t: T) -> ~[T] { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); let mut i = 0u; - (|| { + (|| { // FIXME what if we fail in the middle of this loop? while i < n_elts { intrinsics::move_val_init(&mut(*ptr::mut_offset(p, i as int)), t.clone()); i += 1u; @@ -239,6 +239,25 @@ pub fn build(size: Option, builder: |push: |v: A||) -> ~[A] { vec } +/** + * Converts a pointer to A into a slice of length 1 (without copying). + */ +pub fn ref_slice<'a, A>(s: &'a A) -> &'a [A] { + unsafe { + cast::transmute(Slice { data: s, len: 1 }) + } +} + +/** + * Converts a pointer to A into a slice of length 1 (without copying). + */ +pub fn mut_ref_slice<'a, A>(s: &'a mut A) -> &'a mut [A] { + unsafe { + let ptr: *A = cast::transmute(s); + cast::transmute(Slice { data: ptr, len: 1 }) + } +} + /// An iterator over the slices of a vector separated by elements that /// match a predicate function. pub struct SplitIterator<'a, T> { @@ -2175,6 +2194,9 @@ pub trait MutableVector<'a, T> { /// Returns an iterator that allows modifying each value fn mut_iter(self) -> VecMutIterator<'a, T>; + /// Returns a mutable pointer to the last item in the vector. + fn mut_last(self) -> &'a mut T; + /// Returns a reversed iterator that allows modifying each value fn mut_rev_iter(self) -> MutRevIterator<'a, T>; @@ -2437,6 +2459,13 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] { } } + #[inline] + fn mut_last(self) -> &'a mut T { + let len = self.len(); + if len == 0 { fail!("mut_last: empty vector") } + &mut self[len - 1] + } + #[inline] fn mut_rev_iter(self) -> MutRevIterator<'a, T> { self.mut_iter().invert() diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 3e0caab65c240..8fb18eecd6e0e 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -567,11 +567,7 @@ pub fn visit_ids_for_inlined_item(item: &InlinedItem, visited_outermost: false, }; - match *item { - IIItem(i) => id_visitor.visit_item(i, ()), - IIForeign(i) => id_visitor.visit_foreign_item(i, ()), - IIMethod(_, _, m) => visit::walk_method_helper(&mut id_visitor, m, ()), - } + visit::walk_inlined_item(&mut id_visitor, item, ()); } struct IdRangeComputingVisitor { diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index a4b8dd7840327..3ad5857395d57 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -634,17 +634,24 @@ impl<'a> Context<'a> { self.ecx.expr_ident(e.span, lname))); } + // Now create a vector containing all the arguments + let slicename = self.ecx.ident_of("__args_vec"); + { + let args = names.move_iter().map(|a| a.unwrap()); + let mut args = locals.move_iter().chain(args); + let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect()); + lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args)); + } + // Now create the fmt::Arguments struct with all our locals we created. - let args = names.move_iter().map(|a| a.unwrap()); - let mut args = locals.move_iter().chain(args); let fmt = self.ecx.expr_ident(self.fmtsp, static_name); - let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect()); + let args_slice = self.ecx.expr_ident(self.fmtsp, slicename); let result = self.ecx.expr_call_global(self.fmtsp, ~[ self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), self.ecx.ident_of("Arguments"), self.ecx.ident_of("new"), - ], ~[fmt, args]); + ], ~[fmt, args_slice]); // We did all the work of making sure that the arguments // structure is safe, so we can safely have an unsafe block. diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs index 247962e0b9424..326f712d5b212 100644 --- a/src/libsyntax/opt_vec.rs +++ b/src/libsyntax/opt_vec.rs @@ -42,12 +42,31 @@ impl OptVec { v.push(t); return; } - Empty => {} + Empty => { + *self = Vec(~[t]); + } } + } + + pub fn pop(&mut self) -> T { + match *self { + Vec(ref mut v) => v.pop(), + Empty => fail!("pop from empty opt_vec") + } + } - // FIXME(#5074): flow insensitive means we can't move - // assignment inside `match` - *self = Vec(~[t]); + pub fn last<'a>(&'a self) -> &'a T { + match *self { + Vec(ref v) => v.last(), + Empty => fail!("last on empty opt_vec") + } + } + + pub fn mut_last<'a>(&'a mut self) -> &'a mut T { + match *self { + Vec(ref mut v) => v.mut_last(), + Empty => fail!("mut_last on empty opt_vec") + } } pub fn map(&self, op: |&T| -> U) -> OptVec { @@ -82,6 +101,16 @@ impl OptVec { } } + pub fn swap_remove(&mut self, index: uint) { + match *self { + Empty => { fail!("Index out of bounds"); } + Vec(ref mut v) => { + assert!(index < v.len()); + v.swap_remove(index); + } + } + } + #[inline] pub fn iter<'r>(&'r self) -> OptVecIterator<'r, T> { match *self { @@ -166,6 +195,16 @@ impl<'a, T> Iterator<&'a T> for OptVecIterator<'a, T> { } } +impl<'a, T> DoubleEndedIterator<&'a T> for OptVecIterator<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + match self.iter { + Some(ref mut x) => x.next_back(), + None => None + } + } +} + impl FromIterator for OptVec { fn from_iterator>(iterator: &mut T) -> OptVec { let mut r = Empty; diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index d79522b710382..484f8dce1f745 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -121,6 +121,17 @@ pub trait Visitor { } } +pub fn walk_inlined_item>(visitor: &mut V, + item: &ast::InlinedItem, + env: E) { + match *item { + IIItem(i) => visitor.visit_item(i, env), + IIForeign(i) => visitor.visit_foreign_item(i, env), + IIMethod(_, _, m) => walk_method_helper(visitor, m, env), + } +} + + pub fn walk_crate>(visitor: &mut V, crate: &Crate, env: E) { visitor.visit_mod(&crate.module, crate.span, CRATE_NODE_ID, env) } diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 1fb05b8938123..280df8cb10f57 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -522,6 +522,17 @@ extern "C" char *LLVMTypeToString(LLVMTypeRef Type) { return strdup(os.str().data()); } +extern "C" char *LLVMValueToString(LLVMValueRef Value) { + std::string s; + llvm::raw_string_ostream os(s); + os << "("; + unwrap(Value)->getType()->print(os); + os << ":"; + unwrap(Value)->print(os); + os << ")"; + return strdup(os.str().data()); +} + extern "C" bool LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { Module *Dst = unwrap(dst); diff --git a/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs new file mode 100644 index 0000000000000..a61884d49c853 --- /dev/null +++ b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs @@ -0,0 +1,31 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that assignments to an `&mut` pointer which is found in a +// borrowed (but otherwise non-aliasable) location is illegal. + +struct S<'a> { + pointer: &'a mut int +} + +fn copy_borrowed_ptr<'a,'b>(p: &'a mut S<'b>) -> S<'b> { + S { pointer: &mut *p.pointer } //~ ERROR lifetime of `p` is too short to guarantee its contents can be safely reborrowed +} + +fn main() { + let mut x = 1; + + { + let mut y = S { pointer: &mut x }; + let z = copy_borrowed_ptr(&mut y); + *y.pointer += 1; + *z.pointer += 1; + } +} diff --git a/src/test/compile-fail/borrowck-rvalues-mutable-bad.rs b/src/test/compile-fail/borrowck-rvalues-mutable-bad.rs deleted file mode 100644 index 10bef907a28a3..0000000000000 --- a/src/test/compile-fail/borrowck-rvalues-mutable-bad.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Tests that rvalue lifetimes is limited to the enclosing trans -// cleanup scope. It is unclear that this is the correct lifetime for -// rvalues, but that's what it is right now. - -struct Counter { - value: uint -} - -impl Counter { - fn new(v: uint) -> Counter { - Counter {value: v} - } - - fn inc<'a>(&'a mut self) -> &'a mut Counter { - self.value += 1; - self - } - - fn get(&self) -> uint { - self.value - } -} - -pub fn main() { - let v = Counter::new(22).inc().inc().get(); - //~^ ERROR borrowed value does not live long enough - assert_eq!(v, 24);; -} diff --git a/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs b/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs new file mode 100644 index 0000000000000..76836a4410312 --- /dev/null +++ b/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs @@ -0,0 +1,45 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the borrow checker prevents pointers to temporaries +// with statement lifetimes from escaping. + +#[feature(macro_rules)]; + +use std::ops::Drop; + +static mut FLAGS: u64 = 0; + +struct Box { f: T } +struct AddFlags { bits: u64 } + +fn AddFlags(bits: u64) -> AddFlags { + AddFlags { bits: bits } +} + +fn arg<'a>(x: &'a AddFlags) -> &'a AddFlags { + x +} + +impl AddFlags { + fn get<'a>(&'a self) -> &'a AddFlags { + self + } +} + +pub fn main() { + let _x = arg(&AddFlags(1)); //~ ERROR value does not live long enough + let _x = AddFlags(1).get(); //~ ERROR value does not live long enough + let _x = &*arg(&AddFlags(1)); //~ ERROR value does not live long enough + let ref _x = *arg(&AddFlags(1)); //~ ERROR value does not live long enough + let &ref _x = arg(&AddFlags(1)); //~ ERROR value does not live long enough + let _x = AddFlags(1).get(); //~ ERROR value does not live long enough + let Box { f: _x } = Box { f: AddFlags(1).get() }; //~ ERROR value does not live long enough +} diff --git a/src/test/run-pass/borrowck-rvalues-mutable.rs b/src/test/run-pass/borrowck-rvalues-mutable.rs index cf5a9341c9dc4..d4de4ef34d397 100644 --- a/src/test/run-pass/borrowck-rvalues-mutable.rs +++ b/src/test/run-pass/borrowck-rvalues-mutable.rs @@ -17,6 +17,15 @@ impl Counter { Counter {value: v} } + fn inc<'a>(&'a mut self) -> &'a mut Counter { + self.value += 1; + self + } + + fn get(&self) -> uint { + self.value + } + fn get_and_inc(&mut self) -> uint { let v = self.value; self.value += 1; @@ -27,4 +36,7 @@ impl Counter { pub fn main() { let v = Counter::new(22).get_and_inc(); assert_eq!(v, 22); + + let v = Counter::new(22).inc().inc().get(); + assert_eq!(v, 24);; } diff --git a/src/test/run-pass/cleanup-arm-conditional.rs b/src/test/run-pass/cleanup-arm-conditional.rs new file mode 100644 index 0000000000000..c87302bc134c1 --- /dev/null +++ b/src/test/run-pass/cleanup-arm-conditional.rs @@ -0,0 +1,43 @@ +// copyright 2013 the rust project developers. see the copyright +// file at the top-level directory of this distribution and at +// http://rust-lang.org/copyright. +// +// licensed under the apache license, version 2.0 or the mit license +// , at your +// option. this file may not be copied, modified, or distributed +// except according to those terms. + +// Test that cleanup scope for temporaries created in a match +// arm is confined to the match arm itself. + +use std::{os, run}; +use std::io::process; + +struct Test { x: int } + +impl Test { + fn get_x(&self) -> Option<~int> { + Some(~self.x) + } +} + +fn do_something(t: &Test) -> int { + + // The cleanup scope for the result of `t.get_x()` should be the + // arm itself and not the match, otherwise we'll (potentially) get + // a crash trying to free an uninitialized stack slot. + + match t { + &Test { x: 2 } if t.get_x().is_some() => { + t.x * 2 + } + _ => { 22 } + } +} + +pub fn main() { + let t = Test { x: 1 }; + do_something(&t); +} + diff --git a/src/test/run-pass/cleanup-rvalue-scopes.rs b/src/test/run-pass/cleanup-rvalue-scopes.rs new file mode 100644 index 0000000000000..937b3f81eafa5 --- /dev/null +++ b/src/test/run-pass/cleanup-rvalue-scopes.rs @@ -0,0 +1,138 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that destructors for rvalue temporaries run either at end of +// statement or end of block, as appropriate given the temporary +// lifetime rules. + +#[feature(macro_rules)]; + +use std::ops::Drop; + +static mut FLAGS: u64 = 0; + +struct Box { f: T } +struct AddFlags { bits: u64 } + +fn AddFlags(bits: u64) -> AddFlags { + AddFlags { bits: bits } +} + +fn arg(exp: u64, _x: &AddFlags) { + check_flags(exp); +} + +fn pass(v: T) -> T { + v +} + +fn check_flags(exp: u64) { + unsafe { + let x = FLAGS; + FLAGS = 0; + println!("flags {}, expected {}", x, exp); + assert_eq!(x, exp); + } +} + +impl AddFlags { + fn check_flags<'a>(&'a self, exp: u64) -> &'a AddFlags { + check_flags(exp); + self + } + + fn bits(&self) -> u64 { + self.bits + } +} + +impl Drop for AddFlags { + fn drop(&mut self) { + unsafe { + FLAGS = FLAGS + self.bits; + } + } +} + +macro_rules! end_of_block( + ($pat:pat, $expr:expr) => ( + { + println!("end_of_block({})", stringify!({let $pat = $expr;})); + + { + // Destructor here does not run until exit from the block, + // because value is assigned to. + let $pat = $expr; + check_flags(0); + } + check_flags(1); + } + ) +) + +macro_rules! end_of_stmt( + ($pat:pat, $expr:expr) => ( + { + println!("end_of_stmt({})", stringify!($expr)); + + { + // Destructor here does not run until exit from the block, + // because value is assigned to. + let $pat = $expr; + check_flags(1); + } + + check_flags(0); + } + ) +) + +pub fn main() { + + // In all these cases, we trip over the rules designed to cover + // the case where we are taking addr of rvalue and storing that + // addr into a stack slot, either via `let ref` or via a `&` in + // the initializer. + + end_of_block!(_x, AddFlags(1)); + end_of_block!(_x, &AddFlags(1)); + end_of_block!(_x, & &AddFlags(1)); + end_of_block!(_x, Box { f: AddFlags(1) }); + end_of_block!(_x, Box { f: &AddFlags(1) }); + end_of_block!(_x, Box { f: &AddFlags(1) }); + end_of_block!(_x, pass(AddFlags(1))); + end_of_block!(ref _x, AddFlags(1)); + end_of_block!(AddFlags { bits: ref _x }, AddFlags(1)); + end_of_block!(&AddFlags { bits }, &AddFlags(1)); + end_of_block!((_, ref _y), (AddFlags(1), 22)); + end_of_block!(~ref _x, ~AddFlags(1)); + end_of_block!(~_x, ~AddFlags(1)); + end_of_block!(_, { { check_flags(0); &AddFlags(1) } }); + end_of_block!(_, &((Box { f: AddFlags(1) }).f)); + end_of_block!(_, &(([AddFlags(1)])[0])); + end_of_block!(_, &((&~[AddFlags(1)])[0])); + + // LHS does not create a ref binding, so temporary lives as long + // as statement, and we do not move the AddFlags out: + end_of_stmt!(_, AddFlags(1)); + end_of_stmt!((_, _), (AddFlags(1), 22)); + + // `&` operator appears inside an arg to a function, + // so it is not prolonged: + end_of_stmt!(ref _x, arg(0, &AddFlags(1))); + + // autoref occurs inside receiver, so temp lifetime is not + // prolonged: + end_of_stmt!(ref _x, AddFlags(1).check_flags(0).bits()); + + // No reference is created on LHS, thus RHS is moved into + // a temporary that lives just as long as the statement. + end_of_stmt!(AddFlags { bits }, AddFlags(1)); +} diff --git a/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs new file mode 100644 index 0000000000000..53a009ecc1359 --- /dev/null +++ b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs @@ -0,0 +1,39 @@ +// Test cleanup of rvalue temporary that occurs while `~` construction +// is in progress. This scenario revealed a rather terrible bug. The +// ingredients are: +// +// 1. Partial cleanup of `~` is in scope, +// 2. cleanup of return value from `get_bar()` is in scope, +// 3. do_it() fails. +// +// This led to a bug because `the top-most frame that was to be +// cleaned (which happens to be the partial cleanup of `~`) required +// multiple basic blocks, which led to us dropping part of the cleanup +// from the top-most frame. +// +// It's unclear how likely such a bug is to recur, but it seems like a +// scenario worth testing. + +use std::task; + +enum Conzabble { + Bickwick(Foo) +} + +struct Foo { field: ~uint } + +fn do_it(x: &[uint]) -> Foo { + fail!() +} + +fn get_bar(x: uint) -> ~[uint] { ~[x * 2] } + +pub fn fails() { + let x = 2; + let mut y = ~[]; + y.push(~Bickwick(do_it(get_bar(x)))); +} + +pub fn main() { + task::try(fails); +} diff --git a/src/test/run-pass/cleanup-shortcircuit.rs b/src/test/run-pass/cleanup-shortcircuit.rs new file mode 100644 index 0000000000000..f6eeb5a37e6a6 --- /dev/null +++ b/src/test/run-pass/cleanup-shortcircuit.rs @@ -0,0 +1,30 @@ +// copyright 2013 the rust project developers. see the copyright +// file at the top-level directory of this distribution and at +// http://rust-lang.org/copyright. +// +// licensed under the apache license, version 2.0 or the mit license +// , at your +// option. this file may not be copied, modified, or distributed +// except according to those terms. + +// Test that cleanups for the RHS of shorcircuiting operators work. + +use std::{os, run}; +use std::io::process; + +pub fn main() { + let args = os::args(); + + // Here, the rvalue `~"signal"` requires cleanup. Older versions + // of the code had a problem that the cleanup scope for this + // expression was the end of the `if`, and as the `~"signal"` + // expression was never evaluated, we wound up trying to clean + // uninitialized memory. + + if args.len() >= 2 && args[1] == ~"signal" { + // Raise a segfault. + unsafe { *(0 as *mut int) = 0; } + } +} + diff --git a/src/test/run-pass/intrinsic-move-val.rs b/src/test/run-pass/intrinsic-move-val.rs index 1d2b0197f08e1..f42d5ff2e5267 100644 --- a/src/test/run-pass/intrinsic-move-val.rs +++ b/src/test/run-pass/intrinsic-move-val.rs @@ -8,20 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[feature(managed_boxes)]; +use std::cast::transmute; mod rusti { extern "rust-intrinsic" { + pub fn init() -> T; pub fn move_val_init(dst: &mut T, src: T); - pub fn move_val(dst: &mut T, src: T); } } pub fn main() { unsafe { - let x = @1; - let mut y = @2; - rusti::move_val(&mut y, x); + let x = ~1; + let mut y = rusti::init(); + let mut z: *uint = transmute(&x); + rusti::move_val_init(&mut y, x); assert_eq!(*y, 1); + assert_eq!(*z, 0); // `x` is nulled out, not directly visible } } diff --git a/src/test/run-pass/issue-10626.rs b/src/test/run-pass/issue-10626.rs index 020db44bb87a6..4d5ed9f701a39 100644 --- a/src/test/run-pass/issue-10626.rs +++ b/src/test/run-pass/issue-10626.rs @@ -30,10 +30,10 @@ pub fn main () { let config = process::ProcessConfig { program : args[0].as_slice(), - args : [~"child"], + args : &[~"child"], env : None, cwd : None, - io : [] + io : &[] }; let mut p = process::Process::new(config).unwrap(); diff --git a/src/test/run-pass/issue-9382.rs b/src/test/run-pass/issue-9382.rs index a5f87e6c5365d..f6bbd8ebef86a 100644 --- a/src/test/run-pass/issue-9382.rs +++ b/src/test/run-pass/issue-9382.rs @@ -28,10 +28,10 @@ struct Thing2<'a> { pub fn main() { let _t1_fixed = Thing1 { - baz: [], + baz: &[], bar: ~32, }; - let _t1_uniq = Thing1 { + Thing1 { baz: ~[], bar: ~32, }; @@ -40,10 +40,10 @@ pub fn main() { bar: ~32, }; let _t2_fixed = Thing2 { - baz: [], + baz: &[], bar: 32, }; - let _t2_uniq = Thing2 { + Thing2 { baz: ~[], bar: 32, }; diff --git a/src/test/run-pass/move-1.rs b/src/test/run-pass/move-1.rs index c3335a0d45570..983c701d82031 100644 --- a/src/test/run-pass/move-1.rs +++ b/src/test/run-pass/move-1.rs @@ -15,6 +15,7 @@ struct Triple { x: int, y: int, z: int } fn test(x: bool, foo: @Triple) -> int { let bar = foo; let mut y: @Triple; + y = bar; if x { y = bar; } else { y = @Triple{x: 4, y: 5, z: 6}; } return y.y; } From e71571a3cda0f7a283bd58a1c74a79c06de27661 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jan 2014 19:35:38 -0500 Subject: [PATCH 02/17] Use as_slice() method on option --- src/librustc/middle/trans/foreign.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index ea9f608ff5671..8711932bdd2f5 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -282,20 +282,20 @@ pub fn trans_native_call<'a>( // FIXME(#8357) We really ought to report a span here ccx.sess.fatal( format!("ABI string `{}` has no suitable ABI \ - for target architecture", - fn_abis.user_string(ccx.tcx))); + for target architecture", + fn_abis.user_string(ccx.tcx))); } }; // A function pointer is called without the declaration available, so we have to apply // any attributes with ABI implications directly to the call instruction. Right now, the // only attribute we need to worry about is `sret`. - let sret_attr = [(1, StructRetAttribute)]; - let attrs = if fn_type.ret_ty.is_indirect() { - sret_attr.as_slice() + let sret_attr = if fn_type.ret_ty.is_indirect() { + Some((1, StructRetAttribute)) } else { - &[] + None }; + let attrs = sret_attr.as_slice(); let llforeign_retval = CallWithConv(bcx, llfn, llargs_foreign, cc, attrs); // If the function we just called does not use an outpointer, From 6badef49fe502ca22aaabd4309bd899eea4144d4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jan 2014 19:44:38 -0500 Subject: [PATCH 03/17] Remove FIXMEs and add license --- src/librustc/middle/region.rs | 4 ++-- src/librustc/middle/trans/_match.rs | 2 -- src/librustc/middle/trans/doc.rs | 10 ++++++++++ src/libstd/vec.rs | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 3f4e5a4ef89c0..a224a90a6a440 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -615,7 +615,7 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, // A, but the inner rvalues `a()` and `b()` have an extended lifetime // due to rule C. // - // FIXME -- Note that `[]` patterns work more smoothly post-DST. + // FIXME(#6308) -- Note that `[]` patterns work more smoothly post-DST. match local.init { Some(expr) => { @@ -778,7 +778,7 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, ast::ExprField(ref subexpr, _, _) | ast::ExprIndex(_, ref subexpr, _) | ast::ExprParen(ref subexpr) => { - let subexpr: &'a @Expr = subexpr; // FIXME + let subexpr: &'a @Expr = subexpr; // FIXME(#11586) expr = &**subexpr; } _ => { diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index c7eb837aaf864..96367c769fdbf 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -2092,8 +2092,6 @@ fn bind_irrefutable_pat<'a>( * - pat: the irrefutable pattern being matched. * - val: the value being matched -- must be an lvalue (by ref, with cleanup) * - binding_mode: is this for an argument or a local variable? - * - * FIXME: convert `val` to `Datum` for more type safety */ debug!("bind_irrefutable_pat(bcx={}, pat={}, binding_mode={:?})", diff --git a/src/librustc/middle/trans/doc.rs b/src/librustc/middle/trans/doc.rs index c781d1dcbbfe5..b44f2ba20086c 100644 --- a/src/librustc/middle/trans/doc.rs +++ b/src/librustc/middle/trans/doc.rs @@ -1,3 +1,13 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + /*! # Documentation for the trans module diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index fd7640de12694..1211882fd5da0 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -168,7 +168,7 @@ pub fn from_elem(n_elts: uint, t: T) -> ~[T] { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); let mut i = 0u; - (|| { // FIXME what if we fail in the middle of this loop? + (|| { while i < n_elts { intrinsics::move_val_init(&mut(*ptr::mut_offset(p, i as int)), t.clone()); i += 1u; From 84f33fb13415b74fc5b29ee4162efb86f8b06e9c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Jan 2014 20:31:20 -0500 Subject: [PATCH 04/17] Cleanup trait callees --- src/librustc/middle/trans/meth.rs | 54 +++++++++++++------------------ 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index b9f4a9077b06e..85b9e16e5cca1 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -440,39 +440,31 @@ pub fn trans_trait_callee<'a>( let _icx = push_ctxt("impl::trans_trait_callee"); let mut bcx = bcx; - // make a local copy for trait if needed - let self_ty = expr_ty_adjusted(bcx, self_expr); - let self_scratch = match ty::get(self_ty).sty { - ty::ty_trait(_, _, ty::RegionTraitStore(..), _, _) => { - unpack_datum!(bcx, expr::trans(bcx, self_expr)) - } - _ => { - // Arrange a temporary cleanup for the object in case something - // should go wrong before the method is actually *invoked*. - let datum = unpack_datum!( - bcx, - lvalue_scratch_datum( - bcx, self_ty, "__trait_callee", false, arg_cleanup_scope, (), - |(), bcx, llval| expr::trans_into(bcx, self_expr, - expr::SaveIn(llval)))); - datum.to_expr_datum() - } - }; + // Translate self_datum and take ownership of the value by + // converting to an rvalue. + let self_datum = unpack_datum!( + bcx, expr::trans(bcx, self_expr)); + let self_datum = unpack_datum!( + bcx, self_datum.to_rvalue_datum(bcx, "trait_callee")); + + // Convert to by-ref since `trans_trait_callee_from_llval` wants it + // that way. + let self_datum = unpack_datum!( + bcx, self_datum.to_ref_datum(bcx)); + + // Arrange cleanup in case something should go wrong before the + // actual call occurs. + let llval = self_datum.add_clean(bcx.fcx, arg_cleanup_scope); let callee_ty = node_id_type(bcx, callee_id); - assert!(self_scratch.kind.is_by_ref()); // FIXME why special case above?? - trans_trait_callee_from_llval(bcx, - callee_ty, - n_method, - self_scratch.val) + trans_trait_callee_from_llval(bcx, callee_ty, n_method, llval) } -pub fn trans_trait_callee_from_llval<'a>( - bcx: &'a Block<'a>, - callee_ty: ty::t, - n_method: uint, - llpair: ValueRef) - -> Callee<'a> { +pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>, + callee_ty: ty::t, + n_method: uint, + llpair: ValueRef) + -> Callee<'a> { /*! * Same as `trans_trait_callee()` above, except that it is given * a by-ref pointer to the object pair. @@ -641,8 +633,8 @@ pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>, /*! * Generates the code to convert from a pointer (`~T`, `&T`, etc) * into an object (`~Trait`, `&Trait`, etc). This means creating a - * pair where the first word is the pointer and the second word is - * an appropriate vtable. + * pair where the first word is the vtable and the second word is + * the pointer. */ let mut bcx = bcx; From 7ff6b094fb5c0c7f58e08a9a7c25ff9ec5bbd643 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Jan 2014 05:56:56 -0500 Subject: [PATCH 05/17] Remove typo --- src/libstd/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/util.rs b/src/libstd/util.rs index 22b07d6548f22..06c7923bfeda1 100644 --- a/src/libstd/util.rs +++ b/src/libstd/util.rs @@ -134,7 +134,7 @@ mod tests { } } -/// Completely miscellaneous language-constracuct benchmarks. +/// Completely miscellaneous language-construct benchmarks. #[cfg(test)] mod bench { From 14b0abfd8204aa7673065800c4b8c2572eb5427f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Jan 2014 15:11:22 -0500 Subject: [PATCH 06/17] Consider all zero-sized data structures to be voidish, bypassing some "quirky" parts of LLVM (see e.g. LLVM bug 9900) but also generating better code --- src/librustc/middle/trans/base.rs | 24 +++++++++++++----------- src/librustc/middle/trans/callee.rs | 10 ++++------ src/librustc/middle/trans/common.rs | 9 +++++++++ src/librustc/middle/trans/datum.rs | 8 ++++---- src/librustc/middle/trans/expr.rs | 5 +++-- src/librustc/middle/trans/foreign.rs | 4 ++-- src/librustc/middle/trans/intrinsic.rs | 2 +- src/librustc/middle/trans/type_of.rs | 2 +- src/librustc/middle/ty.rs | 5 ----- 9 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index ca618250c27f7..f908732ea0ad6 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1320,7 +1320,7 @@ pub fn init_function<'a>( } }; - if !ty::type_is_voidish(fcx.ccx.tcx, substd_output_type) { + if !type_is_voidish(fcx.ccx, substd_output_type) { // If the function returns nil/bot, there is no real return // value, so do not set `llretptr`. if !skip_retptr || fcx.caller_expects_out_pointer { @@ -1539,7 +1539,7 @@ pub fn trans_closure(ccx: @CrateContext, // translation calls that don't have a return value (trans_crate, // trans_mod, trans_item, et cetera) and those that do // (trans_block, trans_expr, et cetera). - if body.expr.is_none() || ty::type_is_voidish(bcx.tcx(), block_ty) { + if body.expr.is_none() || type_is_voidish(bcx.ccx(), block_ty) { bcx = controlflow::trans_block(bcx, body, expr::Ignore); } else { let dest = expr::SaveIn(fcx.llretptr.get().unwrap()); @@ -1679,15 +1679,17 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: @CrateContext, let bcx = fcx.entry_bcx.get().unwrap(); - let repr = adt::represent_type(ccx, result_ty); - adt::trans_start_init(bcx, repr, fcx.llretptr.get().unwrap(), disr); - for (i, arg_datum) in arg_datums.move_iter().enumerate() { - let lldestptr = adt::trans_field_ptr(bcx, - repr, - fcx.llretptr.get().unwrap(), - disr, - i); - arg_datum.store_to(bcx, lldestptr); + if !type_is_voidish(fcx.ccx, result_ty) { + let repr = adt::represent_type(ccx, result_ty); + adt::trans_start_init(bcx, repr, fcx.llretptr.get().unwrap(), disr); + for (i, arg_datum) in arg_datums.move_iter().enumerate() { + let lldestptr = adt::trans_field_ptr(bcx, + repr, + fcx.llretptr.get().unwrap(), + disr, + i); + arg_datum.store_to(bcx, lldestptr); + } } finish_fn(&fcx, bcx); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 310ae95ea2a08..7cc80318f872f 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -623,7 +623,6 @@ pub fn trans_call_inner<'a>( // scope will ever execute. let fcx = bcx.fcx; let ccx = fcx.ccx; - let tcx = ccx.tcx; let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); let callee = get_callee(bcx, cleanup::CustomScope(arg_cleanup_scope)); @@ -668,12 +667,11 @@ pub fn trans_call_inner<'a>( } Some(expr::SaveIn(dst)) => Some(dst), Some(expr::Ignore) => { - if !ty::type_is_voidish(tcx, ret_ty) { + if !type_is_voidish(ccx, ret_ty) { Some(alloc_ty(bcx, ret_ty, "__llret")) } else { - unsafe { - Some(llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())) - } + let llty = type_of::type_of(ccx, ret_ty); + Some(C_undef(llty.ptr_to())) } } }; @@ -738,7 +736,7 @@ pub fn trans_call_inner<'a>( match opt_llretslot { Some(llretslot) => { if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !ty::type_is_voidish(bcx.tcx(), ret_ty) + !type_is_voidish(bcx.ccx(), ret_ty) { Store(bcx, llret, llretslot); } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index cb2987ac16a11..e1e026c3171cc 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -79,6 +79,15 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { } } +pub fn type_is_voidish(ccx: &CrateContext, ty: ty::t) -> bool { + //! Identify types like `()`, bottom, or empty structs, which + //! contain no information at all. + use middle::trans::machine::llsize_of_alloc; + use middle::trans::type_of::sizing_type_of; + let llty = sizing_type_of(ccx, ty); + llsize_of_alloc(ccx, llty) == 0 +} + pub fn gensym_name(name: &str) -> (Ident, PathElem) { let name = token::gensym(name); let ident = Ident::new(name); diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 4b89be64f53be..624ff5f6ba18f 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -171,7 +171,7 @@ pub fn appropriate_rvalue_mode(ccx: &CrateContext, ty: ty::t) -> RvalueMode { * on whether type is immediate or not. */ - if ty::type_is_voidish(ccx.tcx, ty) { + if type_is_voidish(ccx, ty) { ByValue } else if type_is_immediate(ccx, ty) { ByValue @@ -583,8 +583,8 @@ fn load<'a>(bcx: &'a Block<'a>, llptr: ValueRef, ty: ty::t) -> ValueRef { * what we are loading. */ - if ty::type_is_voidish(bcx.tcx(), ty) { - C_nil() + if type_is_voidish(bcx.ccx(), ty) { + C_undef(type_of::type_of(bcx.ccx(), ty)) } else if ty::type_is_bool(ty) { LoadRangeAssert(bcx, llptr, 0, 2, lib::llvm::True) } else { @@ -638,7 +638,7 @@ impl Datum { let _icx = push_ctxt("copy_to_no_check"); - if ty::type_is_voidish(bcx.tcx(), self.ty) { + if type_is_voidish(bcx.ccx(), self.ty) { return bcx; } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 385c0c86ae58f..9ae8280428d63 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -474,7 +474,7 @@ fn trans_unadjusted<'a>(bcx: &'a Block<'a>, ty::RvalueDpsExpr => { let ty = expr_ty(bcx, expr); - if ty::type_is_voidish(bcx.tcx(), ty) { + if type_is_voidish(bcx.ccx(), ty) { bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore); nil(bcx, ty) } else { @@ -500,7 +500,8 @@ fn trans_unadjusted<'a>(bcx: &'a Block<'a>, }; fn nil<'a>(bcx: &'a Block<'a>, ty: ty::t) -> DatumBlock<'a, Expr> { - let datum = immediate_rvalue(C_nil(), ty); + let llval = C_undef(type_of::type_of(bcx.ccx(), ty)); + let datum = immediate_rvalue(llval, ty); DatumBlock(bcx, datum.to_expr_datum()) } } diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 8711932bdd2f5..9ba05352b3416 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -198,7 +198,7 @@ pub fn trans_native_call<'a>( _ => ccx.sess.bug("trans_native_call called on non-function type") }; let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys); - let ret_def = !ty::type_is_voidish(bcx.tcx(), fn_sig.output); + let ret_def = !type_is_voidish(bcx.ccx(), fn_sig.output); let fn_type = cabi::compute_abi_info(ccx, llsig.llarg_tys, llsig.llret_ty, @@ -778,7 +778,7 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext, _ => ccx.sess.bug("foreign_types_for_fn_ty called on non-function type") }; let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs); - let ret_def = !ty::type_is_voidish(ccx.tcx, fn_sig.output); + let ret_def = !type_is_voidish(ccx, fn_sig.output); let fn_ty = cabi::compute_abi_info(ccx, llsig.llarg_tys, llsig.llret_ty, diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 81ee292c835ed..c59b2f6eb64f2 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -356,7 +356,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, pluralize(out_type_size))); } - if !ty::type_is_voidish(ccx.tcx, out_type) { + if !type_is_voidish(ccx, out_type) { let llsrcval = get_param(decl, first_real_arg); if type_is_immediate(ccx, in_type) { match fcx.llretptr.get() { diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index ae96f43b07d36..3aab6d5161911 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -68,7 +68,7 @@ pub fn type_of_rust_fn(cx: &CrateContext, atys.push_all(type_of_explicit_args(cx, inputs)); // Use the output as the actual return value if it's immediate. - if !use_out_pointer && !ty::type_is_voidish(cx.tcx, output) { + if !use_out_pointer && !type_is_voidish(cx, output) { Type::func(atys, &lloutputtype) } else { Type::func(atys, &Type::void()) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index fd3bf0deae799..538a3c89bef72 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1455,11 +1455,6 @@ pub fn subst(cx: ctxt, // Type utilities -pub fn type_is_voidish(tcx: ctxt, ty: t) -> bool { - //! "nil" and "bot" are void types in that they represent 0 bits of information - type_is_nil(ty) || type_is_bot(ty) || type_is_empty(tcx, ty) -} - pub fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil } pub fn type_is_bot(ty: t) -> bool { From 4b52d899fffd8e6ef01a5a05c1d513278a7823bd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Jan 2014 16:18:46 -0500 Subject: [PATCH 07/17] Further refine treatment of voidish arrays --- src/librustc/middle/trans/common.rs | 4 ++-- src/librustc/middle/trans/tvec.rs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index e1e026c3171cc..90083541e4267 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -64,7 +64,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { let tcx = ccx.tcx; let simple = ty::type_is_scalar(ty) || ty::type_is_boxed(ty) || ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || - type_is_newtype_immediate(ccx, ty) || + type_is_newtype_immediate(ccx, ty) || ty::type_is_bot(ty) || ty::type_is_simd(tcx, ty); if simple { return true; @@ -75,7 +75,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { let llty = sizing_type_of(ccx, ty); llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type) } - _ => false + _ => type_is_voidish(ccx, ty) } } diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index c119bed189df1..1642d333a9a60 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -553,7 +553,6 @@ pub fn get_base_and_byte_len(bcx: &Block, match vstore { ty::vstore_fixed(n) => { - assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty)); let base = GEPi(bcx, llval, [0u, 0u]); let len = Mul(bcx, C_uint(ccx, n), vt.llunit_size); (base, len) @@ -596,7 +595,6 @@ pub fn get_base_and_len(bcx: &Block, match vstore { ty::vstore_fixed(n) => { - assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty)); let base = GEPi(bcx, llval, [0u, 0u]); (base, C_uint(ccx, n)) } From fd318300cf713f0e74f3a834f6c07970b85f17e4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Jan 2014 18:47:22 -0500 Subject: [PATCH 08/17] Fix test to account for new temporary lifetime rules, which cause the channel to be dropped prematurely. --- src/libstd/comm/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index bf9e28f3e97fa..985a387ee2baa 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -1202,7 +1202,7 @@ mod test { }) test!(fn oneshot_single_thread_peek_open() { - let (port, _) = Chan::::new(); + let (port, _chan) = Chan::::new(); assert_eq!(port.try_recv(), Empty); }) From 76c90283ce9de476357abf9a5fed79e86a2f1f53 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Jan 2014 18:47:42 -0500 Subject: [PATCH 09/17] Fix uninit() intrinsic when used with empty types --- src/librustc/middle/trans/intrinsic.rs | 2 +- src/test/run-pass/uninit-empty-types.rs | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/uninit-empty-types.rs diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index c59b2f6eb64f2..b15377386465d 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -317,7 +317,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, "uninit" => { // Do nothing, this is effectively a no-op let retty = substs.tys[0]; - if type_is_immediate(ccx, retty) && !ty::type_is_nil(retty) { + if type_is_immediate(ccx, retty) && !type_is_voidish(ccx, retty) { unsafe { Ret(bcx, lib::llvm::llvm::LLVMGetUndef(type_of(ccx, retty).to_ref())); } diff --git a/src/test/run-pass/uninit-empty-types.rs b/src/test/run-pass/uninit-empty-types.rs new file mode 100644 index 0000000000000..622f0f8ab00c9 --- /dev/null +++ b/src/test/run-pass/uninit-empty-types.rs @@ -0,0 +1,24 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test the uninit() construct returning various empty types. + +use std::vec; +use std::unstable::intrinsics; + +#[deriving(Clone)] +struct Foo; + +fn main() { + unsafe { + let _x: Foo = intrinsics::uninit(); + let _x: [Foo, ..2] = intrinsics::uninit(); + } +} From 5e7657fafb62b81eb68cb63b2a42e7b77ab9fce6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Jan 2014 19:10:17 -0500 Subject: [PATCH 10/17] Distinguish zero-size types from those that we return as void --- src/librustc/middle/trans/base.rs | 6 +++--- src/librustc/middle/trans/callee.rs | 4 ++-- src/librustc/middle/trans/common.rs | 22 ++++++++++++++++++---- src/librustc/middle/trans/datum.rs | 6 +++--- src/librustc/middle/trans/expr.rs | 2 +- src/librustc/middle/trans/foreign.rs | 4 ++-- src/librustc/middle/trans/intrinsic.rs | 4 ++-- src/librustc/middle/trans/type_of.rs | 6 +++--- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f908732ea0ad6..ef40629946df0 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1320,7 +1320,7 @@ pub fn init_function<'a>( } }; - if !type_is_voidish(fcx.ccx, substd_output_type) { + if !return_type_is_void(fcx.ccx, substd_output_type) { // If the function returns nil/bot, there is no real return // value, so do not set `llretptr`. if !skip_retptr || fcx.caller_expects_out_pointer { @@ -1539,7 +1539,7 @@ pub fn trans_closure(ccx: @CrateContext, // translation calls that don't have a return value (trans_crate, // trans_mod, trans_item, et cetera) and those that do // (trans_block, trans_expr, et cetera). - if body.expr.is_none() || type_is_voidish(bcx.ccx(), block_ty) { + if body.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) { bcx = controlflow::trans_block(bcx, body, expr::Ignore); } else { let dest = expr::SaveIn(fcx.llretptr.get().unwrap()); @@ -1679,7 +1679,7 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: @CrateContext, let bcx = fcx.entry_bcx.get().unwrap(); - if !type_is_voidish(fcx.ccx, result_ty) { + if !type_is_zero_size(fcx.ccx, result_ty) { let repr = adt::represent_type(ccx, result_ty); adt::trans_start_init(bcx, repr, fcx.llretptr.get().unwrap(), disr); for (i, arg_datum) in arg_datums.move_iter().enumerate() { diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 7cc80318f872f..27903e74e07a5 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -667,7 +667,7 @@ pub fn trans_call_inner<'a>( } Some(expr::SaveIn(dst)) => Some(dst), Some(expr::Ignore) => { - if !type_is_voidish(ccx, ret_ty) { + if !type_is_zero_size(ccx, ret_ty) { Some(alloc_ty(bcx, ret_ty, "__llret")) } else { let llty = type_of::type_of(ccx, ret_ty); @@ -736,7 +736,7 @@ pub fn trans_call_inner<'a>( match opt_llretslot { Some(llretslot) => { if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !type_is_voidish(bcx.ccx(), ret_ty) + !type_is_zero_size(bcx.ccx(), ret_ty) { Store(bcx, llret, llretslot); } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 90083541e4267..e466e4da38d72 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -75,19 +75,33 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { let llty = sizing_type_of(ccx, ty); llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type) } - _ => type_is_voidish(ccx, ty) + _ => type_is_zero_size(ccx, ty) } } -pub fn type_is_voidish(ccx: &CrateContext, ty: ty::t) -> bool { - //! Identify types like `()`, bottom, or empty structs, which - //! contain no information at all. +pub fn type_is_zero_size(ccx: &CrateContext, ty: ty::t) -> bool { + /*! + * Identify types which have size zero at runtime. + */ + use middle::trans::machine::llsize_of_alloc; use middle::trans::type_of::sizing_type_of; let llty = sizing_type_of(ccx, ty); llsize_of_alloc(ccx, llty) == 0 } +pub fn return_type_is_void(ccx: &CrateContext, ty: ty::t) -> bool { + /*! + * Identifies types which we declare to be equivalent to `void` + * in C for the purpose of function return types. These are + * `()`, bot, and uninhabited enums. Note that all such types + * are also zero-size, but not all zero-size types use a `void` + * return type (in order to aid with C ABI compatibility). + */ + + ty::type_is_nil(ty) || ty::type_is_bot(ty) || ty::type_is_empty(ccx.tcx, ty) +} + pub fn gensym_name(name: &str) -> (Ident, PathElem) { let name = token::gensym(name); let ident = Ident::new(name); diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 624ff5f6ba18f..467501449b8d5 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -171,7 +171,7 @@ pub fn appropriate_rvalue_mode(ccx: &CrateContext, ty: ty::t) -> RvalueMode { * on whether type is immediate or not. */ - if type_is_voidish(ccx, ty) { + if type_is_zero_size(ccx, ty) { ByValue } else if type_is_immediate(ccx, ty) { ByValue @@ -583,7 +583,7 @@ fn load<'a>(bcx: &'a Block<'a>, llptr: ValueRef, ty: ty::t) -> ValueRef { * what we are loading. */ - if type_is_voidish(bcx.ccx(), ty) { + if type_is_zero_size(bcx.ccx(), ty) { C_undef(type_of::type_of(bcx.ccx(), ty)) } else if ty::type_is_bool(ty) { LoadRangeAssert(bcx, llptr, 0, 2, lib::llvm::True) @@ -638,7 +638,7 @@ impl Datum { let _icx = push_ctxt("copy_to_no_check"); - if type_is_voidish(bcx.ccx(), self.ty) { + if type_is_zero_size(bcx.ccx(), self.ty) { return bcx; } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 9ae8280428d63..36fc927b64c45 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -474,7 +474,7 @@ fn trans_unadjusted<'a>(bcx: &'a Block<'a>, ty::RvalueDpsExpr => { let ty = expr_ty(bcx, expr); - if type_is_voidish(bcx.ccx(), ty) { + if type_is_zero_size(bcx.ccx(), ty) { bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore); nil(bcx, ty) } else { diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 9ba05352b3416..083a1c6988d83 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -198,7 +198,7 @@ pub fn trans_native_call<'a>( _ => ccx.sess.bug("trans_native_call called on non-function type") }; let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys); - let ret_def = !type_is_voidish(bcx.ccx(), fn_sig.output); + let ret_def = !return_type_is_void(bcx.ccx(), fn_sig.output); let fn_type = cabi::compute_abi_info(ccx, llsig.llarg_tys, llsig.llret_ty, @@ -778,7 +778,7 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext, _ => ccx.sess.bug("foreign_types_for_fn_ty called on non-function type") }; let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs); - let ret_def = !type_is_voidish(ccx, fn_sig.output); + let ret_def = !return_type_is_void(ccx, fn_sig.output); let fn_ty = cabi::compute_abi_info(ccx, llsig.llarg_tys, llsig.llret_ty, diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index b15377386465d..1fc749bb6876f 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -317,7 +317,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, "uninit" => { // Do nothing, this is effectively a no-op let retty = substs.tys[0]; - if type_is_immediate(ccx, retty) && !type_is_voidish(ccx, retty) { + if type_is_immediate(ccx, retty) && !return_type_is_void(ccx, retty) { unsafe { Ret(bcx, lib::llvm::llvm::LLVMGetUndef(type_of(ccx, retty).to_ref())); } @@ -356,7 +356,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, pluralize(out_type_size))); } - if !type_is_voidish(ccx, out_type) { + if !return_type_is_void(ccx, out_type) { let llsrcval = get_param(decl, first_real_arg); if type_is_immediate(ccx, in_type) { match fcx.llretptr.get() { diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 3aab6d5161911..4db89fbeccec7 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -68,10 +68,10 @@ pub fn type_of_rust_fn(cx: &CrateContext, atys.push_all(type_of_explicit_args(cx, inputs)); // Use the output as the actual return value if it's immediate. - if !use_out_pointer && !type_is_voidish(cx, output) { - Type::func(atys, &lloutputtype) - } else { + if use_out_pointer || return_type_is_void(cx, output) { Type::func(atys, &Type::void()) + } else { + Type::func(atys, &lloutputtype) } } From 0c916c58e8c978554415933773f0a105dee754d3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 01:02:16 -0500 Subject: [PATCH 11/17] Make main() public in uninit-empty-types --- src/test/run-pass/uninit-empty-types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/uninit-empty-types.rs b/src/test/run-pass/uninit-empty-types.rs index 622f0f8ab00c9..c8b14d8fbc464 100644 --- a/src/test/run-pass/uninit-empty-types.rs +++ b/src/test/run-pass/uninit-empty-types.rs @@ -16,7 +16,7 @@ use std::unstable::intrinsics; #[deriving(Clone)] struct Foo; -fn main() { +pub fn main() { unsafe { let _x: Foo = intrinsics::uninit(); let _x: [Foo, ..2] = intrinsics::uninit(); From 56f4d1831a65b9739d38c2754f65b641b4e0d6b8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 08:03:43 -0500 Subject: [PATCH 12/17] Link lifetimes in `let` patterns just as we do for `match` patterns --- .../middle/borrowck/gather_loans/lifetime.rs | 3 +++ src/librustc/middle/typeck/check/regionck.rs | 25 +++++++++++++++++++ .../borrowck-borrow-from-temporary.rs | 22 ++++++++++++++++ src/test/debug-info/destructured-local.rs | 6 ++--- .../run-pass/regions-dependent-let-ref.rs | 19 ++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/borrowck-borrow-from-temporary.rs create mode 100644 src/test/run-pass/regions-dependent-let-ref.rs diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index ea222839ffe16..9a4869fb6d1d0 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -67,6 +67,9 @@ impl<'a> GuaranteeLifetimeContext<'a> { fn check(&self, cmt: mc::cmt, discr_scope: Option) -> R { //! Main routine. Walks down `cmt` until we find the "guarantor". + debug!("guarantee_lifetime.check(cmt={}, loan_region={})", + cmt.repr(self.bccx.tcx), + self.loan_region.repr(self.bccx.tcx)); match cmt.cat { mc::cat_rvalue(..) | diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 490a9cb985321..c6e43bf968e9b 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -213,6 +213,7 @@ fn visit_arm(rcx: &mut Rcx, arm: &ast::Arm) { fn visit_local(rcx: &mut Rcx, l: &ast::Local) { // see above constrain_bindings_in_pat(l.pat, rcx); + guarantor::for_local(rcx, l); visit::walk_local(rcx, l, ()); } @@ -828,6 +829,30 @@ pub mod guarantor { } } + pub fn for_local(rcx: &mut Rcx, local: &ast::Local) { + /*! + * Link the lifetimes of any ref bindings in a let + * pattern to the lifetimes in the initializer. + * + * For example, given something like this: + * + * let &Foo(ref x) = ...; + * + * this would ensure that the lifetime 'a of the + * region pointer being matched must be >= the lifetime + * of the ref binding. + */ + + debug!("regionck::for_match()"); + let init_expr = match local.init { + None => { return; } + Some(e) => e + }; + let init_guarantor = guarantor(rcx, init_expr); + debug!("init_guarantor={}", init_guarantor.repr(rcx.tcx())); + link_ref_bindings_in_pat(rcx, local.pat, init_guarantor); + } + pub fn for_autoref(rcx: &mut Rcx, expr: &ast::Expr, autoderefs: uint, diff --git a/src/test/compile-fail/borrowck-borrow-from-temporary.rs b/src/test/compile-fail/borrowck-borrow-from-temporary.rs new file mode 100644 index 0000000000000..fa84b91e43df1 --- /dev/null +++ b/src/test/compile-fail/borrowck-borrow-from-temporary.rs @@ -0,0 +1,22 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test lifetimes are linked properly when we take reference +// to interior. + +struct Foo(int); + +fn foo() -> &int { + let &Foo(ref x) = &Foo(3); //~ ERROR borrowed value does not live long enough + x +} + +pub fn main() { +} diff --git a/src/test/debug-info/destructured-local.rs b/src/test/debug-info/destructured-local.rs index bbe453594bc96..0d415a85172b6 100644 --- a/src/test/debug-info/destructured-local.rs +++ b/src/test/debug-info/destructured-local.rs @@ -196,13 +196,13 @@ fn main() { let Unit(ii) = Unit(51); // univariant enum with ref binding - let Unit(ref jj) = Unit(52); + let &Unit(ref jj) = &Unit(52); // tuple struct - let TupleStruct(kk, ll) = TupleStruct(53.0, 54); + let &TupleStruct(kk, ll) = &TupleStruct(53.0, 54); // tuple struct with ref binding - let TupleStruct(mm, ref nn) = TupleStruct(55.0, 56); + let &TupleStruct(mm, ref nn) = &TupleStruct(55.0, 56); zzz(); } diff --git a/src/test/run-pass/regions-dependent-let-ref.rs b/src/test/run-pass/regions-dependent-let-ref.rs new file mode 100644 index 0000000000000..e065146c7a398 --- /dev/null +++ b/src/test/run-pass/regions-dependent-let-ref.rs @@ -0,0 +1,19 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test lifetimes are linked properly when we take reference +// to interior. + +struct Foo(int); +pub fn main() { + // Here the lifetime of the `&` should be at least the + // block, since a ref binding is created to the interior. + let &Foo(ref _x) = &Foo(3); +} From 8f16356e5fb1a86bf2239a6bd51c3dda11155d1e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 08:10:42 -0500 Subject: [PATCH 13/17] Extend temporary lifetimes if there is a ref in an enum binding too. Previously I had omitted this case since function calls don't get the same treatment on the RHS, but it's different on the pattern and is more consistent -- the goal is to identify `let` statements where `ref` bindings create interior pointers. --- src/librustc/middle/region.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index a224a90a6a440..c202684c7abff 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -637,6 +637,7 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, * * P& = ref X * | StructName { ..., P&, ... } + * | VariantName(..., P&, ...) * | [ ..., P&, ... ] * | ( ..., P&, ... ) * | ~P& @@ -656,6 +657,7 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, pats3.iter().any(|&p| is_binding_pat(p)) } + ast::PatEnum(_, Some(ref subpats)) | ast::PatTup(ref subpats) => { subpats.iter().any(|&p| is_binding_pat(p)) } From b1da8c618fb49d8a42270454ea1bf5ddca426683 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 08:30:06 -0500 Subject: [PATCH 14/17] Change expansion of `for` loop to use a `match` statement so that the "innermost enclosing statement" used for rvalue temporaries matches up with user expectations --- src/libsyntax/ext/expand.rs | 30 ++++---- src/test/run-pass/cleanup-rvalue-for-scope.rs | 70 +++++++++++++++++++ 2 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 src/test/run-pass/cleanup-rvalue-for-scope.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 303277afbe84f..6ef938030ff0d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -137,15 +137,16 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { // to: // - // { - // let _i = &mut ; - // [':] loop { - // match i.next() { + // match &mut { + // i => { + // [':] loop { + // match i.next() { // None => break, // Some() => + // } // } + // } // } - // } let local_ident = token::gensym_ident("i"); let next_ident = fld.cx.ident_of("next"); @@ -154,10 +155,6 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { let local_path = fld.cx.path_ident(span, local_ident); let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some")); - // `let i = &mut ` - let iter_decl_stmt = fld.cx.stmt_let(span, false, local_ident, - fld.cx.expr_mut_addr_of(span, src_expr)); - // `None => break ['];` let none_arm = { // FIXME #6993: this map goes away: @@ -185,16 +182,13 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { ast::ExprLoop(fld.cx.block_expr(match_expr), opt_ident)); - // `{ let ... ; loop { ... } }` - let block = fld.cx.block(span, - ~[iter_decl_stmt], - Some(loop_expr)); + // `i => loop { ... }` - @ast::Expr { - id: ast::DUMMY_NODE_ID, - node: ast::ExprBlock(block), - span: span, - } + // `match &mut { i => loop { ... } }` + let discrim = fld.cx.expr_mut_addr_of(span, src_expr); + let i_pattern = fld.cx.pat_ident(span, local_ident); + let arm = fld.cx.arm(span, ~[i_pattern], loop_expr); + fld.cx.expr_match(span, discrim, ~[arm]) } _ => noop_fold_expr(e, fld) diff --git a/src/test/run-pass/cleanup-rvalue-for-scope.rs b/src/test/run-pass/cleanup-rvalue-for-scope.rs new file mode 100644 index 0000000000000..98a0847909857 --- /dev/null +++ b/src/test/run-pass/cleanup-rvalue-for-scope.rs @@ -0,0 +1,70 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the lifetime of rvalues in for loops is extended +// to the for loop itself. + +#[feature(macro_rules)]; + +use std::ops::Drop; + +static mut FLAGS: u64 = 0; + +struct Box { f: T } +struct AddFlags { bits: u64 } + +fn AddFlags(bits: u64) -> AddFlags { + AddFlags { bits: bits } +} + +fn arg(exp: u64, _x: &AddFlags) { + check_flags(exp); +} + +fn pass(v: T) -> T { + v +} + +fn check_flags(exp: u64) { + unsafe { + let x = FLAGS; + FLAGS = 0; + println!("flags {}, expected {}", x, exp); + assert_eq!(x, exp); + } +} + +impl AddFlags { + fn check_flags<'a>(&'a self, exp: u64) -> &'a AddFlags { + check_flags(exp); + self + } + + fn bits(&self) -> u64 { + self.bits + } +} + +impl Drop for AddFlags { + fn drop(&mut self) { + unsafe { + FLAGS = FLAGS + self.bits; + } + } +} + +pub fn main() { + // The array containing [AddFlags] should not be dropped until + // after the for loop: + for x in [AddFlags(1)].iter() { + check_flags(0); + } + check_flags(1); +} From 578b9d1d97dc3f3e65d03b619b653492b1c0baa0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 10:13:53 -0500 Subject: [PATCH 15/17] Update year on license header --- src/test/run-pass/cleanup-rvalue-for-scope.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/cleanup-rvalue-for-scope.rs b/src/test/run-pass/cleanup-rvalue-for-scope.rs index 98a0847909857..68441a2d89462 100644 --- a/src/test/run-pass/cleanup-rvalue-for-scope.rs +++ b/src/test/run-pass/cleanup-rvalue-for-scope.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // From 483ae32189a1af673b6fb6d2c99b949564651d18 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 10:18:39 -0500 Subject: [PATCH 16/17] Update years on more license headers --- src/librustc/middle/trans/cleanup.rs | 2 +- src/test/compile-fail/borrowck-borrow-from-temporary.rs | 2 +- .../compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs | 2 +- src/test/compile-fail/cleanup-rvalue-scopes-cf.rs | 2 +- src/test/run-pass/cleanup-arm-conditional.rs | 2 +- src/test/run-pass/cleanup-rvalue-scopes.rs | 2 +- src/test/run-pass/cleanup-shortcircuit.rs | 2 +- src/test/run-pass/regions-dependent-let-ref.rs | 2 +- src/test/run-pass/uninit-empty-types.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs index 2ecc84ebc0c02..6fed396a78c2a 100644 --- a/src/librustc/middle/trans/cleanup.rs +++ b/src/librustc/middle/trans/cleanup.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/borrowck-borrow-from-temporary.rs b/src/test/compile-fail/borrowck-borrow-from-temporary.rs index fa84b91e43df1..a2f5e28de3bdf 100644 --- a/src/test/compile-fail/borrowck-borrow-from-temporary.rs +++ b/src/test/compile-fail/borrowck-borrow-from-temporary.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // diff --git a/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs index a61884d49c853..85ddfd9424c29 100644 --- a/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs +++ b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // diff --git a/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs b/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs index 76836a4410312..47b7b51b8f73c 100644 --- a/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs +++ b/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // diff --git a/src/test/run-pass/cleanup-arm-conditional.rs b/src/test/run-pass/cleanup-arm-conditional.rs index c87302bc134c1..afe1489ce237b 100644 --- a/src/test/run-pass/cleanup-arm-conditional.rs +++ b/src/test/run-pass/cleanup-arm-conditional.rs @@ -1,4 +1,4 @@ -// copyright 2013 the rust project developers. see the copyright +// 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. // diff --git a/src/test/run-pass/cleanup-rvalue-scopes.rs b/src/test/run-pass/cleanup-rvalue-scopes.rs index 937b3f81eafa5..8a2dd3748915b 100644 --- a/src/test/run-pass/cleanup-rvalue-scopes.rs +++ b/src/test/run-pass/cleanup-rvalue-scopes.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // diff --git a/src/test/run-pass/cleanup-shortcircuit.rs b/src/test/run-pass/cleanup-shortcircuit.rs index f6eeb5a37e6a6..982a0d92c4c4b 100644 --- a/src/test/run-pass/cleanup-shortcircuit.rs +++ b/src/test/run-pass/cleanup-shortcircuit.rs @@ -1,4 +1,4 @@ -// copyright 2013 the rust project developers. see the copyright +// 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. // diff --git a/src/test/run-pass/regions-dependent-let-ref.rs b/src/test/run-pass/regions-dependent-let-ref.rs index e065146c7a398..980fcfb2e9eb9 100644 --- a/src/test/run-pass/regions-dependent-let-ref.rs +++ b/src/test/run-pass/regions-dependent-let-ref.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // diff --git a/src/test/run-pass/uninit-empty-types.rs b/src/test/run-pass/uninit-empty-types.rs index c8b14d8fbc464..4a5c859d4a722 100644 --- a/src/test/run-pass/uninit-empty-types.rs +++ b/src/test/run-pass/uninit-empty-types.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// 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. // From b520c2f28002db0e4120797d823380914871bac4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jan 2014 10:47:29 -0500 Subject: [PATCH 17/17] Adjust comments in test case --- src/test/run-pass/cleanup-rvalue-scopes.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/cleanup-rvalue-scopes.rs b/src/test/run-pass/cleanup-rvalue-scopes.rs index 8a2dd3748915b..7ed59ec74b4d9 100644 --- a/src/test/run-pass/cleanup-rvalue-scopes.rs +++ b/src/test/run-pass/cleanup-rvalue-scopes.rs @@ -67,8 +67,7 @@ macro_rules! end_of_block( println!("end_of_block({})", stringify!({let $pat = $expr;})); { - // Destructor here does not run until exit from the block, - // because value is assigned to. + // Destructor here does not run until exit from the block. let $pat = $expr; check_flags(0); } @@ -83,8 +82,8 @@ macro_rules! end_of_stmt( println!("end_of_stmt({})", stringify!($expr)); { - // Destructor here does not run until exit from the block, - // because value is assigned to. + // Destructor here run after `let` statement + // terminates. let $pat = $expr; check_flags(1); }