From 5e36a997945ddc3964a1fe937bc5390cc5b526c8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 28 Aug 2012 15:54:45 -0700 Subject: [PATCH] Refactor trans to replace lvalue and friends with Datum. Also: - report illegal move/ref combos whether or not ref comes first - commented out fix for #3387, too restrictive and causes an ICE --- Makefile.in | 3 + src/libcore/util.rs | 4 +- src/libstd/getopts.rs | 24 +- src/libstd/par.rs | 4 +- src/libsyntax/print/pprust.rs | 51 +- src/rt/rust_upcall.cpp | 5 +- src/rustc/lib/llvm.rs | 10 +- src/rustc/middle/borrowck.rs | 86 +- src/rustc/middle/borrowck/check_loans.rs | 12 +- src/rustc/middle/borrowck/gather_loans.rs | 34 +- src/rustc/middle/kind.rs | 2 +- src/rustc/middle/liveness.rs | 22 +- src/rustc/middle/region.rs | 4 + src/rustc/middle/trans/alt.rs | 448 +- src/rustc/middle/trans/base.rs | 4094 ++--------------- src/rustc/middle/trans/block.rs | 0 src/rustc/middle/trans/build.rs | 9 +- src/rustc/middle/trans/callee.rs | 665 +++ src/rustc/middle/trans/closure.rs | 306 +- src/rustc/middle/trans/common.rs | 172 +- src/rustc/middle/trans/consts.rs | 95 +- src/rustc/middle/trans/controlflow.rs | 346 ++ src/rustc/middle/trans/datum.rs | 703 +++ src/rustc/middle/trans/expr.rs | 1371 ++++++ src/rustc/middle/trans/foreign.rs | 398 +- src/rustc/middle/trans/glue.rs | 681 +++ src/rustc/middle/trans/impl.rs | 356 +- src/rustc/middle/trans/inline.rs | 87 + src/rustc/middle/trans/macros.rs | 45 + src/rustc/middle/trans/monomorphize.rs | 276 ++ src/rustc/middle/trans/reachable.rs | 6 +- src/rustc/middle/trans/reflect.rs | 27 +- src/rustc/middle/trans/tvec.rs | 573 ++- src/rustc/middle/trans/type_of.rs | 34 +- src/rustc/middle/trans/type_use.rs | 40 +- src/rustc/middle/trans/uniq.rs | 49 +- src/rustc/middle/ty.rs | 228 +- src/rustc/middle/typeck.rs | 13 +- src/rustc/middle/typeck/check.rs | 30 +- src/rustc/middle/typeck/check/alt.rs | 110 +- src/rustc/middle/typeck/check/regionck.rs | 15 +- .../typeck/infer/region_var_bindings.rs | 28 +- src/rustc/rustc.rc | 7 + src/rustc/util/ppaux.rs | 11 +- src/test/bench/graph500-bfs.rs | 16 +- ...her-can-live-while-the-other-survives-4.rs | 9 + .../compile-fail/borrowck-confuse-region.rs | 4 +- .../borrowck-loan-in-overloaded-op.rs | 15 + .../regions-infer-borrow-scope-within-loop.rs | 2 +- ...autoderef-method-on-trait-monomorphized.rs | 16 + .../run-pass/autoderef-method-on-trait.rs | 12 + .../run-pass/const-fields-and-indexing.rs | 4 + .../run-pass/pipe-presentation-examples.rs | 4 +- 53 files changed, 6456 insertions(+), 5110 deletions(-) create mode 100644 src/rustc/middle/trans/block.rs create mode 100644 src/rustc/middle/trans/callee.rs create mode 100644 src/rustc/middle/trans/controlflow.rs create mode 100644 src/rustc/middle/trans/datum.rs create mode 100644 src/rustc/middle/trans/expr.rs create mode 100644 src/rustc/middle/trans/glue.rs create mode 100644 src/rustc/middle/trans/inline.rs create mode 100644 src/rustc/middle/trans/macros.rs create mode 100644 src/rustc/middle/trans/monomorphize.rs create mode 100644 src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs create mode 100644 src/test/compile-fail/borrowck-loan-in-overloaded-op.rs create mode 100644 src/test/run-pass/autoderef-method-on-trait-monomorphized.rs create mode 100644 src/test/run-pass/autoderef-method-on-trait.rs diff --git a/Makefile.in b/Makefile.in index 608f4a3d3ceb3..75d3bd4d5eb1b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -100,6 +100,9 @@ endif ifdef TIME_LLVM_PASSES CFG_RUSTC_FLAGS += -Z time-llvm-passes endif +ifdef TRACE + CFG_RUSTC_FLAGS += -Z trace +endif # platform-specific auto-configuration include $(CFG_SRC_DIR)mk/platform.mk diff --git a/src/libcore/util.rs b/src/libcore/util.rs index 57590dbfc3e25..a27b8fe86bc68 100644 --- a/src/libcore/util.rs +++ b/src/libcore/util.rs @@ -65,7 +65,9 @@ mod tests { fn identity_crisis() { // Writing a test for the identity function. How did it come to this? let x = ~[(5, false)]; - assert x.eq(id(copy x)); + //FIXME #3387 assert x.eq(id(copy x)); + let y = copy x; + assert x.eq(id(y)); } #[test] fn test_swap() { diff --git a/src/libstd/getopts.rs b/src/libstd/getopts.rs index 49d4e8bc26810..50bad1e748a2f 100644 --- a/src/libstd/getopts.rs +++ b/src/libstd/getopts.rs @@ -800,10 +800,11 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(m) => { - assert (opt_present(m, ~"test")); - assert (opt_str(m, ~"test") == ~"20"); - assert (opt_strs(m, ~"test")[0] == ~"20"); - assert (opt_strs(m, ~"test")[1] == ~"30"); + assert (opt_present(m, ~"test")); + assert (opt_str(m, ~"test") == ~"20"); + let pair = opt_strs(m, ~"test"); + assert (pair[0] == ~"20"); + assert (pair[1] == ~"30"); } _ => fail } @@ -854,8 +855,9 @@ mod tests { Ok(m) => { assert (opt_present(m, ~"t")); assert (opt_str(m, ~"t") == ~"20"); - assert (opt_strs(m, ~"t")[0] == ~"20"); - assert (opt_strs(m, ~"t")[1] == ~"30"); + let pair = opt_strs(m, ~"t"); + assert (pair[0] == ~"20"); + assert (pair[1] == ~"30"); } _ => fail } @@ -903,10 +905,12 @@ mod tests { assert (opt_present(m, ~"flag")); assert (opt_str(m, ~"long") == ~"30"); assert (opt_present(m, ~"f")); - assert (opt_strs(m, ~"m")[0] == ~"40"); - assert (opt_strs(m, ~"m")[1] == ~"50"); - assert (opt_strs(m, ~"n")[0] == ~"-A B"); - assert (opt_strs(m, ~"n")[1] == ~"-60 70"); + let pair = opt_strs(m, ~"m"); + assert (pair[0] == ~"40"); + assert (pair[1] == ~"50"); + let pair = opt_strs(m, ~"n"); + assert (pair[0] == ~"-A B"); + assert (pair[1] == ~"-60 70"); assert (!opt_present(m, ~"notpresent")); } _ => fail diff --git a/src/libstd/par.rs b/src/libstd/par.rs index 97057ed20a56d..1735765a474a8 100644 --- a/src/libstd/par.rs +++ b/src/libstd/par.rs @@ -19,7 +19,7 @@ const min_granularity : uint = 1024u; * like map or alli. */ fn map_slices( - xs: ~[A], + xs: &[A], f: fn() -> fn~(uint, v: &[A]) -> B) -> ~[B] { @@ -104,7 +104,7 @@ fn mapi(xs: ~[A], * inner elements. This is to skirt the need for copy constructors. */ fn mapi_factory( - xs: ~[A], f: fn() -> fn~(uint, A) -> B) -> ~[B] { + xs: &[A], f: fn() -> fn~(uint, A) -> B) -> ~[B] { let slices = map_slices(xs, || { let f = f(); fn~(base: uint, slice : &[A], move f) -> ~[B] { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index dd5fc57f684e2..8b17760c7f2fc 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1169,29 +1169,38 @@ fn print_expr(s: ps, &&expr: @ast::expr) { None => () } word_space(s, ~"=>"); + // Extract the expression from the extra block the parser adds - assert arm.body.node.view_items.is_empty(); - assert arm.body.node.stmts.is_empty(); - assert arm.body.node.rules == ast::default_blk; - match arm.body.node.expr { - Some(expr) => { - match expr.node { - ast::expr_block(blk) => { - // the block will close the pattern's ibox - print_block_unclosed_indent(s, blk, alt_indent_unit); - } - _ => { - end(s); // close the ibox for the pattern - print_expr(s, expr); - } - } - if !expr_is_simple_block(expr) - && i < len - 1 { - word(s.s, ~","); + // in the case of foo => expr + if arm.body.node.view_items.is_empty() && + arm.body.node.stmts.is_empty() && + arm.body.node.rules == ast::default_blk && + arm.body.node.expr.is_some() + { + match arm.body.node.expr { + Some(expr) => { + match expr.node { + ast::expr_block(blk) => { + // the block will close the pattern's ibox + print_block_unclosed_indent( + s, blk, alt_indent_unit); + } + _ => { + end(s); // close the ibox for the pattern + print_expr(s, expr); + } + } + if !expr_is_simple_block(expr) + && i < len - 1 { + word(s.s, ~","); + } + end(s); // close enclosing cbox + } + None => fail } - end(s); // close enclosing cbox - } - None => fail + } else { + // the block will close the pattern's ibox + print_block_unclosed_indent(s, arm.body, alt_indent_unit); } } bclose_(s, expr.span, alt_indent_unit); diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 8bab4b9e2d3d7..e59c1b2b8c7c8 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -147,7 +147,6 @@ extern "C" CDECL void upcall_s_exchange_malloc(s_exchange_malloc_args *args) { rust_task *task = args->task; LOG_UPCALL_ENTRY(task); - LOG(task, mem, "upcall exchange malloc(0x%" PRIxPTR ")", args->td); size_t total_size = get_box_size(args->size, args->td->align); // FIXME--does this have to be calloc? (Issue #2682) @@ -159,6 +158,9 @@ upcall_s_exchange_malloc(s_exchange_malloc_args *args) { header->prev = 0; header->next = 0; + LOG(task, mem, "exchange malloced %p of size %" PRIuPTR, + header, args->size); + args->retval = (uintptr_t)header; } @@ -187,6 +189,7 @@ extern "C" CDECL void upcall_s_exchange_free(s_exchange_free_args *args) { rust_task *task = args->task; LOG_UPCALL_ENTRY(task); + LOG(task, mem, "exchange freed %p", args->ptr); task->kernel->free(args->ptr); } diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs index 6da8a9d57c983..fe737442bd056 100644 --- a/src/rustc/lib/llvm.rs +++ b/src/rustc/lib/llvm.rs @@ -456,9 +456,11 @@ extern mod llvm { ValueRef; fn LLVMConstAShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; - fn LLVMConstGEP(ConstantVal: ValueRef, ConstantIndices: *uint, + fn LLVMConstGEP(ConstantVal: ValueRef, + ConstantIndices: *ValueRef, NumIndices: c_uint) -> ValueRef; - fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: *uint, + fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, + ConstantIndices: *ValueRef, NumIndices: c_uint) -> ValueRef; fn LLVMConstTrunc(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; fn LLVMConstSExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; @@ -493,10 +495,10 @@ extern mod llvm { fn LLVMConstShuffleVector(VectorAConstant: ValueRef, VectorBConstant: ValueRef, MaskConstant: ValueRef) -> ValueRef; - fn LLVMConstExtractValue(AggConstant: ValueRef, IdxList: *uint, + fn LLVMConstExtractValue(AggConstant: ValueRef, IdxList: *c_uint, NumIdx: c_uint) -> ValueRef; fn LLVMConstInsertValue(AggConstant: ValueRef, - ElementValueConstant: ValueRef, IdxList: *uint, + ElementValueConstant: ValueRef, IdxList: *c_uint, NumIdx: c_uint) -> ValueRef; fn LLVMConstInlineAsm(Ty: TypeRef, AsmString: *c_char, Constraints: *c_char, HasSideEffects: Bool, diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index 55067d82a77e5..176e133529ca6 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -220,7 +220,8 @@ use syntax::visit; use syntax::ast_util; use syntax::ast_map; use syntax::codemap::span; -use util::ppaux::{ty_to_str, region_to_str, explain_region}; +use util::ppaux::{ty_to_str, region_to_str, explain_region, + note_and_explain_region}; use std::map::{int_hash, hashmap, set}; use std::list; use std::list::{List, Cons, Nil}; @@ -464,6 +465,7 @@ impl borrowck_ctxt { err.cmt.span, fmt!("illegal borrow: %s", self.bckerr_code_to_str(err.code))); + self.note_and_explain_bckerr(err.code); } fn span_err(s: span, m: ~str) { @@ -488,37 +490,65 @@ impl borrowck_ctxt { fn bckerr_code_to_str(code: bckerr_code) -> ~str { match code { - err_mutbl(req, act) => { - fmt!("creating %s alias to aliasable, %s memory", - self.mut_to_str(req), self.mut_to_str(act)) - } - err_mut_uniq => { - ~"unique value in aliasable, mutable location" - } - err_mut_variant => { - ~"enum variant in aliasable, mutable location" - } - err_root_not_permitted => { - // note: I don't expect users to ever see this error - // message, reasons are discussed in attempt_root() in - // preserve.rs. - ~"rooting is not permitted" - } - err_out_of_root_scope(super_scope, sub_scope) => { - fmt!("managed value would have to be rooted for %s, \ - but can only be rooted for %s", - explain_region(self.tcx, sub_scope), - explain_region(self.tcx, super_scope)) - } - err_out_of_scope(super_scope, sub_scope) => { - fmt!("borrowed pointer must be valid for %s, \ - but the borrowed value is only valid for %s", - explain_region(self.tcx, sub_scope), - explain_region(self.tcx, super_scope)) + err_mutbl(req, act) => { + fmt!("creating %s alias to aliasable, %s memory", + self.mut_to_str(req), self.mut_to_str(act)) + } + err_mut_uniq => { + ~"unique value in aliasable, mutable location" + } + err_mut_variant => { + ~"enum variant in aliasable, mutable location" + } + err_root_not_permitted => { + // note: I don't expect users to ever see this error + // message, reasons are discussed in attempt_root() in + // preserve.rs. + ~"rooting is not permitted" + } + err_out_of_root_scope(*) => { + ~"cannot root managed value long enough" + } + err_out_of_scope(*) => { + ~"borrowed value does not live long enough" + } + } + } + + fn note_and_explain_bckerr(code: bckerr_code) { + match code { + err_mutbl(*) | err_mut_uniq | err_mut_variant | + err_root_not_permitted => {} + + err_out_of_root_scope(super_scope, sub_scope) => { + note_and_explain_region( + self.tcx, + ~"managed value would have to be rooted for ", + sub_scope, + ~"..."); + note_and_explain_region( + self.tcx, + ~"...but can only be rooted for ", + super_scope, + ~""); + } + + err_out_of_scope(super_scope, sub_scope) => { + note_and_explain_region( + self.tcx, + ~"borrowed pointer must be valid for ", + sub_scope, + ~"..."); + note_and_explain_region( + self.tcx, + ~"...but borrowed value is only valid for ", + super_scope, + ~""); } } } + fn cmt_to_str(cmt: cmt) -> ~str { let mc = &mem_categorization_ctxt {tcx: self.tcx, method_map: self.method_map}; diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs index a2e2049dcf773..7f2964181d8d0 100644 --- a/src/rustc/middle/borrowck/check_loans.rs +++ b/src/rustc/middle/borrowck/check_loans.rs @@ -423,6 +423,7 @@ impl check_loan_ctxt { e.cmt.span, fmt!("illegal borrow unless pure: %s", self.bccx.bckerr_code_to_str(e.code))); + self.bccx.note_and_explain_bckerr(e.code); self.tcx().sess.span_note( sp, fmt!("impure due to %s", msg)); @@ -484,10 +485,14 @@ impl check_loan_ctxt { // when there is an outstanding loan. In that case, it is not // safe to consider the use a last_use. fn check_last_use(expr: @ast::expr) { + debug!("Checking last use of expr %?", expr.id); let cmt = self.bccx.cat_expr(expr); let lp = match cmt.lp { - None => return, - Some(lp) => lp + None => { + debug!("Not a loanable expression"); + return; + } + Some(lp) => lp }; for self.walk_loans_of(cmt.id, lp) |_loan| { debug!("Removing last use entry %? due to outstanding loan", @@ -592,6 +597,9 @@ fn check_loans_in_local(local: @ast::local, fn check_loans_in_expr(expr: @ast::expr, &&self: check_loan_ctxt, vt: visit::vt) { + debug!("check_loans_in_expr(expr=%?/%s)", + expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr())); + self.check_for_conflicting_loans(expr.id); match expr.node { diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs index 3f31e011b0543..18e27e79fd168 100644 --- a/src/rustc/middle/borrowck/gather_loans.rs +++ b/src/rustc/middle/borrowck/gather_loans.rs @@ -90,8 +90,8 @@ fn req_loans_in_expr(ex: @ast::expr, let tcx = bccx.tcx; let old_root_ub = self.root_ub; - debug!("req_loans_in_expr(ex=%s)", - pprust::expr_to_str(ex, tcx.sess.intr())); + debug!("req_loans_in_expr(expr=%?/%s)", + ex.id, pprust::expr_to_str(ex, tcx.sess.intr())); // If this expression is borrowed, have to ensure it remains valid: for tcx.borrowings.find(ex.id).each |borrow| { @@ -200,6 +200,21 @@ fn req_loans_in_expr(ex: @ast::expr, visit::visit_expr(ex, self, vt); } + // FIXME--#3387 + // ast::expr_binary(_, lhs, rhs) => { + // // Universal comparison operators like ==, >=, etc + // // take their arguments by reference. + // let lhs_ty = ty::expr_ty(self.tcx(), lhs); + // if !ty::type_is_scalar(lhs_ty) { + // let scope_r = ty::re_scope(ex.id); + // let lhs_cmt = self.bccx.cat_expr(lhs); + // self.guarantee_valid(lhs_cmt, m_imm, scope_r); + // let rhs_cmt = self.bccx.cat_expr(rhs); + // self.guarantee_valid(rhs_cmt, m_imm, scope_r); + // } + // visit::visit_expr(ex, self, vt); + // } + ast::expr_field(rcvr, _, _) if self.bccx.method_map.contains_key(ex.id) => { // Receivers in method calls are always passed by ref. @@ -395,14 +410,15 @@ impl gather_loan_ctxt { } fn add_loans(scope_id: ast::node_id, loans: @DVec) { + debug!("adding %u loans to scope_id %?", loans.len(), scope_id); match self.req_maps.req_loan_map.find(scope_id) { - Some(l) => { - (*l).push(loans); - } - None => { - self.req_maps.req_loan_map.insert( - scope_id, @dvec::from_vec(~[mut loans])); - } + Some(l) => { + l.push(loans); + } + None => { + self.req_maps.req_loan_map.insert( + scope_id, @dvec::from_vec(~[mut loans])); + } } } diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index 259b0a111f890..b1396472d86f2 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -419,7 +419,7 @@ fn is_nullary_variant(cx: ctx, ex: @expr) -> bool { fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool, why: Option<(&str,&str)>) { - if ty::expr_is_lval(cx.method_map, ex) && + if ty::expr_is_lval(cx.tcx, cx.method_map, ex) && // this is a move !cx.last_use_map.contains_key(ex.id) && diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs index 1dbbec91402ff..f3459aa8cb3d6 100644 --- a/src/rustc/middle/liveness.rs +++ b/src/rustc/middle/liveness.rs @@ -1572,17 +1572,16 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) { expr_call(f, args, _) => { let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f)); - vt.visit_expr(f, self, vt); do vec::iter2(args, targs) |arg_expr, arg_ty| { match ty::resolved_mode(self.tcx, arg_ty.mode) { - by_val | by_copy | by_ref | by_mutbl_ref => { - vt.visit_expr(arg_expr, self, vt); - } - by_move => { - self.check_move_from_expr(arg_expr, vt); - } + by_val | by_copy | by_ref | by_mutbl_ref => {} + by_move => { + self.check_move_from_expr(arg_expr, vt); + } } } + + visit::visit_expr(expr, self, vt); } // no correctness conditions related to liveness @@ -1670,6 +1669,9 @@ impl @Liveness { } fn consider_last_use(expr: @expr, ln: LiveNode, var: Variable) { + debug!("consider_last_use(expr.id=%?, ln=%s, var=%s)", + expr.id, ln.to_str(), var.to_str()); + match self.live_on_exit(ln, var) { Some(_) => {} None => (*self.ir).add_last_use(expr.id, var) @@ -1682,7 +1684,7 @@ impl @Liveness { if self.ir.method_map.contains_key(expr.id) { // actually an rvalue, since this calls a method - return vt.visit_expr(expr, self, vt); + return; } match expr.node { @@ -1703,18 +1705,16 @@ impl @Liveness { self.check_move_from_expr(base, vt); } - expr_index(base, idx) => { + expr_index(base, _) => { // Moving from x[y] is allowed if x is never used later. // (Note that the borrowck guarantees that anything // being moved from is uniquely tied to the stack frame) self.check_move_from_expr(base, vt); - vt.visit_expr(idx, self, vt); } _ => { // For other kinds of lvalues, no checks are required, // and any embedded expressions are actually rvalues - vt.visit_expr(expr, self, vt); } } } diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs index 5b9a2e7010655..579c096e6349d 100644 --- a/src/rustc/middle/region.rs +++ b/src/rustc/middle/region.rs @@ -254,6 +254,10 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt) { let mut new_cx = cx; match expr.node { + // Calls or overloadable operators + // FIXME #3387 + // ast::expr_index(*) | ast::expr_binary(*) | + // ast::expr_unary(*) | ast::expr_call(*) => { debug!("node %d: %s", expr.id, pprust::expr_to_str(expr, cx.sess.intr())); diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index a96529fa01c74..643e385cd17b0 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -14,8 +14,11 @@ use middle::resolve::DefMap; use back::abi; use std::map::hashmap; use dvec::DVec; - +use datum::*; use common::*; +use expr::Dest; + +fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. // An option identifying a branch (either a literal, a enum variant or a // range) @@ -37,38 +40,25 @@ fn opt_eq(tcx: ty::ctxt, a: opt, b: opt) -> bool { } enum opt_result { - single_result(result), - range_result(result, result), + single_result(Result), + range_result(Result, Result), } fn trans_opt(bcx: block, o: opt) -> opt_result { let _icx = bcx.insn_ctxt("alt::trans_opt"); let ccx = bcx.ccx(); let mut bcx = bcx; match o { - lit(l) => { - match l.node { - ast::expr_vstore(@{node: ast::expr_lit( - @{node: ast::lit_str(s), _}), _}, - ast::vstore_uniq) => { - let strty = ty::mk_estr(bcx.tcx(), ty::vstore_uniq); - let cell = empty_dest_cell(); - bcx = tvec::trans_estr(bcx, s, Some(ast::vstore_uniq), - by_val(cell)); - add_clean_temp_immediate(bcx, *cell, strty); - return single_result(rslt(bcx, *cell)); - } - _ => { - return single_result(trans_temp_expr(bcx, l)); - } + lit(lit_expr) => { + let datumblock = expr::trans_to_datum(bcx, lit_expr); + return single_result(datumblock.to_result()); + } + var(disr_val, _) => { + return single_result(rslt(bcx, C_int(ccx, disr_val))); + } + range(l1, l2) => { + return range_result(rslt(bcx, consts::const_expr(ccx, l1)), + rslt(bcx, consts::const_expr(ccx, l2))); } - } - var(disr_val, _) => { - return single_result(rslt(bcx, C_int(ccx, disr_val))); - } - range(l1, l2) => { - return range_result(rslt(bcx, consts::const_expr(ccx, l1)), - rslt(bcx, consts::const_expr(ccx, l2))); - } } } @@ -140,7 +130,7 @@ fn expand_nested_bindings(bcx: block, m: match_, col: uint, val: ValueRef) ty: node_id_type(bcx, br.pats[col].id) }}]), - .. *br}); + ..*br}); } _ => vec::push(result, br) } @@ -173,7 +163,7 @@ fn enter_match(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef, } _ => br.bound }; - vec::push(result, @{pats: pats, bound: bound,.. *br}); + vec::push(result, @{pats: pats, bound: bound, ..*br}); } None => () } @@ -229,11 +219,10 @@ fn enter_rec_or_struct(bcx: block, dm: DefMap, m: match_, col: uint, ast::pat_rec(fpats, _) | ast::pat_struct(_, fpats, _) => { let mut pats = ~[]; for vec::each(fields) |fname| { - let mut pat = dummy; - for vec::each(fpats) |fpat| { - if fpat.ident == fname { pat = fpat.pat; break; } + match fpats.find(|p| p.ident == fname) { + None => vec::push(pats, dummy), + Some(pat) => vec::push(pats, pat.pat) } - vec::push(pats, pat); } Some(pats) } @@ -327,38 +316,25 @@ fn extract_variant_args(bcx: block, pat_id: ast::node_id, return {vals: args, bcx: bcx}; } -fn collect_record_fields(m: match_, col: uint) -> ~[ast::ident] { +fn collect_record_or_struct_fields(m: match_, col: uint) -> ~[ast::ident] { let mut fields: ~[ast::ident] = ~[]; for vec::each(m) |br| { match br.pats[col].node { - ast::pat_rec(fs, _) => { - for vec::each(fs) |f| { - if !vec::any(fields, |x| f.ident == x) { - vec::push(fields, f.ident); - } - } - } + ast::pat_rec(fs, _) => extend(&mut fields, fs), + ast::pat_struct(_, fs, _) => extend(&mut fields, fs), _ => () } } return fields; -} -fn collect_struct_fields(m: match_, col: uint) -> ~[ast::ident] { - let mut fields: ~[ast::ident] = ~[]; - for vec::each(m) |br| { - match br.pats[col].node { - ast::pat_struct(_, fs, _) => { - for vec::each(fs) |f| { - if !vec::any(fields, |x| f.ident == x) { - vec::push(fields, f.ident); - } + fn extend(idents: &mut ~[ast::ident], field_pats: &[ast::field_pat]) { + for field_pats.each |field_pat| { + let field_ident = field_pat.ident; + if !vec::any(*idents, |x| x == field_ident) { + vec::push(*idents, field_ident); } - } - _ => () } } - return fields; } fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) { @@ -366,17 +342,17 @@ fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) { let pat_id = br.pats[col].id; match bcx.ccx().maps.root_map.find({id:pat_id, derefs:0u}) { - None => (), - Some(scope_id) => { - // Note: the scope_id will always be the id of the alt. See the - // extended comment in rustc::middle::borrowck::preserve() for - // details (look for the case covering cat_discr). - - let ty = node_id_type(bcx, pat_id); - let val = load_if_immediate(bcx, val, ty); - root_value(bcx, val, ty, scope_id); - return; // if we kept going, we'd only be rooting same value again - } + None => (), + Some(scope_id) => { + // Note: the scope_id will always be the id of the alt. See + // the extended comment in rustc::middle::borrowck::preserve() + // for details (look for the case covering cat_discr). + + let datum = Datum {val: val, ty: node_id_type(bcx, pat_id), + mode: ByRef, source: FromLvalue}; + datum.root(bcx, scope_id); + return; // if we kept going, we'd only re-root the same value + } } } } @@ -459,7 +435,10 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], let _icx = bcx.insn_ctxt("alt::compile_submatch"); let mut bcx = bcx; let tcx = bcx.tcx(), dm = tcx.def_map; - if m.len() == 0u { Br(bcx, option::get(chk)()); return; } + if m.len() == 0u { + Br(bcx, option::get(chk)()); + return; + } if m[0].pats.len() == 0u { let data = m[0].data; match data.guard { @@ -471,27 +450,28 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], for data.id_map.each |key, val| { let binding = assoc(key, m[0].bound).get(); - let (llval, mode) = (binding.val, binding.mode); - let ty = binding.ty; + let datum = Datum {val: binding.val, ty: binding.ty, + mode: ByRef, source: FromLvalue}; - if mode == ast::bind_by_value { - let llty = type_of::type_of(bcx.fcx.ccx, ty); + if binding.mode == ast::bind_by_value { + let llty = type_of::type_of(bcx.fcx.ccx, binding.ty); let alloc = alloca(bcx, llty); - bcx = copy_val(bcx, INIT, alloc, - load_if_immediate(bcx, llval, ty), ty); + bcx = datum.copy_to(bcx, INIT, alloc); bcx.fcx.lllocals.insert(val, local_mem(alloc)); - add_clean(bcx, alloc, ty); - } else if mode == ast::bind_by_move { + add_clean(bcx, alloc, binding.ty); + } else if binding.mode == ast::bind_by_move { fail ~"can't translate bind_by_move into a pattern guard"; } else { - bcx.fcx.lllocals.insert(val, local_mem(llval)); + bcx.fcx.lllocals.insert(val, local_mem(datum.val)); } }; - let {bcx: guard_cx, val} = { + + let Result {bcx: guard_cx, val} = { do with_scope_result(bcx, e.info(), ~"guard") |bcx| { - trans_temp_expr(bcx, e) + expr::trans_to_appropriate_llval(bcx, e) } }; + bcx = do with_cond(guard_cx, Not(guard_cx, val)) |bcx| { compile_submatch(bcx, vec::tail(m), vals, chk, exits); bcx @@ -525,58 +505,22 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], root_pats_as_necessary(bcx, m, col, val); - let rec_fields = collect_record_fields(m, col); - // Separate path for extracting and binding record fields + let rec_fields = collect_record_or_struct_fields(m, col); if rec_fields.len() > 0 { - let fields = ty::get_fields(node_id_type(bcx, pat_id)); - let mut rec_vals = ~[]; - for vec::each(rec_fields) |field_name| { - let ix = option::get(ty::field_idx(field_name, fields)); - vec::push(rec_vals, GEPi(bcx, val, [0u, ix])); - } - compile_submatch(bcx, - enter_rec_or_struct(bcx, dm, m, col, rec_fields, - val), - vec::append(rec_vals, vals_left), - chk, - exits); - return; - } - - // Separate path for extracting and binding struct fields. - let struct_fields = collect_struct_fields(m, col); - if struct_fields.len() > 0 { - let class_id, class_fields; - match ty::get(node_id_type(bcx, pat_id)).struct { - ty::ty_class(cid, _) => { - class_id = cid; - class_fields = ty::lookup_class_fields(ccx.tcx, class_id); - } - _ => { - ccx.tcx.sess.bug(~"struct pattern didn't resolve to a \ - struct"); + let pat_ty = node_id_type(bcx, pat_id); + do expr::with_field_tys(tcx, pat_ty) |_has_dtor, field_tys| { + let mut rec_vals = ~[]; + for vec::each(rec_fields) |field_name| { + let ix = ty::field_idx_strict(tcx, field_name, field_tys); + vec::push(rec_vals, GEPi(bcx, val, struct_field(ix))); } + compile_submatch( + bcx, + enter_rec_or_struct(bcx, dm, m, col, rec_fields, val), + vec::append(rec_vals, vals_left), + chk, + exits); } - - // Index the class fields. - let field_map = std::map::uint_hash(); - for class_fields.eachi |i, class_field| { - field_map.insert(class_field.ident, i); - } - - // Fetch each field. - let mut struct_vals = ~[]; - for struct_fields.each |field_name| { - let index = field_map.get(field_name); - let fldptr = base::get_struct_field(bcx, val, class_id, index); - vec::push(struct_vals, fldptr); - } - compile_submatch(bcx, - enter_rec_or_struct(bcx, dm, m, col, struct_fields, - val), - vec::append(struct_vals, vals_left), - chk, - exits); return; } @@ -641,8 +585,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], else { compare }; } range(_, _) => { - let pty = node_id_type(bcx, pat_id); - test_val = load_if_immediate(bcx, val, pty); + test_val = Load(bcx, val); kind = compare; } } @@ -665,6 +608,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], let exhaustive = option::is_none(chk) && defaults.len() == 0u; let len = opts.len(); let mut i = 0u; + // Compile subtrees for each option for vec::each(opts) |opt| { i += 1u; @@ -685,20 +629,25 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], } compare => { let t = node_id_type(bcx, pat_id); - let {bcx: after_cx, val: matches} = { + let Result {bcx: after_cx, val: matches} = { do with_scope_result(bcx, None, ~"compare_scope") |bcx| { match trans_opt(bcx, opt) { - single_result({bcx, val}) => { - trans_compare(bcx, ast::eq, test_val, t, val, t) - } - range_result( - {val: vbegin, _}, {bcx, val: vend}) => { - let {bcx, val: llge} = trans_compare( - bcx, ast::ge, test_val, t, vbegin, t); - let {bcx, val: llle} = trans_compare( - bcx, ast::le, test_val, t, vend, t); - {bcx: bcx, val: And(bcx, llge, llle)} - } + single_result( + Result {bcx, val}) => + { + trans_compare(bcx, ast::eq, test_val, + t, val, t) + } + range_result( + Result {val: vbegin, _}, + Result {bcx, val: vend}) => + { + let Result {bcx, val: llge} = trans_compare( + bcx, ast::ge, test_val, t, vbegin, t); + let Result {bcx, val: llle} = trans_compare( + bcx, ast::le, test_val, t, vend, t); + rslt(bcx, And(bcx, llge, llle)) + } } } }; @@ -805,19 +754,17 @@ fn make_pattern_bindings(bcx: block, phi_bindings: phi_bindings_list) } ast::bind_by_value | ast::bind_by_move => { // by value: make a new temporary and copy the value out - let lltype = type_of::type_of(bcx.fcx.ccx, binding.ty); - let allocation = alloca(bcx, lltype); - let ty = binding.ty; - bcx = if binding.mode == ast::bind_by_value { - copy_val(bcx, INIT, allocation, - load_if_immediate(bcx, phi_val, ty), ty) + let phi_datum = Datum {val: phi_val, ty: binding.ty, + mode: ByRef, source: FromLvalue}; + let scratch = scratch_datum(bcx, binding.ty, false); + if binding.mode == ast::bind_by_value { + phi_datum.copy_to_datum(bcx, INIT, scratch); } else { - move_val(bcx, INIT, allocation, - {bcx: bcx, val: phi_val, kind: lv_owned}, ty) - }; + phi_datum.move_to_datum(bcx, INIT, scratch); + } bcx.fcx.lllocals.insert(binding.pat_id, - local_mem(allocation)); - add_clean(bcx, allocation, ty); + local_mem(scratch.val)); + add_clean(bcx, scratch.val, binding.ty); } } } @@ -827,24 +774,31 @@ fn make_pattern_bindings(bcx: block, phi_bindings: phi_bindings_list) fn trans_alt(bcx: block, alt_expr: @ast::expr, - expr: @ast::expr, + discr_expr: @ast::expr, arms: ~[ast::arm], - dest: dest) -> block { + dest: Dest) -> block { let _icx = bcx.insn_ctxt("alt::trans_alt"); do with_scope(bcx, alt_expr.info(), ~"alt") |bcx| { - trans_alt_inner(bcx, expr, arms, dest) + trans_alt_inner(bcx, discr_expr, arms, dest) } } -fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm], - dest: dest) -> block { +fn trans_alt_inner(scope_cx: block, + discr_expr: @ast::expr, + arms: ~[ast::arm], + dest: Dest) -> block { let _icx = scope_cx.insn_ctxt("alt::trans_alt_inner"); - let bcx = scope_cx, tcx = bcx.tcx(); - let mut bodies = ~[], matches = ~[]; + let mut bcx = scope_cx; + let tcx = bcx.tcx(); - let {bcx, val, _} = trans_temp_expr(bcx, expr); - if bcx.unreachable { return bcx; } + let discr_datum = unpack_datum!(bcx, { + expr::trans_to_datum(bcx, discr_expr) + }); + if bcx.unreachable { + return bcx; + } + let mut bodies = ~[], matches = ~[]; for vec::each(arms) |a| { let body = scope_block(bcx, a.body.info(), ~"case_body"); let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]); @@ -857,30 +811,22 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm], } } - fn mk_fail(bcx: block, sp: span, msg: ~str, - done: @mut Option) -> BasicBlockRef { - match *done { Some(bb) => return bb, _ => () } - let fail_cx = sub_block(bcx, ~"case_fallthrough"); - trans_fail(fail_cx, Some(sp), msg); - *done = Some(fail_cx.llbb); - return fail_cx.llbb; - } - let t = node_id_type(bcx, expr.id); - let mk_fail = { let fail_cx = @mut None; - // special case for uninhabited type - if ty::type_is_empty(tcx, t) { - Some(|| mk_fail(scope_cx, expr.span, + let t = node_id_type(bcx, discr_expr.id); + let chk = { + if ty::type_is_empty(tcx, t) { + // Special case for empty types + let fail_cx = @mut None; + Some(|| mk_fail(scope_cx, discr_expr.span, ~"scrutinizing value that can't exist", fail_cx)) - } - else { - None - } + } else { + None + } }; let mut exit_map = ~[]; - let spilled = spill_if_immediate(bcx, val, t); - compile_submatch(bcx, matches, ~[spilled], mk_fail, exit_map); + let lldiscr = discr_datum.to_ref_llval(bcx); + compile_submatch(bcx, matches, ~[lldiscr], chk, exit_map); - let mut arm_cxs = ~[], arm_dests = ~[], i = 0u; + let mut arm_cxs = ~[], i = 0u; for vec::each(arms) |a| { let body_cx = bodies[i]; let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]); @@ -888,9 +834,8 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm], None => {} Some(phi_bindings) => { let body_cx = make_pattern_bindings(body_cx, phi_bindings); - let arm_dest = dup_for_join(dest); - vec::push(arm_dests, arm_dest); - let mut arm_cx = trans_block(body_cx, a.body, arm_dest); + let mut arm_cx = + controlflow::trans_block(body_cx, a.body, dest); arm_cx = trans_block_cleanups(arm_cx, block_cleanups(body_cx)); vec::push(arm_cxs, arm_cx); @@ -898,7 +843,16 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm], } i += 1u; } - join_returns(scope_cx, arm_cxs, arm_dests, dest) + return controlflow::join_blocks(scope_cx, arm_cxs); + + fn mk_fail(bcx: block, sp: span, msg: ~str, + done: @mut Option) -> BasicBlockRef { + match *done { Some(bb) => return bb, _ => () } + let fail_cx = sub_block(bcx, ~"case_fallthrough"); + controlflow::trans_fail(fail_cx, Some(sp), msg); + *done = Some(fail_cx.llbb); + return fail_cx.llbb; + } } // Not alt-related, but similar to the pattern-munging code above @@ -910,88 +864,66 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef, // Necessary since bind_irrefutable_pat is called outside trans_alt match pat.node { - ast::pat_ident(_, _,inner) => { - if pat_is_variant(bcx.tcx().def_map, pat) { return bcx; } - if make_copy { - let ty = node_id_type(bcx, pat.id); - let llty = type_of::type_of(ccx, ty); - let alloc = alloca(bcx, llty); - bcx = copy_val(bcx, INIT, alloc, - load_if_immediate(bcx, val, ty), ty); - bcx.fcx.lllocals.insert(pat.id, local_mem(alloc)); - add_clean(bcx, alloc, ty); - } else { bcx.fcx.lllocals.insert(pat.id, local_mem(val)); } - match inner { - Some(pat) => { bcx = bind_irrefutable_pat(bcx, pat, val, true); } - _ => () - } - } - ast::pat_enum(_, sub) => { - let vdefs = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat.id)); - let args = extract_variant_args(bcx, pat.id, vdefs, val); - let mut i = 0; - do option::iter(sub) |sub| { for vec::each(args.vals) |argval| { - bcx = bind_irrefutable_pat(bcx, sub[i], argval, make_copy); - i += 1; - }} - } - ast::pat_rec(fields, _) => { - let rec_fields = ty::get_fields(node_id_type(bcx, pat.id)); - for vec::each(fields) |f| { - let ix = option::get(ty::field_idx(f.ident, rec_fields)); - let fldptr = GEPi(bcx, val, [0u, ix]); - bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy); - } + ast::pat_ident(_, _,inner) => { + if pat_is_variant(bcx.tcx().def_map, pat) { + return bcx; + } + + if make_copy { + let binding_ty = node_id_type(bcx, pat.id); + let datum = Datum {val: val, ty: binding_ty, + mode: ByRef, source: FromRvalue}; + let scratch = scratch_datum(bcx, binding_ty, false); + datum.copy_to_datum(bcx, INIT, scratch); + bcx.fcx.lllocals.insert(pat.id, local_mem(scratch.val)); + add_clean(bcx, scratch.val, binding_ty); + } else { + bcx.fcx.lllocals.insert(pat.id, local_mem(val)); + } + + for inner.each |inner_pat| { + bcx = bind_irrefutable_pat(bcx, inner_pat, val, true); + } } - ast::pat_struct(_, fields, _) => { - // Grab the class data that we care about. - let class_fields, class_id; - match ty::get(node_id_type(bcx, pat.id)).struct { - ty::ty_class(cid, _) => { - class_id = cid; - class_fields = ty::lookup_class_fields(ccx.tcx, class_id); + ast::pat_enum(_, sub_pats) => { + let pat_def = ccx.tcx.def_map.get(pat.id); + let vdefs = ast_util::variant_def_ids(pat_def); + let args = extract_variant_args(bcx, pat.id, vdefs, val); + for sub_pats.each |sub_pat| { + for vec::eachi(args.vals) |i, argval| { + bcx = bind_irrefutable_pat(bcx, sub_pat[i], + argval, make_copy); + } } - _ => { - ccx.tcx.sess.span_bug(pat.span, ~"struct pattern didn't \ - resolve to a struct"); + } + ast::pat_rec(fields, _) | ast::pat_struct(_, fields, _) => { + let tcx = bcx.tcx(); + let pat_ty = node_id_type(bcx, pat.id); + do expr::with_field_tys(tcx, pat_ty) |_has_dtor, field_tys| { + for vec::each(fields) |f| { + let ix = ty::field_idx_strict(tcx, f.ident, field_tys); + let fldptr = GEPi(bcx, val, struct_field(ix)); + bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy); + } } } - - // Index the class fields. - let field_map = std::map::uint_hash(); - for class_fields.eachi |i, class_field| { - field_map.insert(class_field.ident, i); + ast::pat_tup(elems) => { + for vec::eachi(elems) |i, elem| { + let fldptr = GEPi(bcx, val, [0u, i]); + bcx = bind_irrefutable_pat(bcx, elem, fldptr, make_copy); + } } - - // Fetch each field. - for fields.each |supplied_field| { - let index = field_map.get(supplied_field.ident); - let fldptr = base::get_struct_field(bcx, val, class_id, index); - bcx = bind_irrefutable_pat(bcx, supplied_field.pat, fldptr, - make_copy); + ast::pat_box(inner) => { + let llbox = Load(bcx, val); + let unboxed = GEPi(bcx, llbox, [0u, abi::box_field_body]); + bcx = bind_irrefutable_pat(bcx, inner, unboxed, true); } - } - ast::pat_tup(elems) => { - let mut i = 0u; - for vec::each(elems) |elem| { - let fldptr = GEPi(bcx, val, [0u, i]); - bcx = bind_irrefutable_pat(bcx, elem, fldptr, make_copy); - i += 1u; + ast::pat_uniq(inner) => { + let llbox = Load(bcx, val); + let unboxed = GEPi(bcx, llbox, [0u, abi::box_field_body]); + bcx = bind_irrefutable_pat(bcx, inner, unboxed, true); } - } - ast::pat_box(inner) => { - let llbox = Load(bcx, val); - let unboxed = - GEPi(bcx, llbox, [0u, abi::box_field_body]); - bcx = bind_irrefutable_pat(bcx, inner, unboxed, true); - } - ast::pat_uniq(inner) => { - let llbox = Load(bcx, val); - let unboxed = - GEPi(bcx, llbox, [0u, abi::box_field_body]); - bcx = bind_irrefutable_pat(bcx, inner, unboxed, true); - } - ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => () + ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => () } return bcx; } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 88fc4fb3ff1c9..b74d89dc46594 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -42,61 +42,18 @@ use metadata::common::link_meta; use util::ppaux; use util::ppaux::{ty_to_str, ty_to_short_str}; use syntax::diagnostic::expect; +use util::common::indenter; use build::*; use shape::*; use type_of::*; use common::*; -use common::result; use syntax::ast_map::{path, path_mod, path_name}; use syntax::parse::token::special_idents; use std::smallintmap; use option::{is_none, is_some}; -// Destinations - -// These are passed around by the code generating functions to track the -// destination of a computation's value. - -enum dest { - by_val(@mut ValueRef), - save_in(ValueRef), - ignore, -} - -impl dest : cmp::Eq { - pure fn eq(&&other: dest) -> bool { - match (self, other) { - (by_val(e0a), by_val(e0b)) => e0a == e0b, - (save_in(e0a), save_in(e0b)) => e0a == e0b, - (ignore, ignore) => true, - (by_val(*), _) => false, - (save_in(*), _) => false, - (ignore, _) => false, - } - } -} - -fn dest_str(ccx: @crate_ctxt, d: dest) -> ~str { - match d { - by_val(v) => fmt!("by_val(%s)", val_str(ccx.tn, *v)), - save_in(v) => fmt!("save_in(%s)", val_str(ccx.tn, v)), - ignore => ~"ignore" - } -} - -fn empty_dest_cell() -> @mut ValueRef { - return @mut llvm::LLVMGetUndef(T_nil()); -} - -fn dup_for_join(dest: dest) -> dest { - match dest { - by_val(_) => by_val(empty_dest_cell()), - _ => dest - } -} - struct icx_popper { let ccx: @crate_ctxt; new(ccx: @crate_ctxt) { self.ccx = ccx; } @@ -133,54 +90,6 @@ impl fn_ctxt: get_insn_ctxt { } } -fn join_returns(parent_cx: block, in_cxs: ~[block], - in_ds: ~[dest], out_dest: dest) -> block { - let out = sub_block(parent_cx, ~"join"); - let mut reachable = false, i = 0u, phi = None; - for vec::each(in_cxs) |cx| { - if !cx.unreachable { - Br(cx, out.llbb); - reachable = true; - match in_ds[i] { - by_val(cell) => { - if option::is_none(phi) { - phi = Some(EmptyPhi(out, val_ty(*cell))); - } - AddIncomingToPhi(option::get(phi), *cell, cx.llbb); - } - _ => () - } - } - i += 1u; - } - if !reachable { - Unreachable(out); - } else { - match out_dest { - by_val(cell) => *cell = option::get(phi), - _ => () - } - } - return out; -} - -// Used to put an immediate value in a dest. -fn store_in_dest(bcx: block, val: ValueRef, dest: dest) -> block { - match dest { - ignore => (), - by_val(cell) => *cell = val, - save_in(addr) => Store(bcx, val, addr) - } - return bcx; -} - -fn get_dest_addr(dest: dest) -> ValueRef { - match dest { - save_in(a) => a, - _ => fail ~"get_dest_addr: not a save_in" - } -} - fn log_fn_time(ccx: @crate_ctxt, name: ~str, start: time::Timespec, end: time::Timespec) { let elapsed = 1000 * ((end.sec - start.sec) as int) + @@ -188,7 +97,6 @@ fn log_fn_time(ccx: @crate_ctxt, name: ~str, start: time::Timespec, vec::push(*ccx.stats.fn_times, {ident: name, time: elapsed}); } - fn decl_fn(llmod: ModuleRef, name: ~str, cc: lib::llvm::CallConv, llty: TypeRef) -> ValueRef { let llfn: ValueRef = str::as_c_str(name, |buf| { @@ -202,7 +110,6 @@ fn decl_cdecl_fn(llmod: ModuleRef, name: ~str, llty: TypeRef) -> ValueRef { return decl_fn(llmod, name, lib::llvm::CCallConv, llty); } - // Only use this if you are going to actually define the function. It's // not valid to simply declare a function as internal. fn decl_internal_cdecl_fn(llmod: ModuleRef, name: ~str, llty: TypeRef) -> @@ -255,17 +162,6 @@ fn trans_foreign_call(cx: block, externs: hashmap<~str, ValueRef>, return Call(cx, llforeign, call_args); } -fn trans_free(cx: block, v: ValueRef) -> block { - let _icx = cx.insn_ctxt("trans_free"); - trans_rtcall(cx, ~"free", ~[PointerCast(cx, v, T_ptr(T_i8()))], ignore) -} - -fn trans_unique_free(cx: block, v: ValueRef) -> block { - let _icx = cx.insn_ctxt("trans_unique_free"); - trans_rtcall(cx, ~"exchange_free", ~[PointerCast(cx, v, T_ptr(T_i8()))], - ignore) -} - fn umax(cx: block, a: ValueRef, b: ValueRef) -> ValueRef { let _icx = cx.insn_ctxt("umax"); let cond = ICmp(cx, lib::llvm::IntULT, a, b); @@ -278,67 +174,6 @@ fn umin(cx: block, a: ValueRef, b: ValueRef) -> ValueRef { return Select(cx, cond, a, b); } -fn alloca(cx: block, t: TypeRef) -> ValueRef { - alloca_maybe_zeroed(cx, t, false) -} - -fn alloca_zeroed(cx: block, t: TypeRef) -> ValueRef { - alloca_maybe_zeroed(cx, t, true) -} - -fn alloca_maybe_zeroed(cx: block, t: TypeRef, zero: bool) -> ValueRef { - let _icx = cx.insn_ctxt("alloca"); - if cx.unreachable { return llvm::LLVMGetUndef(t); } - let initcx = raw_block(cx.fcx, false, cx.fcx.llstaticallocas); - let p = Alloca(initcx, t); - if zero { memzero(initcx, p, t); } - return p; -} - -fn zero_mem(cx: block, llptr: ValueRef, t: ty::t) -> block { - let _icx = cx.insn_ctxt("zero_mem"); - let bcx = cx; - let ccx = cx.ccx(); - let llty = type_of(ccx, t); - memzero(bcx, llptr, llty); - return bcx; -} - -// Always use this function instead of storing a zero constant to the memory -// in question. If you store a zero constant, LLVM will drown in vreg -// allocation for large data structures, and the generated code will be -// awful. (A telltale sign of this is large quantities of -// `mov [byte ptr foo],0` in the generated code.) -fn memzero(cx: block, llptr: ValueRef, llty: TypeRef) { - let _icx = cx.insn_ctxt("memzero"); - let ccx = cx.ccx(); - - let intrinsic_key; - match ccx.sess.targ_cfg.arch { - session::arch_x86 | session::arch_arm => { - intrinsic_key = ~"llvm.memset.p0i8.i32"; - } - session::arch_x86_64 => { - intrinsic_key = ~"llvm.memset.p0i8.i64"; - } - } - - let llintrinsicfn = ccx.intrinsics.get(intrinsic_key); - let llptr = PointerCast(cx, llptr, T_ptr(T_i8())); - let llzeroval = C_u8(0); - let size = IntCast(cx, llsize_of(ccx, llty), ccx.int_type); - let align = C_i32(1i32); - let volatile = C_bool(false); - Call(cx, llintrinsicfn, ~[llptr, llzeroval, size, align, volatile]); -} - -fn arrayalloca(cx: block, t: TypeRef, v: ValueRef) -> ValueRef { - let _icx = cx.insn_ctxt("arrayalloca"); - if cx.unreachable { return llvm::LLVMGetUndef(t); } - return ArrayAlloca( - raw_block(cx.fcx, false, cx.fcx.llstaticallocas), t, v); -} - // Given a pointer p, returns a pointer sz(p) (i.e., inc'd by sz bytes). // The type of the returned pointer is always i8*. If you care about the // return type, use bump_ptr(). @@ -396,7 +231,7 @@ fn opaque_box_body(bcx: block, // malloc_raw_dyn: allocates a box to contain a given type, but with a // potentially dynamic size. fn malloc_raw_dyn(bcx: block, t: ty::t, heap: heap, - size: ValueRef) -> result { + size: ValueRef) -> Result { let _icx = bcx.insn_ctxt("malloc_raw"); let ccx = bcx.ccx(); @@ -411,20 +246,36 @@ fn malloc_raw_dyn(bcx: block, t: ty::t, heap: heap, // Get the tydesc for the body: let static_ti = get_tydesc(ccx, t); - lazily_emit_all_tydesc_glue(ccx, static_ti); + glue::lazily_emit_all_tydesc_glue(ccx, static_ti); // Allocate space: let tydesc = PointerCast(bcx, static_ti.tydesc, T_ptr(T_i8())); let rval = alloca_zeroed(bcx, T_ptr(T_i8())); - let bcx = trans_rtcall(bcx, rtcall, ~[tydesc, size], save_in(rval)); - let retval = {bcx: bcx, val: PointerCast(bcx, Load(bcx, rval), llty)}; - return retval; + let bcx = callee::trans_rtcall(bcx, rtcall, ~[tydesc, size], + expr::SaveIn(rval)); + return rslt(bcx, PointerCast(bcx, Load(bcx, rval), llty)); +} + +/** +* Get the type of a box in the default address space. +* +* Shared box pointers live in address space 1 so the GC strategy can find +* them. Before taking a pointer to the inside of a box it should be cast into +* address space 0. Otherwise the resulting (non-box) pointer will be in the +* wrong address space and thus be the wrong type. +*/ +fn non_gc_box_cast(bcx: block, val: ValueRef) -> ValueRef { + debug!("non_gc_box_cast"); + add_comment(bcx, ~"non_gc_box_cast"); + assert(llvm::LLVMGetPointerAddressSpace(val_ty(val)) == gc_box_addrspace); + let non_gc_t = T_ptr(llvm::LLVMGetElementType(val_ty(val))); + PointerCast(bcx, val, non_gc_t) } // malloc_raw: expects an unboxed type and returns a pointer to // enough space for a box of that type. This includes a rust_opaque_box // header. -fn malloc_raw(bcx: block, t: ty::t, heap: heap) -> result { +fn malloc_raw(bcx: block, t: ty::t, heap: heap) -> Result { malloc_raw_dyn(bcx, t, heap, llsize_of(bcx.ccx(), type_of(bcx.ccx(), t))) } @@ -433,7 +284,7 @@ fn malloc_raw(bcx: block, t: ty::t, heap: heap) -> result { fn malloc_general_dyn(bcx: block, t: ty::t, heap: heap, size: ValueRef) -> {bcx: block, box: ValueRef, body: ValueRef} { let _icx = bcx.insn_ctxt("malloc_general"); - let {bcx: bcx, val: llbox} = malloc_raw_dyn(bcx, t, heap, size); + let Result {bcx: bcx, val: llbox} = malloc_raw_dyn(bcx, t, heap, size); let non_gc_box = non_gc_box_cast(bcx, llbox); let body = GEPi(bcx, non_gc_box, [0u, abi::box_field_body]); return {bcx: bcx, box: llbox, body: body}; @@ -464,7 +315,7 @@ fn get_tydesc(ccx: @crate_ctxt, t: ty::t) -> @tydesc_info { Some(inf) => inf, _ => { ccx.stats.n_static_tydescs += 1u; - let inf = declare_tydesc(ccx, t); + let inf = glue::declare_tydesc(ccx, t); ccx.tydescs.insert(t, inf); inf } @@ -527,391 +378,6 @@ fn note_unique_llvm_symbol(ccx: @crate_ctxt, sym: ~str) { ccx.all_llvm_symbols.insert(sym, ()); } -// Chooses the addrspace for newly declared types. -fn declare_tydesc_addrspace(ccx: @crate_ctxt, t: ty::t) -> addrspace { - if !ty::type_needs_drop(ccx.tcx, t) { - return default_addrspace; - } else if ty::type_is_immediate(t) { - // For immediate types, we don't actually need an addrspace, because - // e.g. boxed types include pointers to their contents which are - // already correctly tagged with addrspaces. - return default_addrspace; - } else { - return ccx.next_addrspace(); - } -} - -// Generates the declaration for (but doesn't emit) a type descriptor. -fn declare_tydesc(ccx: @crate_ctxt, t: ty::t) -> @tydesc_info { - let _icx = ccx.insn_ctxt("declare_tydesc"); - // If emit_tydescs already ran, then we shouldn't be creating any new - // tydescs. - assert !ccx.finished_tydescs; - - let llty = type_of(ccx, t); - - if ccx.sess.count_type_sizes() { - io::println(fmt!("%u\t%s", - llsize_of_real(ccx, llty), - ty_to_str(ccx.tcx, t))); - } - - let llsize = llsize_of(ccx, llty); - let llalign = llalign_of(ccx, llty); - let addrspace = declare_tydesc_addrspace(ccx, t); - //XXX this triggers duplicate LLVM symbols - let name = if false /*ccx.sess.opts.debuginfo*/ { - mangle_internal_name_by_type_only(ccx, t, ~"tydesc") - } else { mangle_internal_name_by_seq(ccx, ~"tydesc") }; - note_unique_llvm_symbol(ccx, name); - log(debug, fmt!("+++ declare_tydesc %s %s", ty_to_str(ccx.tcx, t), name)); - let gvar = str::as_c_str(name, |buf| { - llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type, buf) - }); - let inf = - @{ty: t, - tydesc: gvar, - size: llsize, - align: llalign, - addrspace: addrspace, - mut take_glue: None, - mut drop_glue: None, - mut free_glue: None, - mut visit_glue: None}; - log(debug, ~"--- declare_tydesc " + ppaux::ty_to_str(ccx.tcx, t)); - return inf; -} - -type glue_helper = fn@(block, ValueRef, ty::t); - -fn declare_generic_glue(ccx: @crate_ctxt, t: ty::t, llfnty: TypeRef, - name: ~str) -> ValueRef { - let _icx = ccx.insn_ctxt("declare_generic_glue"); - let name = name; - let mut fn_nm; - //XXX this triggers duplicate LLVM symbols - if false /*ccx.sess.opts.debuginfo*/ { - fn_nm = mangle_internal_name_by_type_only(ccx, t, (~"glue_" + name)); - } else { - fn_nm = mangle_internal_name_by_seq(ccx, (~"glue_" + name)); - } - note_unique_llvm_symbol(ccx, fn_nm); - let llfn = decl_cdecl_fn(ccx.llmod, fn_nm, llfnty); - set_glue_inlining(llfn, t); - return llfn; -} - -fn make_generic_glue_inner(ccx: @crate_ctxt, t: ty::t, - llfn: ValueRef, helper: glue_helper) -> ValueRef { - let _icx = ccx.insn_ctxt("make_generic_glue_inner"); - let fcx = new_fn_ctxt(ccx, ~[], llfn, None); - lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); - ccx.stats.n_glues_created += 1u; - // All glue functions take values passed *by alias*; this is a - // requirement since in many contexts glue is invoked indirectly and - // the caller has no idea if it's dealing with something that can be - // passed by value. - // - // llfn is expected be declared to take a parameter of the appropriate - // type, so we don't need to explicitly cast the function parameter. - - let bcx = top_scope_block(fcx, None); - let lltop = bcx.llbb; - let llrawptr0 = llvm::LLVMGetParam(llfn, 3u as c_uint); - helper(bcx, llrawptr0, t); - finish_fn(fcx, lltop); - return llfn; -} - -fn make_generic_glue(ccx: @crate_ctxt, t: ty::t, llfn: ValueRef, - helper: glue_helper, name: ~str) - -> ValueRef { - let _icx = ccx.insn_ctxt("make_generic_glue"); - if !ccx.sess.trans_stats() { - return make_generic_glue_inner(ccx, t, llfn, helper); - } - - let start = time::get_time(); - let llval = make_generic_glue_inner(ccx, t, llfn, helper); - let end = time::get_time(); - log_fn_time(ccx, ~"glue " + name + ~" " + ty_to_short_str(ccx.tcx, t), - start, end); - return llval; -} - -fn emit_tydescs(ccx: @crate_ctxt) { - let _icx = ccx.insn_ctxt("emit_tydescs"); - // As of this point, allow no more tydescs to be created. - ccx.finished_tydescs = true; - for ccx.tydescs.each |key, val| { - let glue_fn_ty = T_ptr(T_generic_glue_fn(ccx)); - let ti = val; - - // Each of the glue functions needs to be cast to a generic type - // before being put into the tydesc because we only have a singleton - // tydesc type. Then we'll recast each function to its real type when - // calling it. - let take_glue = - match copy ti.take_glue { - None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - Some(v) => { - ccx.stats.n_real_glues += 1u; - llvm::LLVMConstPointerCast(v, glue_fn_ty) - } - }; - let drop_glue = - match copy ti.drop_glue { - None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - Some(v) => { - ccx.stats.n_real_glues += 1u; - llvm::LLVMConstPointerCast(v, glue_fn_ty) - } - }; - let free_glue = - match copy ti.free_glue { - None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - Some(v) => { - ccx.stats.n_real_glues += 1u; - llvm::LLVMConstPointerCast(v, glue_fn_ty) - } - }; - let visit_glue = - match copy ti.visit_glue { - None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } - Some(v) => { - ccx.stats.n_real_glues += 1u; - llvm::LLVMConstPointerCast(v, glue_fn_ty) - } - }; - - let shape = shape_of(ccx, key); - let shape_tables = - llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables, - T_ptr(T_i8())); - - let tydesc = - C_named_struct(ccx.tydesc_type, - ~[ti.size, // size - ti.align, // align - take_glue, // take_glue - drop_glue, // drop_glue - free_glue, // free_glue - visit_glue, // visit_glue - C_shape(ccx, shape), // shape - shape_tables]); // shape_tables - - let gvar = ti.tydesc; - llvm::LLVMSetInitializer(gvar, tydesc); - llvm::LLVMSetGlobalConstant(gvar, True); - lib::llvm::SetLinkage(gvar, lib::llvm::InternalLinkage); - - // Index tydesc by addrspace. - if ti.addrspace > gc_box_addrspace { - let llty = T_ptr(ccx.tydesc_type); - let addrspace_name = #fmt("_gc_addrspace_metadata_%u", - ti.addrspace as uint); - let addrspace_gvar = str::as_c_str(addrspace_name, |buf| { - llvm::LLVMAddGlobal(ccx.llmod, llty, buf) - }); - lib::llvm::SetLinkage(addrspace_gvar, lib::llvm::InternalLinkage); - llvm::LLVMSetInitializer(addrspace_gvar, gvar); - } - }; -} - -fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) { - let _icx = bcx.insn_ctxt("make_take_glue"); - // NB: v is a *pointer* to type t here, not a direct value. - let bcx = match ty::get(t).struct { - ty::ty_box(_) | ty::ty_opaque_box | - ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => { - incr_refcnt_of_boxed(bcx, Load(bcx, v)); bcx - } - ty::ty_uniq(_) => { - let {bcx, val} = uniq::duplicate(bcx, Load(bcx, v), t); - Store(bcx, val, v); - bcx - } - ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => { - let {bcx, val} = tvec::duplicate_uniq(bcx, Load(bcx, v), t); - Store(bcx, val, v); - bcx - } - ty::ty_evec(_, ty::vstore_slice(_)) - | ty::ty_estr(ty::vstore_slice(_)) => { - bcx - } - ty::ty_fn(_) => { - closure::make_fn_glue(bcx, v, t, take_ty) - } - ty::ty_trait(_, _, _) => { - let llbox = Load(bcx, GEPi(bcx, v, [0u, 1u])); - incr_refcnt_of_boxed(bcx, llbox); - bcx - } - ty::ty_opaque_closure_ptr(ck) => { - closure::make_opaque_cbox_take_glue(bcx, ck, v) - } - _ if ty::type_is_structural(t) => { - iter_structural_ty(bcx, v, t, take_ty) - } - _ => bcx - }; - - build_return(bcx); -} - -fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) { - let _icx = cx.insn_ctxt("incr_refcnt_of_boxed"); - let ccx = cx.ccx(); - maybe_validate_box(cx, box_ptr); - let rc_ptr = GEPi(cx, box_ptr, [0u, abi::box_field_refcnt]); - let rc = Load(cx, rc_ptr); - let rc = Add(cx, rc, C_int(ccx, 1)); - Store(cx, rc, rc_ptr); -} - -fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) { - let _icx = bcx.insn_ctxt("make_visit_glue"); - let mut bcx = bcx; - let ty_visitor_name = special_idents::ty_visitor; - assert bcx.ccx().tcx.intrinsic_defs.contains_key(ty_visitor_name); - let (trait_id, ty) = bcx.ccx().tcx.intrinsic_defs.get(ty_visitor_name); - let v = PointerCast(bcx, v, T_ptr(type_of::type_of(bcx.ccx(), ty))); - bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, trait_id); - build_return(bcx); -} - - -fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { - // NB: v0 is an *alias* of type t here, not a direct value. - let _icx = bcx.insn_ctxt("make_free_glue"); - let ccx = bcx.ccx(); - let bcx = match ty::get(t).struct { - ty::ty_box(body_mt) => { - let v = Load(bcx, v); - let body = GEPi(bcx, v, [0u, abi::box_field_body]); - // Cast away the addrspace of the box pointer. - let body = PointerCast(bcx, body, T_ptr(type_of(ccx, body_mt.ty))); - let bcx = drop_ty(bcx, body, body_mt.ty); - trans_free(bcx, v) - } - ty::ty_opaque_box => { - let v = Load(bcx, v); - let td = Load(bcx, GEPi(bcx, v, [0u, abi::box_field_tydesc])); - let valptr = GEPi(bcx, v, [0u, abi::box_field_body]); - // Generate code that, dynamically, indexes into the - // tydesc and calls the drop glue that got set dynamically - call_tydesc_glue_full(bcx, valptr, td, abi::tydesc_field_drop_glue, - None); - trans_free(bcx, v) - } - ty::ty_uniq(*) => { - uniq::make_free_glue(bcx, v, t) - } - ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) | - ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => { - make_free_glue(bcx, v, - tvec::expand_boxed_vec_ty(bcx.tcx(), t)); - return; - } - ty::ty_fn(_) => { - closure::make_fn_glue(bcx, v, t, free_ty) - } - ty::ty_opaque_closure_ptr(ck) => { - closure::make_opaque_cbox_free_glue(bcx, ck, v) - } - ty::ty_class(did, ref substs) => { - // Call the dtor if there is one - do option::map_default(ty::ty_dtor(bcx.tcx(), did), bcx) |dt_id| { - trans_class_drop(bcx, v, dt_id, did, substs) - } - } - _ => bcx - }; - build_return(bcx); -} - -fn trans_class_drop(bcx: block, v0: ValueRef, dtor_did: ast::def_id, - class_did: ast::def_id, - substs: &ty::substs) -> block { - let drop_flag = GEPi(bcx, v0, [0u, 0u]); - do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| { - let mut bcx = cx; - // We have to cast v0 - let classptr = GEPi(bcx, v0, [0u, 1u]); - // Find and call the actual destructor - let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, class_did, substs.tps); - // The second argument is the "self" argument for drop - let params = lib::llvm::fn_ty_param_tys - (llvm::LLVMGetElementType - (llvm::LLVMTypeOf(dtor_addr))); - // Class dtors have no explicit args, so the params should just consist - // of the output pointer and the environment (self) - assert(params.len() == 2u); - let self_arg = PointerCast(bcx, v0, params[1u]); - let args = ~[bcx.fcx.llretptr, self_arg]; - Call(bcx, dtor_addr, args); - // Drop the fields - for vec::eachi(ty::class_items_as_mutable_fields(bcx.tcx(), class_did, - substs)) - |i, fld| { - let llfld_a = GEPi(bcx, classptr, [0u, i]); - bcx = drop_ty(bcx, llfld_a, fld.mt.ty); - } - Store(bcx, C_u8(0u), drop_flag); - bcx - } -} - - -fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { - // NB: v0 is an *alias* of type t here, not a direct value. - let _icx = bcx.insn_ctxt("make_drop_glue"); - let ccx = bcx.ccx(); - let bcx = match ty::get(t).struct { - ty::ty_box(_) | ty::ty_opaque_box | - ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) => { - decr_refcnt_maybe_free(bcx, Load(bcx, v0), t) - } - ty::ty_uniq(_) | - ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => { - free_ty(bcx, v0, t) - } - ty::ty_unboxed_vec(_) => { - tvec::make_drop_glue_unboxed(bcx, v0, t) - } - ty::ty_class(did, ref substs) => { - let tcx = bcx.tcx(); - match ty::ty_dtor(tcx, did) { - Some(dtor) => { - trans_class_drop(bcx, v0, dtor, did, substs) - } - None => { - // No dtor? Just the default case - iter_structural_ty(bcx, v0, t, drop_ty) - } - } - } - ty::ty_fn(_) => { - closure::make_fn_glue(bcx, v0, t, drop_ty) - } - ty::ty_trait(_, _, _) => { - let llbox = Load(bcx, GEPi(bcx, v0, [0u, 1u])); - decr_refcnt_maybe_free(bcx, llbox, ty::mk_opaque_box(ccx.tcx)) - } - ty::ty_opaque_closure_ptr(ck) => { - closure::make_opaque_cbox_drop_glue(bcx, ck, v0) - } - _ => { - if ty::type_needs_drop(ccx.tcx, t) && - ty::type_is_structural(t) { - iter_structural_ty(bcx, v0, t, drop_ty) - } else { bcx } - } - }; - build_return(bcx); -} fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id, parent_id: ast::def_id, substs: ~[ty::t]) @@ -919,10 +385,10 @@ fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id, let _icx = ccx.insn_ctxt("trans_res_dtor"); if (substs.len() > 0u) { let did = if did.crate != ast::local_crate { - maybe_instantiate_inline(ccx, did) + inline::maybe_instantiate_inline(ccx, did) } else { did }; assert did.crate == ast::local_crate; - monomorphic_fn(ccx, did, substs, None, None).val + monomorphize::monomorphic_fn(ccx, did, substs, None, None).val } else if did.crate == ast::local_crate { get_item_val(ccx, did.node) } else { @@ -936,31 +402,6 @@ fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id, } } -fn maybe_validate_box(_cx: block, _box_ptr: ValueRef) { - // Uncomment this when debugging annoying use-after-free - // bugs. But do not commit with this uncommented! Big performance hit. - - // let cx = _cx, box_ptr = _box_ptr; - // let ccx = cx.ccx(); - // warn_not_to_commit(ccx, "validate_box() is uncommented"); - // let raw_box_ptr = PointerCast(cx, box_ptr, T_ptr(T_i8())); - // Call(cx, ccx.upcalls.validate_box, ~[raw_box_ptr]); -} - -fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t) -> block { - let _icx = bcx.insn_ctxt("decr_refcnt_maybe_free"); - let ccx = bcx.ccx(); - maybe_validate_box(bcx, box_ptr); - - do with_cond(bcx, IsNotNull(bcx, box_ptr)) |bcx| { - let rc_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_refcnt]); - let rc = Sub(bcx, Load(bcx, rc_ptr), C_int(ccx, 1)); - Store(bcx, rc, rc_ptr); - let zero_test = ICmp(bcx, lib::llvm::IntEQ, C_int(ccx, 0), rc); - with_cond(bcx, zero_test, |bcx| free_ty_immediate(bcx, box_ptr, t)) - } -} - // Structural comparison: a rather involved form of glue. fn maybe_name_value(cx: @crate_ctxt, v: ValueRef, s: ~str) { if cx.sess.opts.save_temps { @@ -972,27 +413,28 @@ fn maybe_name_value(cx: @crate_ctxt, v: ValueRef, s: ~str) { // Used only for creating scalar comparison glue. enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, } - fn compare_scalar_types(cx: block, lhs: ValueRef, rhs: ValueRef, - t: ty::t, op: ast::binop) -> result { + t: ty::t, op: ast::binop) -> Result { let f = |a| compare_scalar_values(cx, lhs, rhs, a, op); match ty::get(t).struct { - ty::ty_nil => return rslt(cx, f(nil_type)), - ty::ty_bool | ty::ty_ptr(_) => return rslt(cx, f(unsigned_int)), - ty::ty_int(_) => return rslt(cx, f(signed_int)), - ty::ty_uint(_) => return rslt(cx, f(unsigned_int)), - ty::ty_float(_) => return rslt(cx, f(floating_point)), - ty::ty_type => { - return rslt(trans_fail(cx, None, - ~"attempt to compare values of type type"), - C_nil()); - } - _ => { - // Should never get here, because t is scalar. - cx.sess().bug(~"non-scalar type passed to \ - compare_scalar_types"); - } + ty::ty_nil => rslt(cx, f(nil_type)), + ty::ty_bool | ty::ty_ptr(_) => rslt(cx, f(unsigned_int)), + ty::ty_int(_) => rslt(cx, f(signed_int)), + ty::ty_uint(_) => rslt(cx, f(unsigned_int)), + ty::ty_float(_) => rslt(cx, f(floating_point)), + ty::ty_type => { + rslt( + controlflow::trans_fail( + cx, None, + ~"attempt to compare values of type type"), + C_nil()) + } + _ => { + // Should never get here, because t is scalar. + cx.sess().bug(~"non-scalar type passed to \ + compare_scalar_types") + } } } @@ -1102,11 +544,13 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, */ let mut cx = cx; match ty::get(t).struct { - ty::ty_rec(fields) => { - for vec::eachi(fields) |i, fld| { - let llfld_a = GEPi(cx, av, [0u, i]); - cx = f(cx, llfld_a, fld.mt.ty); - } + ty::ty_rec(*) | ty::ty_class(*) => { + do expr::with_field_tys(cx.tcx(), t) |_has_dtor, field_tys| { + for vec::eachi(field_tys) |i, field_ty| { + let llfld_a = GEPi(cx, av, struct_field(i)); + cx = f(cx, llfld_a, field_ty.mt.ty); + } + } } ty::ty_estr(ty::vstore_fixed(_)) | ty::ty_evec(_, ty::vstore_fixed(_)) => { @@ -1156,522 +600,30 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, } return next_cx; } - ty::ty_class(did, ref substs) => { - // Take the drop bit into account - let classptr = if is_some(ty::ty_dtor(cx.tcx(), did)) { - GEPi(cx, av, [0u, 1u]) - } - else { av }; - for vec::eachi(ty::class_items_as_mutable_fields(cx.tcx(), did, - substs)) - |i, fld| { - let llfld_a = GEPi(cx, classptr, [0u, i]); - cx = f(cx, llfld_a, fld.mt.ty); - } - } _ => cx.sess().unimpl(~"type in iter_structural_ty") } return cx; } -fn lazily_emit_all_tydesc_glue(ccx: @crate_ctxt, - static_ti: @tydesc_info) { - lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, static_ti); - lazily_emit_tydesc_glue(ccx, abi::tydesc_field_drop_glue, static_ti); - lazily_emit_tydesc_glue(ccx, abi::tydesc_field_free_glue, static_ti); - lazily_emit_tydesc_glue(ccx, abi::tydesc_field_visit_glue, static_ti); -} - -fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, - ti: @tydesc_info) { - let _icx = ccx.insn_ctxt("lazily_emit_tydesc_glue"); - let llfnty = type_of_glue_fn(ccx, ti.ty); - if field == abi::tydesc_field_take_glue { - match ti.take_glue { - Some(_) => (), - None => { - debug!("+++ lazily_emit_tydesc_glue TAKE %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"take"); - ti.take_glue = Some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, make_take_glue, ~"take"); - debug!("--- lazily_emit_tydesc_glue TAKE %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - } - } - } else if field == abi::tydesc_field_drop_glue { - match ti.drop_glue { - Some(_) => (), - None => { - debug!("+++ lazily_emit_tydesc_glue DROP %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"drop"); - ti.drop_glue = Some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, make_drop_glue, ~"drop"); - debug!("--- lazily_emit_tydesc_glue DROP %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - } - } - } else if field == abi::tydesc_field_free_glue { - match ti.free_glue { - Some(_) => (), - None => { - debug!("+++ lazily_emit_tydesc_glue FREE %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"free"); - ti.free_glue = Some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, make_free_glue, ~"free"); - debug!("--- lazily_emit_tydesc_glue FREE %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - } - } - } else if field == abi::tydesc_field_visit_glue { - match ti.visit_glue { - Some(_) => (), - None => { - debug!("+++ lazily_emit_tydesc_glue VISIT %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"visit"); - ti.visit_glue = Some(glue_fn); - make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, ~"visit"); - debug!("--- lazily_emit_tydesc_glue VISIT %s", - ppaux::ty_to_str(ccx.tcx, ti.ty)); - } - } +fn trans_compare(cx: block, op: ast::binop, lhs: ValueRef, + _lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> Result { + let _icx = cx.insn_ctxt("trans_compare"); + if ty::type_is_scalar(rhs_t) { + let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, op); + return rslt(rs.bcx, rs.val); } -} -// See [Note-arg-mode] -fn call_tydesc_glue_full(++bcx: block, v: ValueRef, tydesc: ValueRef, - field: uint, static_ti: Option<@tydesc_info>) { - let _icx = bcx.insn_ctxt("call_tydesc_glue_full"); - let ccx = bcx.ccx(); - // 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 = - ccx.sess.opts.debugging_opts & session::no_landing_pads != 0; - if bcx.unreachable && !no_lpads { return; } - - let static_glue_fn = match static_ti { - None => None, - Some(sti) => { - lazily_emit_tydesc_glue(ccx, field, sti); - if field == abi::tydesc_field_take_glue { - sti.take_glue - } else if field == abi::tydesc_field_drop_glue { - sti.drop_glue - } else if field == abi::tydesc_field_free_glue { - sti.free_glue - } else if field == abi::tydesc_field_visit_glue { - sti.visit_glue - } else { - None + // Determine the operation we need. + let llop = { + match op { + ast::eq | ast::ne => C_u8(abi::cmp_glue_op_eq), + ast::lt | ast::ge => C_u8(abi::cmp_glue_op_lt), + ast::le | ast::gt => C_u8(abi::cmp_glue_op_le), + _ => cx.tcx().sess.bug(~"trans_compare got non-comparison-op") } - } }; - // When available, use static type info to give glue the right type. - let static_glue_fn = match static_ti { - None => None, - Some(sti) => { - match static_glue_fn { - None => None, - Some(sgf) => Some( - PointerCast(bcx, sgf, T_ptr(type_of_glue_fn(ccx, sti.ty)))) - } - } - }; - - // When static type info is available, avoid casting parameter because the - // function already has the right type. Otherwise cast to generic pointer. - let llrawptr = if is_none(static_ti) || is_none(static_glue_fn) { - PointerCast(bcx, v, T_ptr(T_i8())) - } else { - v - }; - - let llfn = { - match static_glue_fn { - None => { - // Select out the glue function to call from the tydesc - let llfnptr = GEPi(bcx, tydesc, [0u, field]); - Load(bcx, llfnptr) - } - Some(sgf) => sgf - } - }; - - Call(bcx, llfn, ~[C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())), - C_null(T_ptr(T_ptr(bcx.ccx().tydesc_type))), llrawptr]); -} - -// See [Note-arg-mode] -fn call_tydesc_glue(++cx: block, v: ValueRef, t: ty::t, field: uint) - -> block { - let _icx = cx.insn_ctxt("call_tydesc_glue"); - let ti = get_tydesc(cx.ccx(), t); - call_tydesc_glue_full(cx, v, ti.tydesc, field, Some(ti)); - return cx; -} - -fn call_cmp_glue(bcx: block, lhs: ValueRef, rhs: ValueRef, t: ty::t, - llop: ValueRef) -> ValueRef { - // We can't use call_tydesc_glue_full() and friends here because compare - // glue has a special signature. - let _icx = bcx.insn_ctxt("call_cmp_glue"); - - let lllhs = spill_if_immediate(bcx, lhs, t); - let llrhs = spill_if_immediate(bcx, rhs, t); - - let llrawlhsptr = BitCast(bcx, lllhs, T_ptr(T_i8())); - let llrawrhsptr = BitCast(bcx, llrhs, T_ptr(T_i8())); - let lltydesc = get_tydesc_simple(bcx.ccx(), t); - - let llfn = bcx.ccx().upcalls.cmp_type; - - let llcmpresultptr = alloca(bcx, T_i1()); - Call(bcx, llfn, ~[llcmpresultptr, lltydesc, - llrawlhsptr, llrawrhsptr, llop]); - return Load(bcx, llcmpresultptr); -} - -fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block { - // NB: v is an *alias* of type t here, not a direct value. - let _icx = cx.insn_ctxt("take_ty"); - if ty::type_needs_drop(cx.tcx(), t) { - return call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue); - } - return cx; -} - -fn drop_ty(cx: block, v: ValueRef, t: ty::t) -> block { - // NB: v is an *alias* of type t here, not a direct value. - let _icx = cx.insn_ctxt("drop_ty"); - if ty::type_needs_drop(cx.tcx(), t) { - return call_tydesc_glue(cx, v, t, abi::tydesc_field_drop_glue); - } - return cx; -} - -fn drop_ty_root(bcx: block, v: ValueRef, rooted: bool, t: ty::t) -> block { - if rooted { - // NB: v is a raw ptr to an addrspace'd ptr to the value. - let v = PointerCast(bcx, Load(bcx, v), T_ptr(type_of(bcx.ccx(), t))); - drop_ty(bcx, v, t) - } else { - drop_ty(bcx, v, t) - } -} - -fn drop_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { - let _icx = bcx.insn_ctxt("drop_ty_immediate"); - match ty::get(t).struct { - ty::ty_uniq(_) | - ty::ty_evec(_, ty::vstore_uniq) | - ty::ty_estr(ty::vstore_uniq) => { - free_ty_immediate(bcx, v, t) - } - ty::ty_box(_) | ty::ty_opaque_box | - ty::ty_evec(_, ty::vstore_box) | - ty::ty_estr(ty::vstore_box) => { - decr_refcnt_maybe_free(bcx, v, t) - } - _ => bcx.tcx().sess.bug(~"drop_ty_immediate: non-box ty") - } -} - -fn take_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> result { - let _icx = bcx.insn_ctxt("take_ty_immediate"); - match ty::get(t).struct { - ty::ty_box(_) | ty::ty_opaque_box | - ty::ty_evec(_, ty::vstore_box) | - ty::ty_estr(ty::vstore_box) => { - incr_refcnt_of_boxed(bcx, v); - rslt(bcx, v) - } - ty::ty_uniq(_) => { - uniq::duplicate(bcx, v, t) - } - ty::ty_evec(_, ty::vstore_uniq) | - ty::ty_estr(ty::vstore_uniq) => { - tvec::duplicate_uniq(bcx, v, t) - } - _ => rslt(bcx, v) - } -} - -fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block { - // NB: v is an *alias* of type t here, not a direct value. - let _icx = cx.insn_ctxt("free_ty"); - if ty::type_needs_drop(cx.tcx(), t) { - return call_tydesc_glue(cx, v, t, abi::tydesc_field_free_glue); - } - return cx; -} - -fn free_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { - let _icx = bcx.insn_ctxt("free_ty_immediate"); - match ty::get(t).struct { - ty::ty_uniq(_) | - ty::ty_evec(_, ty::vstore_uniq) | - ty::ty_estr(ty::vstore_uniq) | - ty::ty_box(_) | ty::ty_opaque_box | - ty::ty_evec(_, ty::vstore_box) | - ty::ty_estr(ty::vstore_box) | - ty::ty_opaque_closure_ptr(_) => { - let vp = alloca_zeroed(bcx, type_of(bcx.ccx(), t)); - Store(bcx, v, vp); - free_ty(bcx, vp, t) - } - _ => bcx.tcx().sess.bug(~"free_ty_immediate: non-box ty") - } -} - -fn call_memmove(cx: block, dst: ValueRef, src: ValueRef, - n_bytes: ValueRef) { - // FIXME (Related to #1645, I think?): Provide LLVM with better - // alignment information when the alignment is statically known (it must - // be nothing more than a constant int, or LLVM complains -- not even a - // constant element of a tydesc works). - let _icx = cx.insn_ctxt("call_memmove"); - let ccx = cx.ccx(); - let key = match ccx.sess.targ_cfg.arch { - session::arch_x86 | session::arch_arm => ~"llvm.memmove.p0i8.p0i8.i32", - session::arch_x86_64 => ~"llvm.memmove.p0i8.p0i8.i64" - }; - let memmove = ccx.intrinsics.get(key); - let src_ptr = PointerCast(cx, src, T_ptr(T_i8())); - let dst_ptr = PointerCast(cx, dst, T_ptr(T_i8())); - let size = IntCast(cx, n_bytes, ccx.int_type); - let align = C_i32(1i32); - let volatile = C_bool(false); - Call(cx, memmove, ~[dst_ptr, src_ptr, size, align, volatile]); -} - -fn memmove_ty(bcx: block, dst: ValueRef, src: ValueRef, t: ty::t) { - let _icx = bcx.insn_ctxt("memmove_ty"); - let ccx = bcx.ccx(); - if ty::type_is_structural(t) { - let llsz = llsize_of(ccx, type_of(ccx, t)); - call_memmove(bcx, dst, src, llsz); - } else { - Store(bcx, Load(bcx, src), dst); - } -} - -enum copy_action { INIT, DROP_EXISTING, } - -impl copy_action : cmp::Eq { - pure fn eq(&&other: copy_action) -> bool { - match (self, other) { - (INIT, INIT) => true, - (DROP_EXISTING, DROP_EXISTING) => true, - (INIT, _) => false, - (DROP_EXISTING, _) => false, - } - } -} - -// These are the types that are passed by pointer. -fn type_is_structural_or_param(t: ty::t) -> bool { - if ty::type_is_structural(t) { return true; } - match ty::get(t).struct { - ty::ty_param(*) => return true, - _ => return false - } -} - -fn copy_val(cx: block, action: copy_action, dst: ValueRef, - src: ValueRef, t: ty::t) -> block { - let _icx = cx.insn_ctxt("copy_val"); - if action == DROP_EXISTING && - (type_is_structural_or_param(t) || - ty::type_is_unique(t)) { - let dstcmp = load_if_immediate(cx, dst, t); - let cast = PointerCast(cx, dstcmp, val_ty(src)); - // Self-copy check - do with_cond(cx, ICmp(cx, lib::llvm::IntNE, cast, src)) |bcx| { - copy_val_no_check(bcx, action, dst, src, t) - } - } else { - copy_val_no_check(cx, action, dst, src, t) - } -} - -fn copy_val_no_check(bcx: block, action: copy_action, dst: ValueRef, - src: ValueRef, t: ty::t) -> block { - let _icx = bcx.insn_ctxt("copy_val_no_check"); - let ccx = bcx.ccx(); - let mut bcx = bcx; - if ty::type_is_scalar(t) || ty::type_is_region_ptr(t) { - Store(bcx, src, dst); - return bcx; - } - if ty::type_is_nil(t) || ty::type_is_bot(t) { return bcx; } - if ty::type_is_boxed(t) || ty::type_is_unique(t) { - if action == DROP_EXISTING { bcx = drop_ty(bcx, dst, t); } - Store(bcx, src, dst); - return take_ty(bcx, dst, t); - } - if type_is_structural_or_param(t) { - if action == DROP_EXISTING { bcx = drop_ty(bcx, dst, t); } - memmove_ty(bcx, dst, src, t); - return take_ty(bcx, dst, t); - } - ccx.sess.bug(~"unexpected type in trans::copy_val_no_check: " + - ppaux::ty_to_str(ccx.tcx, t)); -} - - -// 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. -// FIXME (#839): We always zero out the source. Ideally we would detect the -// case where a variable is always deinitialized by block exit and thus -// doesn't need to be dropped. -fn move_val(cx: block, action: copy_action, dst: ValueRef, - src: lval_result, t: ty::t) -> block { - - let _icx = cx.insn_ctxt("move_val"); - let mut src_val = src.val; - let tcx = cx.tcx(); - let mut cx = cx; - if ty::type_is_scalar(t) || ty::type_is_region_ptr(t) { - if src.kind == lv_owned { src_val = Load(cx, src_val); } - Store(cx, src_val, dst); - return cx; - } else if ty::type_is_nil(t) || ty::type_is_bot(t) { - return cx; - } else if ty::type_is_boxed(t) || ty::type_is_unique(t) { - if src.kind == lv_owned { src_val = Load(cx, src_val); } - if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); } - Store(cx, src_val, dst); - if src.kind == lv_owned { return zero_mem(cx, src.val, t); } - // If we're here, it must be a temporary. - revoke_clean(cx, src_val); - return cx; - } else if type_is_structural_or_param(t) { - if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); } - memmove_ty(cx, dst, src_val, t); - if src.kind == lv_owned { return zero_mem(cx, src_val, t); } - // If we're here, it must be a temporary. - revoke_clean(cx, src_val); - return cx; - } - cx.sess().bug(~"unexpected type in trans::move_val: " + - ppaux::ty_to_str(tcx, t)); -} - -fn store_temp_expr(cx: block, action: copy_action, dst: ValueRef, - src: lval_result, t: ty::t, last_use: bool) - -> block { - let _icx = cx.insn_ctxt("trans_temp_expr"); - // Lvals in memory are not temporaries. Copy them. - if src.kind != lv_temporary && !last_use { - let v = if src.kind == lv_owned { - load_if_immediate(cx, src.val, t) - } else { - src.val - }; - return copy_val(cx, action, dst, v, t); - } - return move_val(cx, action, dst, src, t); -} - -fn trans_lit(cx: block, e: @ast::expr, lit: ast::lit, dest: dest) -> block { - let _icx = cx.insn_ctxt("trans_lit"); - if dest == ignore { return cx; } - match lit.node { - ast::lit_str(s) => tvec::trans_estr(cx, s, None, dest), - _ => store_in_dest(cx, consts::const_lit(cx.ccx(), e, lit), dest) - } -} - -fn trans_boxed_expr(bcx: block, contents: @ast::expr, - t: ty::t, heap: heap, - dest: dest) -> block { - let _icx = bcx.insn_ctxt("trans_boxed_expr"); - let {bcx, box, body} = malloc_general(bcx, t, heap); - add_clean_free(bcx, box, heap); - let bcx = trans_expr_save_in(bcx, contents, body); - revoke_clean(bcx, box); - return store_in_dest(bcx, box, dest); -} - -fn trans_unary(bcx: block, op: ast::unop, e: @ast::expr, - un_expr: @ast::expr, dest: dest) -> block { - let _icx = bcx.insn_ctxt("trans_unary"); - // Check for user-defined method call - match bcx.ccx().maps.method_map.find(un_expr.id) { - Some(mentry) => { - let fty = node_id_type(bcx, un_expr.callee_id); - return trans_call_inner( - bcx, un_expr.info(), fty, - expr_ty(bcx, un_expr), - |bcx| impl::trans_method_callee(bcx, un_expr.callee_id, e, - mentry), - arg_exprs(~[]), dest); - } - _ => () - } - - if dest == ignore { return trans_expr(bcx, e, ignore); } - let e_ty = expr_ty(bcx, e); - match op { - ast::not => { - let {bcx, val} = trans_temp_expr(bcx, e); - store_in_dest(bcx, Not(bcx, val), dest) - } - ast::neg => { - let {bcx, val} = trans_temp_expr(bcx, e); - let llneg = if ty::type_is_fp(e_ty) { - FNeg(bcx, val) - } else { Neg(bcx, val) }; - store_in_dest(bcx, llneg, dest) - } - ast::box(_) => { - trans_boxed_expr(bcx, e, e_ty, heap_shared, dest) - } - ast::uniq(_) => { - trans_boxed_expr(bcx, e, e_ty, heap_exchange, dest) - } - ast::deref => { - bcx.sess().bug(~"deref expressions should have been \ - translated using trans_lval(), not \ - trans_unary()") - } - } -} - -fn trans_addr_of(cx: block, e: @ast::expr, dest: dest) -> block { - let _icx = cx.insn_ctxt("trans_addr_of"); - let mut {bcx, val, kind} = trans_temp_lval(cx, e); - let ety = expr_ty(cx, e); - let is_immediate = ty::type_is_immediate(ety); - if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm { - val = do_spill(bcx, val, ety); - } - return store_in_dest(bcx, val, dest); -} - -fn trans_compare(cx: block, op: ast::binop, lhs: ValueRef, - _lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result { - let _icx = cx.insn_ctxt("trans_compare"); - if ty::type_is_scalar(rhs_t) { - let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, op); - return rslt(rs.bcx, rs.val); - } - - // Determine the operation we need. - let llop = { - match op { - ast::eq | ast::ne => C_u8(abi::cmp_glue_op_eq), - ast::lt | ast::ge => C_u8(abi::cmp_glue_op_lt), - ast::le | ast::gt => C_u8(abi::cmp_glue_op_le), - _ => cx.tcx().sess.bug(~"trans_compare got non-comparison-op") - } - }; - - let cmpval = call_cmp_glue(cx, lhs, rhs, rhs_t, llop); + let cmpval = glue::call_cmp_glue(cx, lhs, rhs, rhs_t, llop); // Invert the result if necessary. match op { @@ -1727,1718 +679,60 @@ fn fail_if_zero(cx: block, span: span, divmod: ast::binop, ~"modulo zero" }; let is_zero = match ty::get(rhs_t).struct { - ty::ty_int(t) => { - let zero = C_integral(T_int_ty(cx.ccx(), t), 0u64, False); - ICmp(cx, lib::llvm::IntEQ, rhs, zero) - } - ty::ty_uint(t) => { - let zero = C_integral(T_uint_ty(cx.ccx(), t), 0u64, False); - ICmp(cx, lib::llvm::IntEQ, rhs, zero) - } - _ => { - cx.tcx().sess.bug(~"fail-if-zero on unexpected type: " + - ty_to_str(cx.ccx().tcx, rhs_t)); - } - }; - do with_cond(cx, is_zero) |bcx| { - trans_fail(bcx, Some(span), text) - } -} - -// Important to get types for both lhs and rhs, because one might be _|_ -// and the other not. -fn trans_eager_binop(cx: block, span: span, op: ast::binop, lhs: ValueRef, - lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t, dest: dest) - -> block { - let mut cx = cx; - let _icx = cx.insn_ctxt("trans_eager_binop"); - if dest == ignore { return cx; } - let intype = { - if ty::type_is_bot(lhs_t) { rhs_t } - else { lhs_t } - }; - let is_float = ty::type_is_fp(intype); - - let rhs = cast_shift_expr_rhs(cx, op, lhs, rhs); - - let mut cx = cx; - let val = match op { - ast::add => { - if is_float { FAdd(cx, lhs, rhs) } - else { Add(cx, lhs, rhs) } - } - ast::subtract => { - if is_float { FSub(cx, lhs, rhs) } - else { Sub(cx, lhs, rhs) } - } - ast::mul => { - if is_float { FMul(cx, lhs, rhs) } - else { Mul(cx, lhs, rhs) } - } - ast::div => { - if is_float { - FDiv(cx, lhs, rhs) - } else { - // Only zero-check integers; fp /0 is NaN - cx = fail_if_zero(cx, span, op, rhs, rhs_t); - if ty::type_is_signed(intype) { - SDiv(cx, lhs, rhs) - } else { - UDiv(cx, lhs, rhs) - } - } - } - ast::rem => { - if is_float { - FRem(cx, lhs, rhs) - } else { - // Only zero-check integers; fp %0 is NaN - cx = fail_if_zero(cx, span, op, rhs, rhs_t); - if ty::type_is_signed(intype) { - SRem(cx, lhs, rhs) - } else { - URem(cx, lhs, rhs) - } - } - } - ast::bitor => Or(cx, lhs, rhs), - ast::bitand => And(cx, lhs, rhs), - ast::bitxor => Xor(cx, lhs, rhs), - ast::shl => Shl(cx, lhs, rhs), - ast::shr => { - if ty::type_is_signed(intype) { - AShr(cx, lhs, rhs) - } else { LShr(cx, lhs, rhs) } - } - _ => { - let cmpr = trans_compare(cx, op, lhs, lhs_t, rhs, rhs_t); - cx = cmpr.bcx; - cmpr.val - } - }; - return store_in_dest(cx, val, dest); -} - -fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop, - dst: @ast::expr, src: @ast::expr) -> block { - debug!("%s", expr_to_str(ex, bcx.tcx().sess.parse_sess.interner)); - let _icx = bcx.insn_ctxt("trans_assign_op"); - let t = expr_ty(bcx, src); - let lhs_res = trans_lval(bcx, dst); - assert (lhs_res.kind == lv_owned); - - // A user-defined operator method - match bcx.ccx().maps.method_map.find(ex.id) { - Some(origin) => { - let bcx = lhs_res.bcx; - debug!("user-defined method callee_id: %s", - ast_map::node_id_to_str(bcx.tcx().items, ex.callee_id, - bcx.sess().parse_sess.interner)); - let fty = node_id_type(bcx, ex.callee_id); - - let dty = expr_ty(bcx, dst); - let target = alloc_ty(bcx, dty); - - let bcx = trans_call_inner( - bcx, ex.info(), fty, - expr_ty(bcx, ex), - |bcx| { - // FIXME (#2528): provide the already-computed address, not - // the expr. - impl::trans_method_callee(bcx, ex.callee_id, dst, origin) - }, - arg_exprs(~[src]), save_in(target)); - - return move_val(bcx, DROP_EXISTING, lhs_res.val, - {bcx: bcx, val: target, kind: lv_owned}, - dty); - } - _ => () - } - - let {bcx, val: rhs_val} = trans_temp_expr(lhs_res.bcx, src); - return trans_eager_binop(bcx, ex.span, - op, Load(bcx, lhs_res.val), t, rhs_val, t, - save_in(lhs_res.val)); -} - -fn root_value(bcx: block, val: ValueRef, ty: ty::t, - scope_id: ast::node_id) { - let _icx = bcx.insn_ctxt("root_value"); - - if bcx.sess().trace() { - trans_trace( - bcx, None, - fmt!("preserving until end of scope %d", scope_id)); - } - - let root_loc = alloca_zeroed(bcx, type_of(bcx.ccx(), ty)); - copy_val(bcx, INIT, root_loc, val, ty); - add_root_cleanup(bcx, scope_id, root_loc, ty); -} - -// autoderefs the value `v`, either as many times as we can (if `max == -// uint::max_value`) or `max` times. -fn autoderef(cx: block, e_id: ast::node_id, - v: ValueRef, t: ty::t, - max: uint) -> result_t { - let _icx = cx.insn_ctxt("autoderef"); - let mut v1: ValueRef = v; - let mut t1: ty::t = t; - let ccx = cx.ccx(); - let mut derefs = 0u; - while derefs < max { - debug!("autoderef(e_id=%d, v1=%s, t1=%s, derefs=%u)", - e_id, val_str(ccx.tn, v1), ppaux::ty_to_str(ccx.tcx, t1), - derefs); - - // root the autoderef'd value, if necessary: - derefs += 1u; - match ccx.maps.root_map.find({id:e_id, derefs:derefs}) { - None => (), - Some(scope_id) => { - root_value(cx, v1, t1, scope_id); - } - } - - match ty::get(t1).struct { - ty::ty_box(mt) => { - let body = GEPi(cx, v1, [0u, abi::box_field_body]); - t1 = mt.ty; - - // Since we're changing levels of box indirection, we may have - // to cast this pointer, since statically-sized enum types have - // different types depending on whether they're behind a box - // or not. - let llty = type_of(ccx, t1); - v1 = PointerCast(cx, body, T_ptr(llty)); - } - ty::ty_uniq(_) => { - let derefed = uniq::autoderef(cx, v1, t1); - t1 = derefed.t; - v1 = derefed.v; - } - ty::ty_rptr(_, mt) => { - t1 = mt.ty; - v1 = v; - } - ty::ty_enum(did, ref substs) => { - let variants = ty::enum_variants(ccx.tcx, did); - if (*variants).len() != 1u || variants[0].args.len() != 1u { - break; - } - t1 = ty::subst(ccx.tcx, substs, variants[0].args[0]); - v1 = PointerCast(cx, v1, T_ptr(type_of(ccx, t1))); - } - _ => break - } - v1 = load_if_immediate(cx, v1, t1); - } - - // 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; - - return {bcx: cx, val: v1, ty: t1}; -} - -// refinement types would obviate the need for this -enum lazy_binop_ty { lazy_and, lazy_or } - -fn trans_lazy_binop(bcx: block, op: lazy_binop_ty, a: @ast::expr, - b: @ast::expr, dest: dest) -> block { - let _icx = bcx.insn_ctxt("trans_lazy_binop"); - let {bcx: past_lhs, val: lhs} = { - do with_scope_result(bcx, a.info(), ~"lhs") |bcx| { - trans_temp_expr(bcx, a) - } - }; - if past_lhs.unreachable { return past_lhs; } - let join = sub_block(bcx, ~"join"), before_rhs = sub_block(bcx, ~"rhs"); - - match op { - lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb), - lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb) - } - let {bcx: past_rhs, val: rhs} = { - do with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| { - trans_temp_expr(bcx, b) - } - }; - - if past_rhs.unreachable { return store_in_dest(join, lhs, dest); } - Br(past_rhs, join.llbb); - let phi = - Phi(join, T_bool(), ~[lhs, rhs], ~[past_lhs.llbb, past_rhs.llbb]); - return store_in_dest(join, phi, dest); -} - -fn trans_binary(bcx: block, op: ast::binop, lhs: @ast::expr, - rhs: @ast::expr, dest: dest, ex: @ast::expr) -> block { - let _icx = bcx.insn_ctxt("trans_binary"); - // User-defined operators - match bcx.ccx().maps.method_map.find(ex.id) { - Some(origin) => { - let fty = node_id_type(bcx, ex.callee_id); - return trans_call_inner( - bcx, ex.info(), fty, - expr_ty(bcx, ex), - |bcx| { - impl::trans_method_callee(bcx, ex.callee_id, lhs, origin) - }, - arg_exprs(~[rhs]), dest); - } - _ => () - } - - // First couple cases are lazy: - match op { - ast::and => { - return trans_lazy_binop(bcx, lazy_and, lhs, rhs, dest); - } - ast::or => { - return trans_lazy_binop(bcx, lazy_or, lhs, rhs, dest); - } - _ => { - // Remaining cases are eager: - let lhs_res = trans_temp_expr(bcx, lhs); - let rhs_res = trans_temp_expr(lhs_res.bcx, rhs); - return trans_eager_binop(rhs_res.bcx, ex.span, - op, lhs_res.val, - expr_ty(bcx, lhs), rhs_res.val, - expr_ty(bcx, rhs), dest); - } - } -} - -fn trans_if(cx: block, cond: @ast::expr, thn: ast::blk, - els: Option<@ast::expr>, dest: dest) - -> block { - let _icx = cx.insn_ctxt("trans_if"); - let {bcx, val: cond_val} = trans_temp_expr(cx, cond); - - let then_dest = dup_for_join(dest); - let else_dest = dup_for_join(dest); - let then_cx = scope_block(bcx, thn.info(), ~"then"); - let else_cx = scope_block(bcx, els.info(), ~"else"); - CondBr(bcx, cond_val, then_cx.llbb, else_cx.llbb); - let then_bcx = trans_block(then_cx, thn, then_dest); - let then_bcx = trans_block_cleanups(then_bcx, block_cleanups(then_cx)); - // 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 = match els { - Some(elexpr) => { - match elexpr.node { - ast::expr_if(_, _, _) => { - let elseif_blk = ast_util::block_from_expr(elexpr); - trans_block(else_cx, elseif_blk, else_dest) - } - ast::expr_block(blk) => { - trans_block(else_cx, blk, else_dest) - } - // would be nice to have a constraint on ifs - _ => cx.tcx().sess.bug(~"strange alternative in if") - } - } - _ => else_cx - }; - let else_bcx = trans_block_cleanups(else_bcx, block_cleanups(else_cx)); - return join_returns(cx, - ~[then_bcx, else_bcx], ~[then_dest, else_dest], dest); -} - -fn trans_while(cx: block, cond: @ast::expr, body: ast::blk) - -> block { - let _icx = cx.insn_ctxt("trans_while"); - let next_cx = sub_block(cx, ~"while next"); - let loop_cx = loop_scope_block(cx, next_cx, ~"`while`", body.info()); - let cond_cx = scope_block(loop_cx, cond.info(), ~"while loop cond"); - let body_cx = scope_block(loop_cx, body.info(), ~"while loop body"); - Br(cx, loop_cx.llbb); - Br(loop_cx, cond_cx.llbb); - let cond_res = trans_temp_expr(cond_cx, cond); - let cond_bcx = trans_block_cleanups(cond_res.bcx, - block_cleanups(cond_cx)); - CondBr(cond_bcx, cond_res.val, body_cx.llbb, next_cx.llbb); - let body_end = trans_block(body_cx, body, ignore); - cleanup_and_Br(body_end, body_cx, cond_cx.llbb); - return next_cx; -} - -fn trans_loop(cx:block, body: ast::blk) -> block { - let _icx = cx.insn_ctxt("trans_loop"); - let next_cx = sub_block(cx, ~"next"); - let body_cx = loop_scope_block(cx, next_cx, ~"`loop`", body.info()); - let body_end = trans_block(body_cx, body, ignore); - cleanup_and_Br(body_end, body_cx, body_cx.llbb); - Br(cx, body_cx.llbb); - return next_cx; -} - -enum lval_kind { - lv_temporary, //< Temporary value passed by value if of immediate type - lv_owned, //< Non-temporary value passed by pointer - lv_owned_imm, //< Non-temporary value passed by value -} - -impl lval_kind : cmp::Eq { - pure fn eq(&&other: lval_kind) -> bool { - match (self, other) { - (lv_temporary, lv_temporary) => true, - (lv_owned, lv_owned) => true, - (lv_owned_imm, lv_owned_imm) => true, - (lv_temporary, _) => false, - (lv_owned, _) => false, - (lv_owned_imm, _) => false, - } - } -} - -type local_var_result = {val: ValueRef, kind: lval_kind}; -type lval_result = {bcx: block, val: ValueRef, kind: lval_kind}; -enum callee_env { - null_env, - is_closure, - self_env(ValueRef, ty::t, Option, ast::rmode), -} -type lval_maybe_callee = {bcx: block, - val: ValueRef, - kind: lval_kind, - env: callee_env}; - -fn null_env_ptr(bcx: block) -> ValueRef { - C_null(T_opaque_box_ptr(bcx.ccx())) -} - -fn lval_from_local_var(bcx: block, r: local_var_result) -> lval_result { - return { bcx: bcx, val: r.val, kind: r.kind }; -} - -fn lval_owned(bcx: block, val: ValueRef) -> lval_result { - return {bcx: bcx, val: val, kind: lv_owned}; -} -fn lval_temp(bcx: block, val: ValueRef) -> lval_result { - return {bcx: bcx, val: val, kind: lv_temporary}; -} - -fn lval_no_env(bcx: block, val: ValueRef, kind: lval_kind) - -> lval_maybe_callee { - return {bcx: bcx, val: val, kind: kind, env: is_closure}; -} - -fn trans_external_path(ccx: @crate_ctxt, did: ast::def_id, t: ty::t) - -> ValueRef { - let name = csearch::get_symbol(ccx.sess.cstore, did); - match ty::get(t).struct { - ty::ty_fn(_) => { - let llty = type_of_fn_from_ty(ccx, t); - return get_extern_fn(ccx.externs, ccx.llmod, name, - lib::llvm::CCallConv, llty); - } - _ => { - let llty = type_of(ccx, t); - return get_extern_const(ccx.externs, ccx.llmod, name, llty); - } - }; -} - -fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option { - // FIXME[mono] could do this recursively. is that worthwhile? (#2529) - match ty::get(ty).struct { - ty::ty_box(*) => { - Some(ty::mk_opaque_box(tcx)) - } - ty::ty_fn(ref fty) => { - Some(ty::mk_fn(tcx, {purity: ast::impure_fn, - proto: fty.proto, - bounds: @~[], - inputs: ~[], - output: ty::mk_nil(tcx), - ret_style: ast::return_val})) - } - ty::ty_trait(_, _, _) => { - Some(ty::mk_fn(tcx, {purity: ast::impure_fn, - proto: ty::proto_vstore(ty::vstore_box), - bounds: @~[], - inputs: ~[], - output: ty::mk_nil(tcx), - ret_style: ast::return_val})) - } - ty::ty_ptr(_) => Some(ty::mk_uint(tcx)), - _ => None - } -} - -fn make_mono_id(ccx: @crate_ctxt, item: ast::def_id, substs: ~[ty::t], - vtables: Option, - param_uses: Option<~[type_use::type_uses]>) -> mono_id { - let precise_param_ids = match vtables { - Some(vts) => { - let bounds = ty::lookup_item_type(ccx.tcx, item).bounds; - let mut i = 0u; - vec::map2(*bounds, substs, |bounds, subst| { - let mut v = ~[]; - for vec::each(*bounds) |bound| { - match bound { - ty::bound_trait(_) => { - vec::push(v, impl::vtable_id(ccx, vts[i])); - i += 1u; - } - _ => () - } - } - (subst, if v.len() > 0u { Some(v) } else { None }) - }) - } - None => { - vec::map(substs, |subst| (subst, None)) - } - }; - let param_ids = match param_uses { - Some(uses) => { - vec::map2(precise_param_ids, uses, |id, uses| { - match id { - (a, b@Some(_)) => mono_precise(a, b), - (subst, None) => { - if uses == 0u { mono_any } - else if uses == type_use::use_repr && - !ty::type_needs_drop(ccx.tcx, subst) { - let llty = type_of(ccx, subst); - let size = shape::llsize_of_real(ccx, llty); - let align = shape::llalign_of_pref(ccx, llty); - // Special value for nil to prevent problems with undef - // return pointers. - if size == 1u && ty::type_is_nil(subst) { - mono_repr(0u, 0u) - } else { mono_repr(size, align) } - } else { mono_precise(subst, None) } - } - } - }) - } - None => precise_param_ids.map(|x| { let (a, b) = x; - mono_precise(a, b) }) - }; - @{def: item, params: param_ids} -} - -fn monomorphic_fn(ccx: @crate_ctxt, fn_id: ast::def_id, - real_substs: ~[ty::t], - vtables: Option, - ref_id: Option) - -> {val: ValueRef, must_cast: bool} { - let _icx = ccx.insn_ctxt("monomorphic_fn"); - let mut must_cast = false; - let substs = vec::map(real_substs, |t| { - match normalize_for_monomorphization(ccx.tcx, t) { - Some(t) => { must_cast = true; t } - None => t - } - }); - - for real_substs.each() |s| { assert !ty::type_has_params(s); } - for substs.each() |s| { assert !ty::type_has_params(s); } - let param_uses = type_use::type_uses_for(ccx, fn_id, substs.len()); - let hash_id = make_mono_id(ccx, fn_id, substs, vtables, Some(param_uses)); - if vec::any(hash_id.params, - |p| match p { mono_precise(_, _) => false, _ => true }) { - must_cast = true; - } - - #debug["monomorphic_fn(fn_id=%? (%s), real_substs=%?, substs=%?, \ - hash_id = %?", - fn_id, ty::item_path_str(ccx.tcx, fn_id), - real_substs.map(|s| ty_to_str(ccx.tcx, s)), - substs.map(|s| ty_to_str(ccx.tcx, s)), hash_id]; - - match ccx.monomorphized.find(hash_id) { - Some(val) => { - debug!("leaving monomorphic fn %s", - ty::item_path_str(ccx.tcx, fn_id)); - return {val: val, must_cast: must_cast}; - } - None => () - } - - let tpt = ty::lookup_item_type(ccx.tcx, fn_id); - let mut llitem_ty = tpt.ty; - - let map_node = session::expect(ccx.sess, ccx.tcx.items.find(fn_id.node), - || fmt!("While monomorphizing %?, couldn't find it in the item map \ - (may have attempted to monomorphize an item defined in a different \ - crate?)", fn_id)); - // Get the path so that we can create a symbol - let (pt, name, span) = match map_node { - ast_map::node_item(i, pt) => (pt, i.ident, i.span), - ast_map::node_variant(v, enm, pt) => (pt, v.node.name, enm.span), - ast_map::node_method(m, _, pt) => (pt, m.ident, m.span), - ast_map::node_foreign_item(i, ast::foreign_abi_rust_intrinsic, pt) - => (pt, i.ident, i.span), - ast_map::node_foreign_item(*) => { - // Foreign externs don't have to be monomorphized. - return {val: get_item_val(ccx, fn_id.node), - must_cast: true}; - } - ast_map::node_ctor(nm, _, ct, _, pt) => (pt, nm, ct.span), - ast_map::node_dtor(_, dtor, _, pt) => - (pt, special_idents::dtor, dtor.span), - ast_map::node_trait_method(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize a trait method") - } - ast_map::node_expr(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize an expr") - } - ast_map::node_stmt(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize a stmt") - } - ast_map::node_export(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize an export") - } - ast_map::node_arg(*) => ccx.tcx.sess.bug(~"Can't monomorphize an arg"), - ast_map::node_block(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize a block") - } - ast_map::node_local(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize a local") - } - }; - let mono_ty = ty::subst_tps(ccx.tcx, substs, llitem_ty); - let llfty = type_of_fn_from_ty(ccx, mono_ty); - - let depth = option::get_default(ccx.monomorphizing.find(fn_id), 0u); - // Random cut-off -- code that needs to instantiate the same function - // recursively more than ten times can probably safely be assumed to be - // causing an infinite expansion. - if depth > 10u { - ccx.sess.span_fatal( - span, ~"overly deep expansion of inlined function"); - } - ccx.monomorphizing.insert(fn_id, depth + 1u); - - let pt = vec::append(*pt, - ~[path_name(ccx.names(ccx.sess.str_of(name)))]); - let s = mangle_exported_name(ccx, pt, mono_ty); - - let mk_lldecl = || { - let lldecl = decl_internal_cdecl_fn(ccx.llmod, s, llfty); - ccx.monomorphized.insert(hash_id, lldecl); - lldecl - }; - - let psubsts = Some({tys: substs, vtables: vtables, bounds: tpt.bounds}); - let lldecl = match map_node { - ast_map::node_item(i@@{node: ast::item_fn(decl, _, _, body), _}, _) => { - let d = mk_lldecl(); - set_inline_hint_if_appr(i.attrs, d); - trans_fn(ccx, pt, decl, body, d, no_self, psubsts, fn_id.node); - d - } - ast_map::node_item(*) => { - ccx.tcx.sess.bug(~"Can't monomorphize this kind of item") - } - ast_map::node_foreign_item(i, _, _) => { - let d = mk_lldecl(); - foreign::trans_intrinsic(ccx, d, i, pt, option::get(psubsts), - ref_id); - d - } - ast_map::node_variant(v, enum_item, _) => { - let tvs = ty::enum_variants(ccx.tcx, local_def(enum_item.id)); - let this_tv = option::get(vec::find(*tvs, |tv| { - tv.id.node == fn_id.node})); - let d = mk_lldecl(); - set_inline_hint(d); - match v.node.kind { - ast::tuple_variant_kind(args) => { - trans_enum_variant(ccx, enum_item.id, v, args, - this_tv.disr_val, (*tvs).len() == 1u, - psubsts, d); - } - ast::struct_variant_kind(_) => - ccx.tcx.sess.bug(~"can't monomorphize struct variants"), - ast::enum_variant_kind(_) => - ccx.tcx.sess.bug(~"can't monomorphize enum variants") - } - d - } - ast_map::node_method(mth, _, _) => { - let d = mk_lldecl(); - set_inline_hint_if_appr(mth.attrs, d); - impl::trans_method(ccx, pt, mth, psubsts, d); - d - } - ast_map::node_ctor(_, tps, ctor, parent_id, _) => { - // ctors don't have attrs, at least not right now - let d = mk_lldecl(); - let tp_tys = ty::ty_params_to_tys(ccx.tcx, tps); - trans_class_ctor(ccx, pt, ctor.node.dec, ctor.node.body, d, - option::get_default(psubsts, - {tys:tp_tys, vtables: None, bounds: @~[]}), - fn_id.node, parent_id, ctor.span); - d - } - ast_map::node_dtor(_, dtor, _, pt) => { - let parent_id = match ty::ty_to_def_id(ty::node_id_to_type(ccx.tcx, - dtor.node.self_id)) { - Some(did) => did, - None => ccx.sess.span_bug(dtor.span, ~"Bad self ty in \ - dtor") - }; - trans_class_dtor(ccx, *pt, dtor.node.body, - dtor.node.id, psubsts, Some(hash_id), parent_id) - } - // Ugh -- but this ensures any new variants won't be forgotten - ast_map::node_expr(*) | - ast_map::node_stmt(*) | - ast_map::node_trait_method(*) | - ast_map::node_export(*) | - ast_map::node_arg(*) | - ast_map::node_block(*) | - ast_map::node_local(*) => { - ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node)) - } - }; - ccx.monomorphizing.insert(fn_id, depth); - - debug!("leaving monomorphic fn %s", ty::item_path_str(ccx.tcx, fn_id)); - {val: lldecl, must_cast: must_cast} -} - -fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id) - -> ast::def_id { - let _icx = ccx.insn_ctxt("maybe_instantiate_inline"); - match ccx.external.find(fn_id) { - Some(Some(node_id)) => { - // Already inline - debug!("maybe_instantiate_inline(%s): already inline as node id %d", - ty::item_path_str(ccx.tcx, fn_id), node_id); - local_def(node_id) - } - Some(None) => fn_id, // Not inlinable - None => { // Not seen yet - match csearch::maybe_get_item_ast( - ccx.tcx, fn_id, - |a,b,c,d| { - astencode::decode_inlined_item(a, b, ccx.maps, c, d) - }) { - - csearch::not_found => { - ccx.external.insert(fn_id, None); - fn_id - } - csearch::found(ast::ii_item(item)) => { - ccx.external.insert(fn_id, Some(item.id)); - trans_item(ccx, *item); - local_def(item.id) - } - csearch::found(ast::ii_ctor(ctor, _, _, _)) => { - ccx.external.insert(fn_id, Some(ctor.node.id)); - local_def(ctor.node.id) - } - csearch::found(ast::ii_foreign(item)) => { - ccx.external.insert(fn_id, Some(item.id)); - local_def(item.id) - } - csearch::found_parent(parent_id, ast::ii_item(item)) => { - ccx.external.insert(parent_id, Some(item.id)); - let mut my_id = 0; - match item.node { - ast::item_enum(_, _) => { - let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id)); - let vs_there = ty::enum_variants(ccx.tcx, parent_id); - do vec::iter2(*vs_here, *vs_there) |here, there| { - if there.id == fn_id { my_id = here.id.node; } - ccx.external.insert(there.id, Some(here.id.node)); - } - } - _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \ - non-enum parent") - } - trans_item(ccx, *item); - local_def(my_id) - } - csearch::found_parent(_, _) => { - ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \ - with a non-item parent"); - } - csearch::found(ast::ii_method(impl_did, mth)) => { - ccx.external.insert(fn_id, Some(mth.id)); - let {bounds: impl_bnds, region_param: _, ty: impl_ty} = - ty::lookup_item_type(ccx.tcx, impl_did); - if (*impl_bnds).len() + mth.tps.len() == 0u { - let llfn = get_item_val(ccx, mth.id); - let path = vec::append( - ty::item_path(ccx.tcx, impl_did), - ~[path_name(mth.ident)]); - trans_fn(ccx, path, mth.decl, mth.body, - llfn, impl_self(impl_ty), None, mth.id); - } - local_def(mth.id) - } - csearch::found(ast::ii_dtor(dtor, _, _, _)) => { - ccx.external.insert(fn_id, Some(dtor.node.id)); - local_def(dtor.node.id) - } - } - } - } -} - -fn lval_static_fn(bcx: block, fn_id: ast::def_id, id: ast::node_id) - -> lval_maybe_callee { - let _icx = bcx.insn_ctxt("lval_static_fn"); - let vts = option::map(bcx.ccx().maps.vtable_map.find(id), |vts| { - impl::resolve_vtables_in_fn_ctxt(bcx.fcx, vts) - }); - lval_static_fn_inner(bcx, fn_id, id, node_id_type_params(bcx, id), vts) -} - -fn lval_static_fn_inner(bcx: block, fn_id: ast::def_id, id: ast::node_id, - tys: ~[ty::t], vtables: Option) - -> lval_maybe_callee { - let _icx = bcx.insn_ctxt("lval_static_fn_inner"); - let ccx = bcx.ccx(), tcx = ccx.tcx; - let tpt = ty::lookup_item_type(tcx, fn_id); - - // Check whether this fn has an inlined copy and, if so, redirect fn_id to - // the local id of the inlined copy. - let fn_id = if fn_id.crate != ast::local_crate { - maybe_instantiate_inline(ccx, fn_id) - } else { fn_id }; - - let must_monomorphise = { - let local_with_type_params = - fn_id.crate == ast::local_crate && tys.len() > 0u; - - // Intrinsic functions should always be monomorphised. In particular, - // if we see an intrinsic that is inlined from a different crate, we - // want to reemit the intrinsic instead of trying to call it in the - // other crate. - let rust_intrinsic = if fn_id.crate == ast::local_crate { - - let map_node = session::expect( - ccx.sess, - ccx.tcx.items.find(fn_id.node), - || fmt!("local item should be in ast map")); - - match map_node { - ast_map::node_foreign_item( - _, ast::foreign_abi_rust_intrinsic, _) => true, - _ => false - } - } else { - false - }; - - local_with_type_params || rust_intrinsic - }; - - if must_monomorphise { - let mut {val, must_cast} = - monomorphic_fn(ccx, fn_id, tys, vtables, Some(id)); - if must_cast { - val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty( - ccx, node_id_type(bcx, id)))); - } - return {bcx: bcx, val: val, kind: lv_owned, env: null_env}; - } - - let mut val = if fn_id.crate == ast::local_crate { - // Internal reference. - get_item_val(ccx, fn_id.node) - } else { - // External reference. - trans_external_path(ccx, fn_id, tpt.ty) - }; - if tys.len() > 0u { - val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty( - ccx, node_id_type(bcx, id)))); - } - - match ty::get(tpt.ty).struct { - ty::ty_fn(fn_ty) => { - match fn_ty.purity { - ast::extern_fn => { - // Extern functions are just opaque pointers - let val = PointerCast(bcx, val, T_ptr(T_i8())); - return lval_no_env(bcx, val, lv_owned_imm); - } - _ => { /* fall through */ } - } - } - _ => { /* fall through */ } - } - - return {bcx: bcx, val: val, kind: lv_owned, env: null_env}; -} - -fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef { - let _icx = ccx.insn_ctxt("lookup_discriminant"); - match ccx.discrims.find(vid) { - None => { - // It's an external discriminant that we haven't seen yet. - assert (vid.crate != ast::local_crate); - let sym = csearch::get_symbol(ccx.sess.cstore, vid); - let gvar = str::as_c_str(sym, |buf| { - llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) - }); - lib::llvm::SetLinkage(gvar, lib::llvm::ExternalLinkage); - llvm::LLVMSetGlobalConstant(gvar, True); - ccx.discrims.insert(vid, gvar); - return gvar; - } - Some(llval) => return llval, - } -} - -// This shouldn't exist. We should cast self *once*, but right now this -// conflicts with default methods. (FIXME: #2794) -fn cast_self(cx: block, slf: val_self_data) -> ValueRef { - PointerCast(cx, slf.v, T_ptr(type_of(cx.ccx(), slf.t))) -} - -fn trans_local_var(cx: block, def: ast::def) -> local_var_result { - let _icx = cx.insn_ctxt("trans_local_var"); - fn take_local(table: hashmap, - id: ast::node_id) -> local_var_result { - match table.find(id) { - Some(local_mem(v)) => {val: v, kind: lv_owned}, - Some(local_imm(v)) => {val: v, kind: lv_owned_imm}, - None => fail(fmt!("take_local: internal error, \ - found no entry for %?", id)) - } - } - match def { - ast::def_upvar(nid, _, _, _) => { - assert (cx.fcx.llupvars.contains_key(nid)); - return { val: cx.fcx.llupvars.get(nid), kind: lv_owned }; - } - ast::def_arg(nid, _) => { - assert (cx.fcx.llargs.contains_key(nid)); - return take_local(cx.fcx.llargs, nid); - } - ast::def_local(nid, _) | ast::def_binding(nid, _) => { - assert (cx.fcx.lllocals.contains_key(nid)); - return take_local(cx.fcx.lllocals, nid); - } - ast::def_self(_) => { - let slf = match copy cx.fcx.llself { - Some(s) => cast_self(cx, s), - None => cx.sess().bug(~"trans_local_var: reference to self \ - out of context") - }; - return {val: slf, kind: lv_owned}; - } - _ => { - cx.sess().unimpl(fmt!("unsupported def type in trans_local_var: %?", - def)); - } - } -} - -fn trans_path(cx: block, id: ast::node_id) - -> lval_maybe_callee { - let _icx = cx.insn_ctxt("trans_path"); - match cx.tcx().def_map.find(id) { - None => cx.sess().bug(~"trans_path: unbound node ID"), - Some(df) => { - return trans_var(cx, df, id); - } - } -} - -fn trans_var(cx: block, def: ast::def, id: ast::node_id)-> lval_maybe_callee { - let _icx = cx.insn_ctxt("trans_var"); - let ccx = cx.ccx(); - match def { - ast::def_fn(did, _) => { - return lval_static_fn(cx, did, id); - } - ast::def_static_method(did, _) => { - return impl::trans_static_method_callee(cx, did, id); - } - ast::def_variant(tid, vid) => { - if ty::enum_variant_with_id(ccx.tcx, tid, vid).args.len() > 0u { - // N-ary variant. - return lval_static_fn(cx, vid, id); - } else { - // Nullary variant. - let enum_ty = node_id_type(cx, id); - let llenumptr = alloc_ty(cx, enum_ty); - let lldiscrimptr = GEPi(cx, llenumptr, [0u, 0u]); - let lldiscrim_gv = lookup_discriminant(ccx, vid); - let lldiscrim = Load(cx, lldiscrim_gv); - Store(cx, lldiscrim, lldiscrimptr); - return lval_no_env(cx, llenumptr, lv_temporary); - } - } - ast::def_const(did) => { - if did.crate == ast::local_crate { - return lval_no_env(cx, get_item_val(ccx, did.node), lv_owned); - } else { - let tp = node_id_type(cx, id); - let val = trans_external_path(ccx, did, tp); - return lval_no_env(cx, load_if_immediate(cx, val, tp), - lv_owned_imm); - } - } - _ => { - let loc = trans_local_var(cx, def); - return lval_no_env(cx, loc.val, loc.kind); - } - } -} - -fn trans_rec_field(bcx: block, base: @ast::expr, - field: ast::ident) -> lval_result { - let _icx = bcx.insn_ctxt("trans_rec_field"); - let {bcx, val} = trans_temp_expr(bcx, base); - let {bcx, val, ty} = - autoderef(bcx, base.id, val, expr_ty(bcx, base), - uint::max_value); - trans_rec_field_inner(bcx, val, ty, field, base.span) -} - -fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t, - field: ast::ident, sp: span) -> lval_result { - let mut llderef = false; - let fields = match ty::get(ty).struct { - ty::ty_rec(fs) => fs, - ty::ty_class(did, ref substs) => { - if option::is_some(ty::ty_dtor(bcx.tcx(), did)) { - llderef = true; - } - ty::class_items_as_mutable_fields(bcx.tcx(), did, substs) - } - // Constraint? - _ => bcx.tcx().sess.span_bug(sp, ~"trans_rec_field:\ - base expr has non-record type") - }; - // seems wrong? Doesn't take into account the field - // sizes - - let ix = field_idx_strict(bcx.tcx(), sp, field, fields); - - debug!("val = %s ix = %u", bcx.val_str(val), ix); - - /* self is a class with a dtor, which means we - have to select out the object itself - (If any other code does the same thing, that's - a bug */ - let val = if llderef { - GEPi(bcx, GEPi(bcx, val, [0u, 1u]), [0u, ix]) - } - else { GEPi(bcx, val, [0u, ix]) }; - - return {bcx: bcx, val: val, kind: lv_owned}; -} - - -fn trans_index(cx: block, ex: @ast::expr, base: @ast::expr, - idx: @ast::expr) -> lval_result { - let _icx = cx.insn_ctxt("trans_index"); - let base_ty = expr_ty(cx, base); - let exp = trans_temp_expr(cx, base); - let lv = autoderef(exp.bcx, base.id, exp.val, base_ty, uint::max_value); - let ix = trans_temp_expr(lv.bcx, idx); - let v = lv.val; - let bcx = ix.bcx; - let ccx = cx.ccx(); - - // Cast to an LLVM integer. Rust is less strict than LLVM in this regard. - let ix_size = llsize_of_real(cx.ccx(), val_ty(ix.val)); - let int_size = llsize_of_real(cx.ccx(), ccx.int_type); - let ix_val = if ix_size < int_size { - if ty::type_is_signed(expr_ty(cx, 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 unit_ty = node_id_type(cx, ex.id); - let llunitty = type_of(ccx, unit_ty); - let unit_sz = llsize_of(ccx, llunitty); - maybe_name_value(cx.ccx(), unit_sz, ~"unit_sz"); - let scaled_ix = Mul(bcx, ix_val, unit_sz); - maybe_name_value(cx.ccx(), scaled_ix, ~"scaled_ix"); - - let mut (base, len) = tvec::get_base_and_len(bcx, v, base_ty); - - if ty::type_is_str(base_ty) { - len = Sub(bcx, len, C_uint(bcx.ccx(), 1u)); - } - - debug!("trans_index: base %s", val_str(bcx.ccx().tn, base)); - debug!("trans_index: len %s", val_str(bcx.ccx().tn, len)); - - let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len); - let bcx = do with_cond(bcx, bounds_check) |bcx| { - // fail: bad bounds check. - trans_fail(bcx, Some(ex.span), ~"bounds check") - }; - let elt = InBoundsGEP(bcx, base, ~[ix_val]); - return lval_owned(bcx, PointerCast(bcx, elt, T_ptr(llunitty))); -} - -fn expr_is_borrowed(bcx: block, e: @ast::expr) -> bool { - bcx.tcx().borrowings.contains_key(e.id) -} - -fn expr_is_lval(bcx: block, e: @ast::expr) -> bool { - let ccx = bcx.ccx(); - ty::expr_is_lval(ccx.maps.method_map, e) -} - -fn trans_callee(bcx: block, e: @ast::expr) -> lval_maybe_callee { - let _icx = bcx.insn_ctxt("trans_callee"); - match e.node { - ast::expr_path(_) => return trans_path(bcx, e.id), - ast::expr_field(base, _, _) => { - // Lval means this is a record field, so not a method - if !expr_is_lval(bcx, e) { - match bcx.ccx().maps.method_map.find(e.id) { - Some(origin) => { // An impl method - return impl::trans_method_callee(bcx, e.id, base, origin); - } - _ => { - bcx.ccx().sess.span_bug(e.span, ~"trans_callee: weird expr"); - } - } - } - } - _ => () - } - let lv = trans_temp_lval(bcx, e); - return lval_no_env(lv.bcx, lv.val, lv.kind); -} - -// Use this when you know you are compiling an lval. -// The additional bool returned indicates whether it's mem (that is -// represented as an alloca or heap, hence needs a 'load' to be used as an -// immediate). -fn trans_lval(cx: block, e: @ast::expr) -> lval_result { - return match cx.ccx().maps.root_map.find({id:e.id, derefs:0u}) { - // No need to root this lvalue. - None => unrooted(cx, e), - - // Lvalue must remain rooted until exit of `scope_id`. See - // add_root_cleanup() for comments on why this works the way it does. - Some(scope_id) => { - let lv = unrooted(cx, e); - - if !cx.sess().no_asm_comments() { - add_comment(cx, fmt!("preserving until end of scope %d", - scope_id)); - } - - let _icx = lv.bcx.insn_ctxt("root_value_lval"); - let ty = expr_ty(lv.bcx, e); - let root_loc = alloca_zeroed(lv.bcx, type_of(cx.ccx(), ty)); - let bcx = store_temp_expr(lv.bcx, INIT, root_loc, lv, ty, false); - add_root_cleanup(bcx, scope_id, root_loc, ty); - {bcx: bcx,.. lv} - } - }; - - fn unrooted(cx: block, e: @ast::expr) -> lval_result { - let _icx = cx.insn_ctxt("trans_lval"); - match e.node { - ast::expr_path(_) => { - let v = trans_path(cx, e.id); - return lval_maybe_callee_to_lval(v, e.span); - } - ast::expr_field(base, ident, _) => { - return trans_rec_field(cx, base, ident); - } - ast::expr_index(base, idx) => { - return trans_index(cx, e, base, idx); - } - ast::expr_unary(ast::deref, base) => { - let ccx = cx.ccx(); - let sub = trans_temp_expr(cx, base); - let t = expr_ty(cx, base); - let val = match ty::get(t).struct { - ty::ty_box(_) => { - let non_gc_val = non_gc_box_cast(sub.bcx, sub.val); - GEPi(sub.bcx, non_gc_val, [0u, abi::box_field_body]) - } - ty::ty_uniq(_) => { - let non_gc_val = non_gc_box_cast(sub.bcx, sub.val); - GEPi(sub.bcx, non_gc_val, [0u, abi::box_field_body]) - } - ty::ty_enum(_, _) => { - let ety = expr_ty(cx, e); - let ellty = T_ptr(type_of(ccx, ety)); - PointerCast(sub.bcx, sub.val, ellty) - } - ty::ty_ptr(_) | ty::ty_rptr(_,_) => sub.val, - _ => cx.sess().impossible_case(e.span, #fmt("unary operand \ - may not have type %s", cx.ty_to_str(t))) - }; - return lval_owned(sub.bcx, val); - } - _ => cx.sess().span_bug(e.span, ~"non-lval in trans_lval") - } - } -} - -/** - * Get the type of a box in the default address space. - * - * Shared box pointers live in address space 1 so the GC strategy can find - * them. Before taking a pointer to the inside of a box it should be cast into - * address space 0. Otherwise the resulting (non-box) pointer will be in the - * wrong address space and thus be the wrong type. - */ -fn non_gc_box_cast(cx: block, val: ValueRef) -> ValueRef { - debug!("non_gc_box_cast"); - add_comment(cx, ~"non_gc_box_cast"); - assert(llvm::LLVMGetPointerAddressSpace(val_ty(val)) == gc_box_addrspace); - let non_gc_t = T_ptr(llvm::LLVMGetElementType(val_ty(val))); - PointerCast(cx, val, non_gc_t) -} - -fn lval_maybe_callee_to_lval(c: lval_maybe_callee, sp: span) -> lval_result { - match c.env { - self_env(*) => { - c.bcx.sess().span_bug(sp, ~"implicitly binding method call"); - } - is_closure => { {bcx: c.bcx, val: c.val, kind: c.kind} } - null_env => { - let llfnty = llvm::LLVMGetElementType(val_ty(c.val)); - let llfn = create_real_fn_pair(c.bcx, llfnty, c.val, - null_env_ptr(c.bcx)); - {bcx: c.bcx, val: llfn, kind: lv_temporary} - } - } -} - -fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, - llsrc: ValueRef, signed: bool) -> ValueRef { - let _icx = bcx.insn_ctxt("int_cast"); - let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype); - let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype); - return if dstsz == srcsz { - BitCast(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - TruncOrBitCast(bcx, llsrc, lldsttype) - } else if signed { - SExtOrBitCast(bcx, llsrc, lldsttype) - } else { ZExtOrBitCast(bcx, llsrc, lldsttype) }; -} - -fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, - llsrc: ValueRef) -> ValueRef { - let _icx = bcx.insn_ctxt("float_cast"); - let srcsz = lib::llvm::float_width(llsrctype); - let dstsz = lib::llvm::float_width(lldsttype); - return if dstsz > srcsz { - FPExt(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - FPTrunc(bcx, llsrc, lldsttype) - } else { llsrc }; -} - -enum cast_kind { - cast_pointer, - cast_integral, - cast_float, - cast_enum, - cast_other, -} - -impl cast_kind : cmp::Eq { - pure fn eq(&&other: cast_kind) -> bool { - match (self, other) { - (cast_pointer, cast_pointer) => true, - (cast_integral, cast_integral) => true, - (cast_float, cast_float) => true, - (cast_enum, cast_enum) => true, - (cast_other, cast_other) => true, - (cast_pointer, _) => false, - (cast_integral, _) => false, - (cast_float, _) => false, - (cast_enum, _) => false, - (cast_other, _) => false, - } - } -} - -fn cast_type_kind(t: ty::t) -> cast_kind { - match ty::get(t).struct { - ty::ty_float(*) => cast_float, - ty::ty_ptr(*) => cast_pointer, - ty::ty_rptr(*) => cast_pointer, - ty::ty_int(*) => cast_integral, - ty::ty_uint(*) => cast_integral, - ty::ty_bool => cast_integral, - ty::ty_enum(*) => cast_enum, - _ => cast_other - } -} - - -fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id, - dest: dest) -> block { - let _icx = cx.insn_ctxt("trans_cast"); - let ccx = cx.ccx(); - let t_out = node_id_type(cx, id); - match ty::get(t_out).struct { - ty::ty_trait(_, _, _) => return impl::trans_cast(cx, e, id, dest), - _ => () - } - let e_res = trans_temp_expr(cx, e); - let ll_t_in = val_ty(e_res.val); - let t_in = expr_ty(cx, e); - let ll_t_out = type_of(ccx, t_out); - - 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 newval = - match {in: k_in, out: k_out} { - {in: cast_integral, out: cast_integral} => { - int_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val, s_in) - } - {in: cast_float, out: cast_float} => { - float_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val) - } - {in: cast_integral, out: cast_float} => { - if s_in { - SIToFP(e_res.bcx, e_res.val, ll_t_out) - } else { UIToFP(e_res.bcx, e_res.val, ll_t_out) } - } - {in: cast_float, out: cast_integral} => { - if ty::type_is_signed(t_out) { - FPToSI(e_res.bcx, e_res.val, ll_t_out) - } else { FPToUI(e_res.bcx, e_res.val, ll_t_out) } - } - {in: cast_integral, out: cast_pointer} => { - IntToPtr(e_res.bcx, e_res.val, ll_t_out) - } - {in: cast_pointer, out: cast_integral} => { - PtrToInt(e_res.bcx, e_res.val, ll_t_out) - } - {in: cast_pointer, out: cast_pointer} => { - PointerCast(e_res.bcx, e_res.val, ll_t_out) - } - {in: cast_enum, out: cast_integral} | - {in: cast_enum, out: cast_float} => { - let cx = e_res.bcx; - let llenumty = T_opaque_enum_ptr(ccx); - let av_enum = PointerCast(cx, e_res.val, llenumty); - let lldiscrim_a_ptr = GEPi(cx, av_enum, [0u, 0u]); - let lldiscrim_a = Load(cx, lldiscrim_a_ptr); - match k_out { - cast_integral => int_cast(e_res.bcx, ll_t_out, - val_ty(lldiscrim_a), - lldiscrim_a, true), - cast_float => SIToFP(e_res.bcx, lldiscrim_a, ll_t_out), - _ => ccx.sess.bug(~"translating unsupported cast.") - } - } - _ => ccx.sess.bug(~"translating unsupported cast.") - }; - return store_in_dest(e_res.bcx, newval, dest); -} - -fn trans_loop_body(bcx: block, id: ast::node_id, - decl: ast::fn_decl, body: ast::blk, - proto: ty::fn_proto, cap: ast::capture_clause, - ret_flag: Option, - dest: dest) -> block { - closure::trans_expr_fn(bcx, proto, decl, body, id, - cap, Some(ret_flag), dest) -} - -// temp_cleanups: cleanups that should run only if failure occurs before the -// call takes place: -fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr, - &temp_cleanups: ~[ValueRef], ret_flag: Option, - derefs: uint) - -> result { - let _icx = cx.insn_ctxt("trans_arg_expr"); - let ccx = cx.ccx(); - debug!("+++ trans_arg_expr on %s", expr_to_str(e, ccx.sess.intr())); - let e_ty = expr_ty(cx, e); - let is_bot = ty::type_is_bot(e_ty); - - // translate the arg expr as an lvalue - let lv = match ret_flag { - // If there is a ret_flag, this *must* be a loop body - Some(_) => match e.node { - ast::expr_loop_body(blk@@{node: - ast::expr_fn_block(decl, body, cap),_}) => { - let scratch = alloc_ty(cx, expr_ty(cx, blk)); - let proto = match ty::get(expr_ty(cx, e)).struct { - ty::ty_fn({proto, _}) => proto, - _ => cx.sess().impossible_case(e.span, ~"Loop body has \ - non-fn ty") - }; - let bcx = trans_loop_body(cx, blk.id, decl, body, proto, - cap, ret_flag, save_in(scratch)); - {bcx: bcx, val: scratch, kind: lv_temporary} - } - _ => cx.sess().impossible_case(e.span, ~"ret_flag with non-loop-\ - body expr") - }, - None => { - trans_temp_lval(cx, e) - } - }; - - // auto-deref value as required (this only applies to method - // call receivers) of method - debug!(" pre-deref value: %s", val_str(lv.bcx.ccx().tn, lv.val)); - let {lv, e_ty} = if derefs == 0u { - {lv: lv, e_ty: e_ty} - } else { - let {bcx, val} = lval_result_to_result(lv, e_ty); - let {bcx, val, ty: e_ty} = - autoderef(bcx, e.id, val, e_ty, derefs); - {lv: {bcx: bcx, val: val, kind: lv_temporary}, - e_ty: e_ty} - }; - - // borrow value (convert from @T to &T and so forth) - debug!(" pre-adaptation value: %s", val_str(lv.bcx.ccx().tn, lv.val)); - let {lv, ty: e_ty} = adapt_borrowed_value(lv, e, e_ty); - let mut bcx = lv.bcx; - let mut val = lv.val; - debug!(" adapted value: %s", val_str(bcx.ccx().tn, val)); - - // finally, deal with the various modes - let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode); - if is_bot { - // For values of type _|_, we generate an - // "undef" value, as such a value should never - // be inspected. It's important for the value - // to have type lldestty (the callee's expected type). - val = llvm::LLVMGetUndef(lldestty); - } else { - match arg_mode { - ast::by_ref | ast::by_mutbl_ref => { - // Ensure that the value is spilled into memory: - if lv.kind != lv_owned && ty::type_is_immediate(e_ty) { - val = do_spill_noroot(bcx, val); - } - } - - ast::by_val => { - // Ensure that the value is not spilled into memory: - if lv.kind == lv_owned || !ty::type_is_immediate(e_ty) { - val = Load(bcx, val); - } - } - - ast::by_copy | ast::by_move => { - // Ensure that an owned copy of the value is in memory: - let alloc = alloc_ty(bcx, e_ty); - let move_out = arg_mode == ast::by_move || - ccx.maps.last_use_map.contains_key(e.id); - if lv.kind == lv_temporary { revoke_clean(bcx, val); } - if lv.kind == lv_owned || !ty::type_is_immediate(e_ty) { - memmove_ty(bcx, alloc, val, e_ty); - if move_out && ty::type_needs_drop(ccx.tcx, e_ty) { - bcx = zero_mem(bcx, val, e_ty); - } - } else { Store(bcx, val, alloc); } - val = alloc; - if lv.kind != lv_temporary && !move_out { - bcx = take_ty(bcx, val, e_ty); - } - - // In the event that failure occurs before the call actually - // happens, have to cleanup this copy: - add_clean_temp_mem(bcx, val, e_ty); - vec::push(temp_cleanups, val); - } - } - } - - if !is_bot && arg.ty != e_ty || ty::type_has_params(arg.ty) { - debug!(" casting from %s", val_str(bcx.ccx().tn, val)); - val = PointerCast(bcx, val, lldestty); - } - - debug!("--- trans_arg_expr passing %s", val_str(bcx.ccx().tn, val)); - return rslt(bcx, val); -} - -// when invoking a method, an argument of type @T or ~T can be implicltly -// converted to an argument of type &T. Similarly, ~[T] can be converted to -// &[T] and so on. If such a conversion (called borrowing) is necessary, -// then the borrowings table will have an appropriate entry inserted. This -// routine consults this table and performs these adaptations. It returns a -// new location for the borrowed result as well as a new type for the argument -// that reflects the borrowed value and not the original. -// -// NB: "e" has already been translated; do not translate it again. If you do, -// this will cause problems with autoderef and method receivers (bug #3357). -fn adapt_borrowed_value(lv: lval_result, - e: @ast::expr, - e_ty: ty::t) -> {lv: lval_result, - ty: ty::t} { - let bcx = lv.bcx; - if !expr_is_borrowed(bcx, e) { - return {lv:lv, ty:e_ty}; - } - - match ty::get(e_ty).struct { - ty::ty_uniq(mt) | ty::ty_box(mt) => { - let box_ptr = load_value_from_lval_result(lv, e_ty); - let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]); - let rptr_ty = ty::mk_rptr(bcx.tcx(), ty::re_static, mt); - return {lv: lval_temp(bcx, body_ptr), ty: rptr_ty}; + ty::ty_int(t) => { + let zero = C_integral(T_int_ty(cx.ccx(), t), 0u64, False); + ICmp(cx, lib::llvm::IntEQ, rhs, zero) } - - ty::ty_estr(_) | ty::ty_evec(_, _) => { - let ccx = bcx.ccx(); - let val = match lv.kind { - lv_temporary => lv.val, - lv_owned => load_if_immediate(bcx, lv.val, e_ty), - lv_owned_imm => lv.val - }; - - let unit_ty = ty::sequence_element_type(ccx.tcx, e_ty); - let llunit_ty = type_of(ccx, unit_ty); - let (base, len) = tvec::get_base_and_len(bcx, val, e_ty); - let p = alloca(bcx, T_struct(~[T_ptr(llunit_ty), ccx.int_type])); - - debug!("adapt_borrowed_value: adapting %s to %s", - val_str(bcx.ccx().tn, val), - val_str(bcx.ccx().tn, p)); - - Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base])); - Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len])); - - // this isn't necessarily the type that rust would assign but it's - // close enough for trans purposes, as it will have the same runtime - // representation - let slice_ty = ty::mk_evec(bcx.tcx(), - {ty: unit_ty, mutbl: ast::m_imm}, - ty::vstore_slice(ty::re_static)); - - return {lv: lval_temp(bcx, p), ty: slice_ty}; + ty::ty_uint(t) => { + let zero = C_integral(T_uint_ty(cx.ccx(), t), 0u64, False); + ICmp(cx, lib::llvm::IntEQ, rhs, zero) } - _ => { - // Just take a reference. This is basically like trans_addr_of. - let mut {bcx, val, kind} = lv; - let is_immediate = ty::type_is_immediate(e_ty); - if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm { - val = do_spill(bcx, val, e_ty); - } - return {lv: {bcx: bcx, val: val, kind: lv_temporary}, - ty: ty::mk_rptr(bcx.tcx(), ty::re_static, - {ty: e_ty, mutbl: ast::m_imm})}; + cx.tcx().sess.bug(~"fail-if-zero on unexpected type: " + + ty_to_str(cx.ccx().tcx, rhs_t)); } + }; + do with_cond(cx, is_zero) |bcx| { + controlflow::trans_fail(bcx, Some(span), text) } } -enum call_args { - arg_exprs(~[@ast::expr]), - arg_vals(~[ValueRef]) +fn null_env_ptr(bcx: block) -> ValueRef { + C_null(T_opaque_box_ptr(bcx.ccx())) } -// NB: must keep 4 fns in sync: -// -// - type_of_fn -// - create_llargs_for_fn_args. -// - new_fn_ctxt -// - trans_args -fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t, - dest: dest, ret_flag: Option) - -> {bcx: block, args: ~[ValueRef], retslot: ValueRef} { - let _icx = cx.insn_ctxt("trans_args"); - let mut temp_cleanups = ~[]; - let arg_tys = ty::ty_fn_args(fn_ty); - let mut llargs: ~[ValueRef] = ~[]; - - let ccx = cx.ccx(); - let mut bcx = cx; - - let retty = ty::ty_fn_ret(fn_ty); - // Arg 0: Output pointer. - let llretslot = match dest { - ignore => { - if ty::type_is_nil(retty) { - llvm::LLVMGetUndef(T_ptr(T_nil())) - } else { alloc_ty(bcx, retty) } - } - save_in(dst) => dst, - by_val(_) => alloc_ty(bcx, retty) - }; - - vec::push(llargs, llretslot); - - // Arg 1: Env (closure-bindings / self value) - vec::push(llargs, llenv); - - // ... then explicit args. - - // First we figure out the caller's view of the types of the arguments. - // This will be needed if this is a generic call, because the callee has - // to cast her view of the arguments to the caller's view. - match args { - arg_exprs(es) => { - let llarg_tys = type_of_explicit_args(ccx, arg_tys); - let last = es.len() - 1u; - do vec::iteri(es) |i, e| { - let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i], - e, temp_cleanups, if i == last { ret_flag } - else { None }, 0u); - bcx = r.bcx; - vec::push(llargs, r.val); - } +fn trans_external_path(ccx: @crate_ctxt, did: ast::def_id, t: ty::t) + -> ValueRef { + let name = csearch::get_symbol(ccx.sess.cstore, did); + match ty::get(t).struct { + ty::ty_fn(_) => { + let llty = type_of_fn_from_ty(ccx, t); + return get_extern_fn(ccx.externs, ccx.llmod, name, + lib::llvm::CCallConv, llty); } - arg_vals(vs) => { - vec::push_all(llargs, vs); + _ => { + let llty = type_of(ccx, t); + return get_extern_const(ccx.externs, ccx.llmod, name, llty); } - } - - // 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). - do vec::iter(temp_cleanups) |c| { - revoke_clean(bcx, c) - } - - return {bcx: bcx, - args: llargs, - retslot: llretslot}; + }; } -fn trans_call(in_cx: block, call_ex: @ast::expr, f: @ast::expr, - args: call_args, id: ast::node_id, dest: dest) - -> block { - let _icx = in_cx.insn_ctxt("trans_call"); - trans_call_inner( - in_cx, call_ex.info(), expr_ty(in_cx, f), node_id_type(in_cx, id), - |cx| trans_callee(cx, f), args, dest) -} - -fn body_contains_ret(body: ast::blk) -> bool { - let cx = {mut found: false}; - visit::visit_block(body, cx, visit::mk_vt(@{ - visit_item: |_i, _cx, _v| { }, - visit_expr: |e: @ast::expr, cx: {mut found: bool}, v| { - if !cx.found { - match e.node { - ast::expr_ret(_) => cx.found = true, - _ => visit::visit_expr(e, cx, v), - } - } - } ,.. *visit::default_visitor() - })); - cx.found -} - -// See [Note-arg-mode] -fn trans_call_inner( - ++in_cx: block, - call_info: Option, - fn_expr_ty: ty::t, - ret_ty: ty::t, - get_callee: fn(block) -> lval_maybe_callee, - args: call_args, - dest: dest) -> block { - - do with_scope(in_cx, call_info, ~"call") |cx| { - let ret_in_loop = match args { - arg_exprs(args) => { - args.len() > 0u && match vec::last(args).node { - ast::expr_loop_body(@{ - node: ast::expr_fn_block(_, body, _), - _ - }) => body_contains_ret(body), - _ => false - } - } - _ => false - }; - - let f_res = get_callee(cx); - let mut bcx = f_res.bcx; - let ccx = cx.ccx(); - let ret_flag = if ret_in_loop { - let flag = alloca(bcx, T_bool()); - Store(bcx, C_bool(false), flag); - Some(flag) - } else { None }; - - let mut faddr = f_res.val; - let llenv = match f_res.env { - null_env => { - llvm::LLVMGetUndef(T_opaque_box_ptr(ccx)) - } - self_env(e, _, _, _) => { - PointerCast(bcx, e, T_opaque_box_ptr(ccx)) - } - is_closure => { - // It's a closure. Have to fetch the elements - if f_res.kind == lv_owned { - faddr = load_if_immediate(bcx, faddr, fn_expr_ty); - } - let pair = faddr; - faddr = GEPi(bcx, pair, [0u, abi::fn_field_code]); - faddr = Load(bcx, faddr); - let llclosure = GEPi(bcx, pair, [0u, abi::fn_field_box]); - Load(bcx, llclosure) - } - }; - - let args_res = { - trans_args(bcx, llenv, args, fn_expr_ty, dest, ret_flag) - }; - bcx = args_res.bcx; - let mut llargs = args_res.args; - - let llretslot = args_res.retslot; - - // Now that the arguments have finished evaluating, we need to revoke - // the cleanup for the self argument, if it exists - match f_res.env { - self_env(e, _, _, ast::by_copy) => revoke_clean(bcx, e), - _ => (), - } - - /* If the block is terminated, - then one or more of the args has - type _|_. Since that means it diverges, the code - for the call itself is unreachable. */ - bcx = invoke(bcx, faddr, llargs); - match dest { - ignore => { - if llvm::LLVMIsUndef(llretslot) != lib::llvm::True { - bcx = drop_ty(bcx, llretslot, ret_ty); - } - } - save_in(_) => { } // Already saved by callee - by_val(cell) => { - *cell = Load(bcx, llretslot); - } - } - if ty::type_is_bot(ret_ty) { - Unreachable(bcx); - } else if ret_in_loop { - bcx = do with_cond(bcx, Load(bcx, option::get(ret_flag))) |bcx| { - do option::iter(copy bcx.fcx.loop_ret) |lret| { - Store(bcx, C_bool(true), lret.flagptr); - Store(bcx, C_bool(false), bcx.fcx.llretptr); - } - cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); - Unreachable(bcx); - bcx - } - } - bcx +fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef { + let _icx = ccx.insn_ctxt("lookup_discriminant"); + match ccx.discrims.find(vid) { + None => { + // It's an external discriminant that we haven't seen yet. + assert (vid.crate != ast::local_crate); + let sym = csearch::get_symbol(ccx.sess.cstore, vid); + let gvar = str::as_c_str(sym, |buf| { + llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) + }); + lib::llvm::SetLinkage(gvar, lib::llvm::ExternalLinkage); + llvm::LLVMSetGlobalConstant(gvar, True); + ccx.discrims.insert(vid, gvar); + return gvar; + } + Some(llval) => return llval, } } @@ -3571,282 +865,6 @@ fn get_landing_pad(bcx: block) -> BasicBlockRef { return pad_bcx.llbb; } -fn trans_tup(bcx: block, elts: ~[@ast::expr], dest: dest) -> block { - let _icx = bcx.insn_ctxt("trans_tup"); - let mut bcx = bcx; - let addr = match dest { - ignore => { - for vec::each(elts) |ex| { bcx = trans_expr(bcx, ex, ignore); } - return bcx; - } - save_in(pos) => pos, - _ => bcx.tcx().sess.bug(~"trans_tup: weird dest") - }; - let mut temp_cleanups = ~[]; - for vec::eachi(elts) |i, e| { - let dst = GEPi(bcx, addr, [0u, i]); - let e_ty = expr_ty(bcx, e); - bcx = trans_expr_save_in(bcx, e, dst); - add_clean_temp_mem(bcx, dst, e_ty); - vec::push(temp_cleanups, dst); - } - for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, cleanup); } - return bcx; -} - -fn trans_rec(bcx: block, fields: ~[ast::field], - base: Option<@ast::expr>, id: ast::node_id, - // none = ignore; some(x) = save_in(x) - dest: Option) -> block { - let _icx = bcx.insn_ctxt("trans_rec"); - let t = node_id_type(bcx, id); - let mut bcx = bcx; - let addr = match dest { - None => { - for vec::each(fields) |fld| { - bcx = trans_expr(bcx, fld.node.expr, ignore); - } - return bcx; - } - Some(pos) => pos - }; - - let ty_fields = match ty::get(t).struct { - ty::ty_rec(f) => f, - _ => bcx.sess().bug(~"trans_rec: record has non-record type") - }; - - let mut temp_cleanups = ~[]; - for fields.each |fld| { - let ix = option::get(vec::position(ty_fields, - |ft| ft.ident == fld.node.ident)); - let dst = GEPi(bcx, addr, [0u, ix]); - bcx = trans_expr_save_in(bcx, fld.node.expr, dst); - add_clean_temp_mem(bcx, dst, ty_fields[ix].mt.ty); - vec::push(temp_cleanups, dst); - } - match base { - Some(bexp) => { - let {bcx: cx, val: base_val} = trans_temp_expr(bcx, bexp); - bcx = cx; - // Copy over inherited fields - for ty_fields.eachi |i, tf| { - if !vec::any(fields, |f| f.node.ident == tf.ident) { - let dst = GEPi(bcx, addr, [0u, i]); - let base = GEPi(bcx, base_val, [0u, i]); - let val = load_if_immediate(bcx, base, tf.mt.ty); - bcx = copy_val(bcx, INIT, dst, val, tf.mt.ty); - } - } - } - None => () - }; - - // Now revoke the cleanups as we pass responsibility for the data - // structure on to the caller - for temp_cleanups.each |cleanup| { revoke_clean(bcx, cleanup); } - return bcx; -} - -// If the class has a destructor, our GEP is a little more -// complicated. -fn get_struct_field(block_context: block, dest_address: ValueRef, - class_id: ast::def_id, index: uint) -> ValueRef { - if ty::ty_dtor(block_context.tcx(), class_id).is_some() { - return GEPi(block_context, - GEPi(block_context, dest_address, [0, 1]), - [0, index]); - } - return GEPi(block_context, dest_address, [0, index]); -} - -fn trans_struct(block_context: block, span: span, fields: ~[ast::field], - base: Option<@ast::expr>, id: ast::node_id, dest: dest) - -> block { - - let _instruction_context = block_context.insn_ctxt("trans_struct"); - let mut block_context = block_context; - let type_context = block_context.ccx().tcx; - - let struct_type = node_id_type(block_context, id); - - // Get the address to store the structure into. If there is no address, - // just translate each field and be done with it. - let dest_address; - match dest { - ignore => { - for fields.each |field| { - block_context = trans_expr(block_context, - field.node.expr, - ignore); - } - return block_context; - } - save_in(destination_address) => { - dest_address = destination_address; - } - by_val(_) => { - type_context.sess.span_bug(span, ~"didn't expect by_val"); - } - } - - // Get the class ID and its fields. - let class_fields, class_id, substitutions; - match ty::get(struct_type).struct { - ty::ty_class(existing_class_id, ref existing_substitutions) => { - class_id = existing_class_id; - substitutions = existing_substitutions; - class_fields = ty::lookup_class_fields(type_context, class_id); - } - _ => { - type_context.sess.span_bug(span, ~"didn't resolve to a struct"); - } - } - - // Add the drop flag if necessary. - if ty::ty_dtor(block_context.tcx(), class_id).is_some() { - let llflagptr = GEPi(block_context, dest_address, [0, 0]); - Store(block_context, C_u8(1), llflagptr); - } - - // Now translate each field. - let mut temp_cleanups = ~[]; - for fields.each |field| { - let mut found = None; - for class_fields.eachi |i, class_field| { - if class_field.ident == field.node.ident { - found = Some((i, class_field.id)); - break; - } - } - - let index, field_id; - match found { - Some((found_index, found_field_id)) => { - index = found_index; - field_id = found_field_id; - } - None => { - type_context.sess.span_bug(span, ~"unknown field"); - } - } - - let dest = get_struct_field(block_context, dest_address, class_id, - index); - - block_context = trans_expr_save_in(block_context, - field.node.expr, - dest); - - let field_type = ty::lookup_field_type(type_context, class_id, - field_id, substitutions); - add_clean_temp_mem(block_context, dest, field_type); - vec::push(temp_cleanups, dest); - } - - match base { - Some(base_expr) => { - let { bcx: bcx, val: llbasevalue } = - trans_temp_expr(block_context, base_expr); - block_context = bcx; - - // Copy over inherited fields. - for class_fields.eachi |i, class_field| { - let exists = do vec::any(fields) |provided_field| { - provided_field.node.ident == class_field.ident - }; - if exists { - again; - } - let lldestfieldvalue = get_struct_field(block_context, - dest_address, - class_id, - i); - let llbasefieldvalue = GEPi(block_context, - llbasevalue, - [0, i]); - let field_type = ty::lookup_field_type(block_context.tcx(), - class_id, - class_field.id, - substitutions); - let llbasefieldvalue = load_if_immediate(block_context, - llbasefieldvalue, - field_type); - block_context = copy_val(block_context, INIT, - lldestfieldvalue, llbasefieldvalue, - field_type); - } - } - None => () - } - - // Now revoke the cleanups, as we pass responsibility for the data - // structure onto the caller. - for temp_cleanups.each |temp_cleanup| { - revoke_clean(block_context, temp_cleanup); - } - - block_context -} - -// Store the result of an expression in the given memory location, ensuring -// that nil or bot expressions get ignore rather than save_in as destination. -fn trans_expr_save_in(bcx: block, e: @ast::expr, dest: ValueRef) - -> block { - let t = expr_ty(bcx, e); - let do_ignore = ty::type_is_bot(t) || ty::type_is_nil(t); - return trans_expr(bcx, e, if do_ignore { ignore } else { save_in(dest) }); -} - -// Call this to compile an expression that you need as an intermediate value, -// and you want to know whether you're dealing with an lval or not (the kind -// field in the returned struct). For non-intermediates, use trans_expr or -// trans_expr_save_in. For intermediates where you don't care about lval-ness, -// use trans_temp_expr. -fn trans_temp_lval(bcx: block, e: @ast::expr) -> lval_result { - let _icx = bcx.insn_ctxt("trans_temp_lval"); - let mut bcx = bcx; - if expr_is_lval(bcx, e) { - return trans_lval(bcx, e); - } else { - let ty = expr_ty(bcx, e); - if ty::type_is_nil(ty) || ty::type_is_bot(ty) { - bcx = trans_expr(bcx, e, ignore); - return {bcx: bcx, val: C_nil(), kind: lv_temporary}; - } else if ty::type_is_immediate(ty) { - let cell = empty_dest_cell(); - bcx = trans_expr(bcx, e, by_val(cell)); - add_clean_temp_immediate(bcx, *cell, ty); - return {bcx: bcx, val: *cell, kind: lv_temporary}; - } else { - let scratch = alloc_ty(bcx, ty); - let bcx = trans_expr_save_in(bcx, e, scratch); - add_clean_temp_mem(bcx, scratch, ty); - return {bcx: bcx, val: scratch, kind: lv_temporary}; - } - } -} - -// Use only for intermediate values. See trans_expr and trans_expr_save_in for -// expressions that must 'end up somewhere' (or get ignored). -fn trans_temp_expr(bcx: block, e: @ast::expr) -> result { - let _icx = bcx.insn_ctxt("trans_temp_expr"); - lval_result_to_result(trans_temp_lval(bcx, e), expr_ty(bcx, e)) -} - -fn load_value_from_lval_result(lv: lval_result, ty: ty::t) -> ValueRef { - match lv.kind { - lv_temporary => lv.val, - lv_owned => load_if_immediate(lv.bcx, lv.val, ty), - lv_owned_imm => lv.val - } -} - -fn lval_result_to_result(lv: lval_result, ty: ty::t) -> result { - let val = load_value_from_lval_result(lv, ty); - {bcx: lv.bcx, val: val} -} - // Arranges for the value found in `*root_loc` to be dropped once the scope // associated with `scope_id` exits. This is used to keep boxes live when // there are extant region pointers pointing at the interior. @@ -3867,314 +885,23 @@ fn add_root_cleanup(bcx: block, scope_id: ast::node_id, let bcx_scope = find_bcx_for_scope(bcx, scope_id); add_clean_temp_mem(bcx_scope, root_loc, ty); - fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block { - let mut bcx_sid = bcx; - loop { - bcx_sid = match bcx_sid.node_info { - Some({id, _}) if id == scope_id => { - return bcx_sid - } - _ => { - match bcx_sid.parent { - None => bcx.tcx().sess.bug( - fmt!("no enclosing scope with id %d", scope_id)), - Some(bcx_par) => bcx_par - } - } - } - } - } -} - -// Translate an expression, with the dest argument deciding what happens with -// the result. Invariants: -// - exprs returning nil or bot always get dest=ignore -// - exprs with non-immediate type never get dest=by_val -fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { - let _icx = bcx.insn_ctxt("trans_expr"); - debuginfo::update_source_pos(bcx, e.span); - - if expr_is_lval(bcx, e) { - return lval_to_dps(bcx, e, dest); - } - - return match bcx.ccx().maps.root_map.find({id:e.id, derefs:0u}) { - None => unrooted(bcx, e, dest), - Some(scope_id) => { - debug!("expression %d found in root map with scope %d", - e.id, scope_id); - - let ty = expr_ty(bcx, e); - let root_loc = alloca_zeroed(bcx, type_of(bcx.ccx(), ty)); - let bcx = unrooted(bcx, e, save_in(root_loc)); - - if !bcx.sess().no_asm_comments() { - add_comment(bcx, fmt!("preserving until end of scope %d", - scope_id)); - } - - let _icx = bcx.insn_ctxt("root_value_expr"); - add_root_cleanup(bcx, scope_id, root_loc, ty); - let lv = {bcx: bcx, val: root_loc, kind: lv_owned}; - lval_result_to_dps(lv, ty, false, dest) - } - }; - - fn unrooted(bcx: block, e: @ast::expr, dest: dest) -> block { - let tcx = bcx.tcx(); - match e.node { - ast::expr_if(cond, thn, els) => { - return trans_if(bcx, cond, thn, els, dest); - } - ast::expr_match(expr, arms) => { - return alt::trans_alt(bcx, e, expr, arms, dest); - } - ast::expr_block(blk) => { - return do with_scope(bcx, blk.info(), ~"block-expr body") |bcx| { - trans_block(bcx, blk, dest) - }; - } - ast::expr_rec(args, base) => { - let d = match dest { - ignore => None, - save_in(p) => Some(p), - _ => bcx.sess().impossible_case(e.span, - "trans_expr::unrooted: can't pass a record by val") - }; - return trans_rec(bcx, args, base, e.id, d); - } - ast::expr_struct(_, fields, base) => { - return trans_struct(bcx, e.span, fields, base, e.id, dest); - } - ast::expr_tup(args) => { return trans_tup(bcx, args, dest); } - ast::expr_vstore(e, v) => { - return tvec::trans_vstore(bcx, e, v, dest); - } - ast::expr_lit(lit) => return trans_lit(bcx, e, *lit, dest), - ast::expr_vec(args, _) => { - return tvec::trans_evec(bcx, tvec::individual_evec(args), - ast::vstore_fixed(None), e.id, dest); - } - ast::expr_repeat(element, count_expr, _) => { - let count = ty::eval_repeat_count(bcx.tcx(), count_expr, e.span); - return tvec::trans_evec(bcx, tvec::repeating_evec(element, count), - ast::vstore_fixed(None), e.id, dest); - } - ast::expr_binary(op, lhs, rhs) => { - return trans_binary(bcx, op, lhs, rhs, dest, e); - } - ast::expr_unary(op, x) => { - assert op != ast::deref; // lvals are handled above - return trans_unary(bcx, op, x, e, dest); - } - ast::expr_addr_of(_, x) => { return trans_addr_of(bcx, x, dest); } - ast::expr_fn(proto, decl, body, cap_clause) => { - // Don't use this function for anything real. Use the one in - // astconv instead. - fn ast_proto_to_proto_simple(ast_proto: ast::proto) - -> ty::fn_proto { - match ast_proto { - ast::proto_bare => - ty::proto_bare, - ast::proto_uniq => - ty::proto_vstore(ty::vstore_uniq), - ast::proto_box => - ty::proto_vstore(ty::vstore_box), - ast::proto_block => - ty::proto_vstore(ty::vstore_slice(ty::re_static)) - } - } - - // XXX: This syntax should be reworked a bit (in the parser I - // guess?); @fn() { ... } won't work. - return closure::trans_expr_fn(bcx, - ast_proto_to_proto_simple(proto), - decl, body, e.id, cap_clause, None, - dest); - } - ast::expr_fn_block(decl, body, cap_clause) => { - match ty::get(expr_ty(bcx, e)).struct { - ty::ty_fn({proto, _}) => { - debug!("translating fn_block %s with type %s", - expr_to_str(e, tcx.sess.intr()), - ppaux::ty_to_str(tcx, expr_ty(bcx, e))); - return closure::trans_expr_fn(bcx, proto, decl, body, - e.id, cap_clause, None, dest); - } - _ => bcx.sess().impossible_case(e.span, "fn_block has \ - body with a non-fn type") - } - } - ast::expr_loop_body(blk) => { - match ty::get(expr_ty(bcx, e)).struct { - ty::ty_fn({proto, _}) => { - match blk.node { - ast::expr_fn_block(decl, body, cap) => - return trans_loop_body(bcx, blk.id, decl, body, - proto, cap, None, dest), - _ => bcx.sess().impossible_case(e.span, "loop_body \ - has the wrong kind of contents") - } - - } - _ => bcx.sess().impossible_case(e.span, "loop_body has \ - body with a non-fn type") - } - } - ast::expr_do_body(blk) => { - return trans_expr(bcx, blk, dest); - } - ast::expr_copy(a) | ast::expr_unary_move(a) => { - if !expr_is_lval(bcx, a) { - return trans_expr(bcx, a, dest); - } - else { return lval_to_dps(bcx, a, dest); } - } - ast::expr_cast(val, _) => return trans_cast(bcx, val, e.id, dest), - ast::expr_call(f, args, _) => { - return trans_call(bcx, e, f, arg_exprs(args), e.id, dest); - } - ast::expr_field(base, _, _) => { - if dest == ignore { return trans_expr(bcx, base, ignore); } - let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e); - let lv = lval_maybe_callee_to_lval(callee, e.span); - revoke_clean(lv.bcx, lv.val); - memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty); - return lv.bcx; - } - ast::expr_index(base, idx) => { - // If it is here, it's not an lval, so this is a user-defined - // index op - let origin = bcx.ccx().maps.method_map.get(e.id); - let fty = node_id_type(bcx, e.callee_id); - return trans_call_inner( - bcx, e.info(), fty, - expr_ty(bcx, e), - |bcx| impl::trans_method_callee(bcx, e.callee_id, base, - origin), - arg_exprs(~[idx]), dest); - } - - // These return nothing - ast::expr_break(label_opt) => { - assert dest == ignore; - if label_opt.is_some() { - bcx.tcx().sess.span_unimpl(e.span, ~"labeled break"); - } - return trans_break(bcx); - } - ast::expr_again(label_opt) => { - assert dest == ignore; - if label_opt.is_some() { - bcx.tcx().sess.span_unimpl(e.span, ~"labeled again"); - } - return trans_cont(bcx); - } - ast::expr_ret(ex) => { - assert dest == ignore; - return trans_ret(bcx, ex); - } - ast::expr_fail(expr) => { - assert dest == ignore; - return trans_fail_expr(bcx, Some(e.span), expr); - } - ast::expr_log(_, lvl, a) => { - assert dest == ignore; - return trans_log(e, lvl, bcx, a); - } - ast::expr_assert(a) => { - assert dest == ignore; - return trans_check_expr(bcx, e, a, ~"Assertion"); - } - ast::expr_while(cond, body) => { - assert dest == ignore; - return trans_while(bcx, cond, body); - } - ast::expr_loop(body, _) => { - assert dest == ignore; - return trans_loop(bcx, body); - } - ast::expr_assign(dst, src) => { - assert dest == ignore; - let src_r = trans_temp_lval(bcx, src); - let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); - assert kind == lv_owned; - let is_last_use = - bcx.ccx().maps.last_use_map.contains_key(src.id); - return store_temp_expr(bcx, DROP_EXISTING, addr, src_r, - expr_ty(bcx, src), is_last_use); - } - ast::expr_move(dst, src) => { - // FIXME: calculate copy init-ness in typestate. (#839) - assert dest == ignore; - let src_r = trans_temp_lval(bcx, src); - let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); - assert kind == lv_owned; - return move_val(bcx, DROP_EXISTING, addr, src_r, - expr_ty(bcx, src)); - } - ast::expr_swap(dst, src) => { - assert dest == ignore; - let lhs_res = trans_lval(bcx, dst); - assert lhs_res.kind == lv_owned; - let rhs_res = trans_lval(lhs_res.bcx, src); - let t = expr_ty(bcx, src); - let tmp_alloc = alloc_ty(rhs_res.bcx, t); - // Swap through a temporary. - let bcx = move_val(rhs_res.bcx, INIT, tmp_alloc, lhs_res, t); - let bcx = move_val(bcx, INIT, lhs_res.val, rhs_res, t); - return move_val(bcx, INIT, rhs_res.val, - lval_owned(bcx, tmp_alloc), t); - } - ast::expr_assign_op(op, dst, src) => { - assert dest == ignore; - return trans_assign_op(bcx, e, op, dst, src); - } - _ => { - bcx.tcx().sess.span_bug(e.span, ~"trans_expr reached \ - fall-through case"); - } - } - } -} - -fn lval_to_dps(bcx: block, e: @ast::expr, dest: dest) -> block { - let last_use_map = bcx.ccx().maps.last_use_map; - let ty = expr_ty(bcx, e); - let lv = trans_lval(bcx, e); - let last_use = (lv.kind == lv_owned && last_use_map.contains_key(e.id)); - debug!("is last use (%s) = %b, %d", expr_to_str(e, bcx.ccx().sess.intr()), - last_use, lv.kind as int); - lval_result_to_dps(lv, ty, last_use, dest) -} - -fn lval_result_to_dps(lv: lval_result, ty: ty::t, - last_use: bool, dest: dest) -> block { - let mut {bcx, val, kind} = lv; - let ccx = bcx.ccx(); - match dest { - by_val(cell) => { - if kind == lv_temporary { - revoke_clean(bcx, val); - *cell = val; - } else if last_use { - *cell = Load(bcx, val); - if ty::type_needs_drop(ccx.tcx, ty) { - bcx = zero_mem(bcx, val, ty); + fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block { + let mut bcx_sid = bcx; + loop { + bcx_sid = match bcx_sid.node_info { + Some({id, _}) if id == scope_id => { + return bcx_sid + } + _ => { + match bcx_sid.parent { + None => bcx.tcx().sess.bug( + fmt!("no enclosing scope with id %d", scope_id)), + Some(bcx_par) => bcx_par + } + } } - } else { - if kind == lv_owned { val = Load(bcx, val); } - let {bcx: cx, val} = take_ty_immediate(bcx, val, ty); - *cell = val; - bcx = cx; } - } - save_in(loc) => { - bcx = store_temp_expr(bcx, INIT, loc, lv, ty, last_use); - } - ignore => () } - return bcx; } fn do_spill(bcx: block, v: ValueRef, t: ty::t) -> ValueRef { @@ -4209,99 +936,6 @@ fn load_if_immediate(cx: block, v: ValueRef, t: ty::t) -> ValueRef { return v; } -fn trans_log(log_ex: @ast::expr, lvl: @ast::expr, - bcx: block, e: @ast::expr) -> block { - let _icx = bcx.insn_ctxt("trans_log"); - let ccx = bcx.ccx(); - if ty::type_is_bot(expr_ty(bcx, lvl)) { - return trans_expr(bcx, lvl, ignore); - } - - let modpath = vec::append( - ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))], - vec::filter(bcx.fcx.path, |e| - match e { path_mod(_) => true, _ => false } - )); - let modname = path_str(ccx.sess, modpath); - - let global = if ccx.module_data.contains_key(modname) { - ccx.module_data.get(modname) - } else { - let s = link::mangle_internal_name_by_path_and_seq( - ccx, modpath, ~"loglevel"); - let global = str::as_c_str(s, |buf| { - llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf) - }); - llvm::LLVMSetGlobalConstant(global, False); - llvm::LLVMSetInitializer(global, C_null(T_i32())); - lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage); - ccx.module_data.insert(modname, global); - global - }; - let current_level = Load(bcx, global); - let {bcx, val: level} = { - do with_scope_result(bcx, lvl.info(), ~"level") |bcx| { - trans_temp_expr(bcx, lvl) - } - }; - - do with_cond(bcx, ICmp(bcx, lib::llvm::IntUGE, current_level, level)) - |bcx| { - do with_scope(bcx, log_ex.info(), ~"log") |bcx| { - let {bcx, val, _} = trans_temp_expr(bcx, e); - let e_ty = expr_ty(bcx, e); - let tydesc = get_tydesc_simple(ccx, e_ty); - // Call the polymorphic log function. - let val = spill_if_immediate(bcx, val, e_ty); - let val = PointerCast(bcx, val, T_ptr(T_i8())); - Call(bcx, ccx.upcalls.log_type, ~[tydesc, val, level]); - bcx - } - } -} - -fn trans_check_expr(bcx: block, chk_expr: @ast::expr, - pred_expr: @ast::expr, s: ~str) -> block { - let _icx = bcx.insn_ctxt("trans_check_expr"); - let expr_str = s + ~" " + expr_to_str(pred_expr, bcx.ccx().sess.intr()) - + ~" failed"; - let {bcx, val} = { - do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| { - trans_temp_expr(bcx, pred_expr) - } - }; - do with_cond(bcx, Not(bcx, val)) |bcx| { - trans_fail(bcx, Some(pred_expr.span), expr_str) - } -} - -fn trans_fail_expr(bcx: block, sp_opt: Option, - fail_expr: Option<@ast::expr>) -> block { - let _icx = bcx.insn_ctxt("trans_fail_expr"); - let mut bcx = bcx; - match fail_expr { - Some(expr) => { - let ccx = bcx.ccx(), tcx = ccx.tcx; - let expr_res = trans_temp_expr(bcx, expr); - let e_ty = expr_ty(bcx, expr); - bcx = expr_res.bcx; - - if ty::type_is_str(e_ty) { - let body = tvec::get_bodyptr(bcx, expr_res.val); - let data = tvec::get_dataptr(bcx, body); - return trans_fail_value(bcx, sp_opt, data); - } else if bcx.unreachable || ty::type_is_bot(e_ty) { - return bcx; - } else { - bcx.sess().span_bug( - expr.span, ~"fail called with unsupported type " + - ppaux::ty_to_str(tcx, e_ty)); - } - } - _ => return trans_fail(bcx, sp_opt, ~"explicit failure") - } -} - fn trans_trace(bcx: block, sp_opt: Option, trace_str: ~str) { if !bcx.sess().trace() { return; } let _icx = bcx.insn_ctxt("trans_trace"); @@ -4326,123 +960,6 @@ fn trans_trace(bcx: block, sp_opt: Option, trace_str: ~str) { Call(bcx, ccx.upcalls.trace, args); } -fn trans_fail(bcx: block, sp_opt: Option, fail_str: ~str) -> - block { - let _icx = bcx.insn_ctxt("trans_fail"); - let V_fail_str = C_cstr(bcx.ccx(), fail_str); - return trans_fail_value(bcx, sp_opt, V_fail_str); -} - -fn trans_fail_value(bcx: block, sp_opt: Option, - V_fail_str: ValueRef) -> block { - let _icx = bcx.insn_ctxt("trans_fail_value"); - let ccx = bcx.ccx(); - let {V_filename, V_line} = match sp_opt { - Some(sp) => { - let sess = bcx.sess(); - let loc = codemap::lookup_char_pos(sess.parse_sess.cm, sp.lo); - {V_filename: C_cstr(bcx.ccx(), loc.file.name), - V_line: loc.line as int} - } - None => { - {V_filename: C_cstr(bcx.ccx(), ~""), - V_line: 0} - } - }; - let V_str = PointerCast(bcx, V_fail_str, T_ptr(T_i8())); - let V_filename = PointerCast(bcx, V_filename, T_ptr(T_i8())); - let args = ~[V_str, V_filename, C_int(ccx, V_line)]; - let bcx = trans_rtcall(bcx, ~"fail", args, ignore); - Unreachable(bcx); - return bcx; -} - -fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: dest) - -> block { - let did = bcx.ccx().rtcalls[name]; - let fty = if did.crate == ast::local_crate { - ty::node_id_to_type(bcx.ccx().tcx, did.node) - } else { - csearch::get_type(bcx.ccx().tcx, did).ty - }; - let rty = ty::ty_fn_ret(fty); - return trans_call_inner( - bcx, None, fty, rty, - |bcx| lval_static_fn_inner(bcx, did, 0, ~[], None), - arg_vals(args), dest); -} - -fn trans_break_cont(bcx: block, to_end: bool) - -> block { - let _icx = bcx.insn_ctxt("trans_break_cont"); - // Locate closest loop block, outputting cleanup as we go. - let mut unwind = bcx; - let mut target; - loop { - match unwind.kind { - block_scope({loop_break: Some(brk), _}) => { - target = if to_end { - brk - } else { - unwind - }; - break; - } - _ => () - } - unwind = match unwind.parent { - Some(cx) => cx, - // This is a return from a loop body block - None => { - Store(bcx, C_bool(!to_end), bcx.fcx.llretptr); - cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); - Unreachable(bcx); - return bcx; - } - }; - } - cleanup_and_Br(bcx, unwind, target.llbb); - Unreachable(bcx); - return bcx; -} - -fn trans_break(cx: block) -> block { - return trans_break_cont(cx, true); -} - -fn trans_cont(cx: block) -> block { - return trans_break_cont(cx, false); -} - -fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block { - let _icx = bcx.insn_ctxt("trans_ret"); - let mut bcx = bcx; - let retptr = match copy bcx.fcx.loop_ret { - Some({flagptr, retptr}) => { - // This is a loop body return. Must set continue flag (our retptr) - // to false, return flag to true, and then store the value in the - // parent's retptr. - Store(bcx, C_bool(true), flagptr); - Store(bcx, C_bool(false), bcx.fcx.llretptr); - match e { - Some(x) => PointerCast(bcx, retptr, - T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))), - None => retptr - } - } - None => bcx.fcx.llretptr - }; - match e { - Some(x) => { - bcx = trans_expr_save_in(bcx, x, retptr); - } - _ => () - } - cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); - Unreachable(bcx); - return bcx; -} - fn build_return(bcx: block) { let _icx = bcx.insn_ctxt("build_return"); Br(bcx, bcx.fcx.llreturn); @@ -4455,14 +972,21 @@ fn ignore_lhs(_bcx: block, local: @ast::local) -> bool { } fn init_local(bcx: block, local: @ast::local) -> block { + + debug!("init_local(bcx=%s, local.id=%?)", + bcx.to_str(), local.node.id); + let _indenter = indenter(); + let _icx = bcx.insn_ctxt("init_local"); let ty = node_id_type(bcx, local.node.id); + debug!("ty=%s", bcx.ty_to_str(ty)); + if ignore_lhs(bcx, local) { // Handle let _ = e; just like e; match local.node.init { Some(init) => { - return trans_expr(bcx, init.expr, ignore); + return expr::trans_into(bcx, init.expr, expr::Ignore); } None => { return bcx; } } @@ -4478,18 +1002,25 @@ fn init_local(bcx: block, local: @ast::local) -> block { let mut bcx = bcx; match local.node.init { - Some(init) => { - if init.op == ast::init_assign || !expr_is_lval(bcx, init.expr) { - bcx = trans_expr_save_in(bcx, init.expr, llptr); - } else { // This is a move from an lval, must perform an actual move - let sub = trans_lval(bcx, init.expr); - bcx = move_val(sub.bcx, INIT, llptr, sub, ty); + Some(init) => { + if init.op == ast::init_assign || !bcx.expr_is_lval(init.expr) { + bcx = expr::trans_into(bcx, init.expr, expr::SaveIn(llptr)); + } else { // This is a move from an lval, perform an actual move + let init_datumblock = expr::trans_to_datum(bcx, init.expr); + bcx = init_datumblock.move_to(datum::INIT, llptr); + } + } + _ => { + zero_mem(bcx, llptr, ty); } - } - _ => bcx = zero_mem(bcx, llptr, ty), } + // Make a note to drop this slot on the way out. + debug!("adding clean for %?/%s to bcx=%s", + local.node.id, bcx.ty_to_str(ty), + bcx.to_str()); add_clean(bcx, llptr, ty); + return alt::bind_irrefutable_pat(bcx, local.node.pat, llptr, false); } @@ -4505,22 +1036,22 @@ fn trans_stmt(cx: block, s: ast::stmt) -> block { debuginfo::update_source_pos(cx, s.span); match s.node { - ast::stmt_expr(e, _) | ast::stmt_semi(e, _) => { - bcx = trans_expr(cx, e, ignore); - } - ast::stmt_decl(d, _) => { - match d.node { - ast::decl_local(locals) => { - for vec::each(locals) |local| { - bcx = init_local(bcx, local); - if cx.sess().opts.extra_debuginfo { - debuginfo::create_local_var(bcx, local); + ast::stmt_expr(e, _) | ast::stmt_semi(e, _) => { + bcx = expr::trans_into(cx, e, expr::Ignore); + } + ast::stmt_decl(d, _) => { + match d.node { + ast::decl_local(locals) => { + for vec::each(locals) |local| { + bcx = init_local(bcx, local); + if cx.sess().opts.extra_debuginfo { + debuginfo::create_local_var(bcx, local); + } + } } + ast::decl_item(i) => trans_item(cx.fcx.ccx, *i) } - } - ast::decl_item(i) => trans_item(cx.fcx.ccx, *i) } - } } return bcx; @@ -4687,28 +1218,38 @@ fn leave_block(bcx: block, out_of: block) -> block { fn with_scope(bcx: block, opt_node_info: Option, name: ~str, f: fn(block) -> block) -> block { let _icx = bcx.insn_ctxt("with_scope"); + + debug!("with_scope(bcx=%s, opt_node_info=%?, name=%s)", + bcx.to_str(), opt_node_info, name); + let _indenter = indenter(); + let scope_cx = scope_block(bcx, opt_node_info, name); Br(bcx, scope_cx.llbb); leave_block(f(scope_cx), scope_cx) } fn with_scope_result(bcx: block, opt_node_info: Option, - name: ~str, f: fn(block) -> result) - -> result { + name: ~str, f: fn(block) -> Result) + -> Result +{ let _icx = bcx.insn_ctxt("with_scope_result"); let scope_cx = scope_block(bcx, opt_node_info, name); Br(bcx, scope_cx.llbb); - let {bcx, val} = f(scope_cx); - {bcx: leave_block(bcx, scope_cx), val: val} + let Result {bcx, val} = f(scope_cx); + rslt(leave_block(bcx, scope_cx), val) } -fn with_cond(bcx: block, val: ValueRef, f: fn(block) -> block) -> block { - let _icx = bcx.insn_ctxt("with_cond"); - let next_cx = sub_block(bcx, ~"next"), cond_cx = sub_block(bcx, ~"cond"); - CondBr(bcx, val, cond_cx.llbb, next_cx.llbb); - let after_cx = f(cond_cx); - if !after_cx.terminated { Br(after_cx, next_cx.llbb); } - next_cx +fn with_scope_datumblock(bcx: block, opt_node_info: Option, + name: ~str, f: fn(block) -> datum::DatumBlock) + -> datum::DatumBlock +{ + import datum::DatumBlock; + + let _icx = bcx.insn_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} } fn block_locals(b: ast::blk, it: fn(@ast::local)) { @@ -4727,16 +1268,6 @@ fn block_locals(b: ast::blk, it: fn(@ast::local)) { } } -fn alloc_ty(bcx: block, t: ty::t) -> ValueRef { - let _icx = bcx.insn_ctxt("alloc_ty"); - let ccx = bcx.ccx(); - let llty = type_of(ccx, t); - if ty::type_has_params(t) { log(error, ppaux::ty_to_str(ccx.tcx, t)); } - assert !ty::type_has_params(t); - let val = alloca(bcx, llty); - return val; -} - fn alloc_local(cx: block, local: @ast::local) -> block { let _icx = cx.insn_ctxt("alloc_local"); let t = node_id_type(cx, local.node.id); @@ -4756,24 +1287,117 @@ fn alloc_local(cx: block, local: @ast::local) -> block { return cx; } -fn trans_block(bcx: block, b: ast::blk, dest: dest) - -> block { - let _icx = bcx.insn_ctxt("trans_block"); - let mut bcx = bcx; - do block_locals(b) |local| { bcx = alloc_local(bcx, local); }; - for vec::each(b.node.stmts) |s| { - debuginfo::update_source_pos(bcx, b.span); - bcx = trans_stmt(bcx, *s); + +fn with_cond(bcx: block, val: ValueRef, f: fn(block) -> block) -> block { + let _icx = bcx.insn_ctxt("with_cond"); + let next_cx = base::sub_block(bcx, ~"next"); + let cond_cx = base::sub_block(bcx, ~"cond"); + CondBr(bcx, val, cond_cx.llbb, next_cx.llbb); + let after_cx = f(cond_cx); + if !after_cx.terminated { Br(after_cx, next_cx.llbb); } + next_cx +} + +fn call_memmove(cx: block, dst: ValueRef, src: ValueRef, + n_bytes: ValueRef) { + // FIXME (Related to #1645, I think?): Provide LLVM with better + // alignment information when the alignment is statically known (it must + // be nothing more than a constant int, or LLVM complains -- not even a + // constant element of a tydesc works). + let _icx = cx.insn_ctxt("call_memmove"); + let ccx = cx.ccx(); + let key = match ccx.sess.targ_cfg.arch { + session::arch_x86 | session::arch_arm => ~"llvm.memmove.p0i8.p0i8.i32", + session::arch_x86_64 => ~"llvm.memmove.p0i8.p0i8.i64" + }; + let memmove = ccx.intrinsics.get(key); + let src_ptr = PointerCast(cx, src, T_ptr(T_i8())); + let dst_ptr = PointerCast(cx, dst, T_ptr(T_i8())); + let size = IntCast(cx, n_bytes, ccx.int_type); + let align = C_i32(1i32); + let volatile = C_bool(false); + Call(cx, memmove, ~[dst_ptr, src_ptr, size, align, volatile]); +} + +fn memmove_ty(bcx: block, dst: ValueRef, src: ValueRef, t: ty::t) { + let _icx = bcx.insn_ctxt("memmove_ty"); + let ccx = bcx.ccx(); + if ty::type_is_structural(t) { + let llsz = llsize_of(ccx, type_of::type_of(ccx, t)); + call_memmove(bcx, dst, src, llsz); + } else { + Store(bcx, Load(bcx, src), dst); } - match b.node.expr { - Some(e) => { - let bt = ty::type_is_bot(expr_ty(bcx, e)); - debuginfo::update_source_pos(bcx, e.span); - bcx = trans_expr(bcx, e, if bt { ignore } else { dest }); - } - _ => assert dest == ignore || bcx.unreachable +} + +fn zero_mem(cx: block, llptr: ValueRef, t: ty::t) { + let _icx = cx.insn_ctxt("zero_mem"); + let bcx = cx; + let ccx = cx.ccx(); + let llty = type_of::type_of(ccx, t); + memzero(bcx, llptr, llty); +} + +// Always use this function instead of storing a zero constant to the memory +// in question. If you store a zero constant, LLVM will drown in vreg +// allocation for large data structures, and the generated code will be +// awful. (A telltale sign of this is large quantities of +// `mov [byte ptr foo],0` in the generated code.) +fn memzero(cx: block, llptr: ValueRef, llty: TypeRef) { + let _icx = cx.insn_ctxt("memzero"); + let ccx = cx.ccx(); + + let intrinsic_key; + match ccx.sess.targ_cfg.arch { + session::arch_x86 | session::arch_arm => { + intrinsic_key = ~"llvm.memset.p0i8.i32"; + } + session::arch_x86_64 => { + intrinsic_key = ~"llvm.memset.p0i8.i64"; + } } - return bcx; + + let llintrinsicfn = ccx.intrinsics.get(intrinsic_key); + let llptr = PointerCast(cx, llptr, T_ptr(T_i8())); + let llzeroval = C_u8(0); + let size = IntCast(cx, shape::llsize_of(ccx, llty), ccx.int_type); + let align = C_i32(1i32); + let volatile = C_bool(false); + Call(cx, llintrinsicfn, ~[llptr, llzeroval, size, align, volatile]); +} + +fn alloc_ty(bcx: block, t: ty::t) -> ValueRef { + let _icx = bcx.insn_ctxt("alloc_ty"); + let ccx = bcx.ccx(); + let llty = type_of::type_of(ccx, t); + if ty::type_has_params(t) { log(error, ty_to_str(ccx.tcx, t)); } + assert !ty::type_has_params(t); + let val = alloca(bcx, llty); + return val; +} + +fn alloca(cx: block, t: TypeRef) -> ValueRef { + alloca_maybe_zeroed(cx, t, false) +} + +fn alloca_zeroed(cx: block, t: TypeRef) -> ValueRef { + alloca_maybe_zeroed(cx, t, true) +} + +fn alloca_maybe_zeroed(cx: block, t: TypeRef, zero: bool) -> ValueRef { + let _icx = cx.insn_ctxt("alloca"); + if cx.unreachable { return llvm::LLVMGetUndef(t); } + let initcx = base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas); + let p = Alloca(initcx, t); + if zero { memzero(initcx, p, t); } + return p; +} + +fn arrayalloca(cx: block, t: TypeRef, v: ValueRef) -> ValueRef { + let _icx = cx.insn_ctxt("arrayalloca"); + if cx.unreachable { return llvm::LLVMGetUndef(t); } + return ArrayAlloca( + base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas), t, v); } // Creates the standard set of basic blocks for a function @@ -4845,10 +1469,18 @@ fn create_llargs_for_fn_args(cx: fn_ctxt, let mut arg_n = first_real_arg; match ty_self { impl_self(tt) => { - cx.llself = Some({v: cx.llenv, t: tt, is_owned: false}); + cx.llself = Some(ValSelfData { + v: cx.llenv, + t: tt, + is_owned: false + }); } impl_owned_self(tt) => { - cx.llself = Some({v: cx.llenv, t: tt, is_owned: true}); + cx.llself = Some(ValSelfData { + v: cx.llenv, + t: tt, + is_owned: true + }); } no_self => () } @@ -4878,12 +1510,12 @@ fn copy_args_to_allocas(fcx: fn_ctxt, bcx: block, args: ~[ast::arg], match fcx.llself { Some(copy slf) => { - // We really should do this regardless of whether self is owned, - // but it doesn't work right with default method impls yet. + // We really should do this regardless of whether self is owned, but + // it doesn't work right with default method impls yet. (FIXME: #2794) if slf.is_owned { let self_val = PointerCast(bcx, slf.v, T_ptr(type_of(bcx.ccx(), slf.t))); - fcx.llself = Some({v: self_val,.. slf}); + fcx.llself = Some(ValSelfData {v: self_val, ..slf}); add_clean(bcx, self_val, slf.t); } } @@ -4984,9 +1616,9 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl, (option::is_none(body.node.expr) || ty::type_is_bot(block_ty) || ty::type_is_nil(block_ty)) { - bcx = trans_block(bcx, body, ignore); + bcx = controlflow::trans_block(bcx, body, expr::Ignore); } else { - bcx = trans_block(bcx, body, save_in(fcx.llretptr)); + bcx = controlflow::trans_block(bcx, body, expr::SaveIn(fcx.llretptr)); } finish(bcx); cleanup_and_Br(bcx, bcx_top, fcx.llreturn); @@ -5081,69 +1713,53 @@ fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl, body: ast::blk, llctor_decl: ValueRef, psubsts: param_substs, ctor_id: ast::node_id, parent_id: ast::def_id, sp: span) { - // Add ctor to the ctor map - ccx.class_ctors.insert(ctor_id, parent_id); - - // Translate the ctor - - // Set up the type for the result of the ctor - // kludgy -- this wouldn't be necessary if the typechecker - // special-cased constructors, then we could just look up - // the ctor's return type. - let rslt_ty = ty::mk_class(ccx.tcx, parent_id, - dummy_substs(psubsts.tys)); - - // Make the fn context - let fcx = new_fn_ctxt_w_id(ccx, path, llctor_decl, ctor_id, - Some(psubsts), Some(sp)); - create_llargs_for_fn_args(fcx, no_self, decl.inputs); - let mut bcx_top = top_scope_block(fcx, body.info()); - let lltop = bcx_top.llbb; - bcx_top = copy_args_to_allocas(fcx, bcx_top, decl.inputs, - ty::ty_fn_args(node_id_type(bcx_top, ctor_id))); - - // We *don't* want self to be passed to the ctor -- that - // wouldn't make sense - // So we initialize it here - - let selfptr = alloc_ty(bcx_top, rslt_ty); - // If we have a dtor, we have a two-word representation with a drop - // flag, then a pointer to the class itself - let valptr = if option::is_some(ty::ty_dtor(bcx_top.tcx(), - parent_id)) { - // Initialize the drop flag - let one = C_u8(1u); - let flag = GEPi(bcx_top, selfptr, [0u, 0u]); - Store(bcx_top, one, flag); - // Select the pointer to the class itself - GEPi(bcx_top, selfptr, [0u, 1u]) - } - else { selfptr }; - - // initialize fields to zero - let dsubsts = dummy_substs(psubsts.tys); - let fields = ty::class_items_as_mutable_fields(bcx_top.tcx(), parent_id, - &dsubsts); - let mut bcx = bcx_top; - // Initialize fields to zero so init assignments can validly - // drop their LHS - for fields.each |field| { - let ix = field_idx_strict(bcx.tcx(), sp, field.ident, fields); - bcx = zero_mem(bcx, GEPi(bcx, valptr, [0u, ix]), field.mt.ty); - } + // Add ctor to the ctor map + ccx.class_ctors.insert(ctor_id, parent_id); + + // Translate the ctor + + // Set up the type for the result of the ctor + // kludgy -- this wouldn't be necessary if the typechecker + // special-cased constructors, then we could just look up + // the ctor's return type. + let rslt_ty = ty::mk_class(ccx.tcx, parent_id, + dummy_substs(psubsts.tys)); + + // Make the fn context + let fcx = new_fn_ctxt_w_id(ccx, path, llctor_decl, ctor_id, + Some(psubsts), Some(sp)); + create_llargs_for_fn_args(fcx, no_self, decl.inputs); + let mut bcx_top = top_scope_block(fcx, body.info()); + let lltop = bcx_top.llbb; + let arg_tys = ty::ty_fn_args(node_id_type(bcx_top, ctor_id)); + bcx_top = copy_args_to_allocas(fcx, bcx_top, decl.inputs, arg_tys); + + // Create a temporary for `self` that we will return at the end + let selfdatum = datum::scratch_datum(bcx_top, rslt_ty, true); + + // Initialize dtor flag (if any) to 1 + if option::is_some(ty::ty_dtor(bcx_top.tcx(), parent_id)) { + let flag = GEPi(bcx_top, selfdatum.val, [0, 1]); + Store(bcx_top, C_u8(1), flag); + } - // note we don't want to take *or* drop self. - fcx.llself = Some({v: selfptr, t: rslt_ty, is_owned: false}); + // initialize fields to zero + let mut bcx = bcx_top; + + // note we don't want to take *or* drop self. + fcx.llself = Some(ValSelfData {v: selfdatum.val, + t: rslt_ty, + is_owned: false}); + + // Translate the body of the ctor + bcx = controlflow::trans_block(bcx, body, expr::Ignore); + + // Generate the return expression + bcx = selfdatum.move_to(bcx, datum::INIT, fcx.llretptr); - // Translate the body of the ctor - bcx = trans_block(bcx_top, body, ignore); - let lval_res = {bcx: bcx, val: selfptr, kind: lv_owned}; - // Generate the return expression - bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res, - rslt_ty, true); - cleanup_and_leave(bcx, None, Some(fcx.llreturn)); - Unreachable(bcx); - finish_fn(fcx, lltop); + cleanup_and_leave(bcx, None, Some(fcx.llreturn)); + Unreachable(bcx); + finish_fn(fcx, lltop); } fn trans_class_dtor(ccx: @crate_ctxt, path: path, @@ -5429,16 +2045,6 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef, } } -// Create a /real/ closure: this is like create_fn_pair, but creates a -// a fn value on the stack with a specified environment (which need not be -// on the stack). -fn create_real_fn_pair(cx: block, llfnty: TypeRef, llfn: ValueRef, - llenvptr: ValueRef) -> ValueRef { - let pair = alloca(cx, T_fn_pair(cx.ccx(), llfnty)); - fill_fn_pair(cx, pair, llfn, llenvptr); - return pair; -} - fn fill_fn_pair(bcx: block, pair: ValueRef, llfn: ValueRef, llenvptr: ValueRef) { let ccx = bcx.ccx(); @@ -5668,7 +2274,7 @@ fn trans_constant(ccx: @crate_ctxt, it: @ast::item) { fn trans_constants(ccx: @crate_ctxt, crate: @ast::crate) { visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{ visit_item: |a| trans_constant(ccx, a), - .. *visit::default_simple_visitor() + ..*visit::default_simple_visitor() })); } @@ -5776,7 +2382,7 @@ fn gather_local_rtcalls(ccx: @crate_ctxt, crate: @ast::crate) { } _ => () }, - .. *visit::default_simple_visitor() + ..*visit::default_simple_visitor() })); } @@ -6089,7 +2695,7 @@ fn trans_crate(sess: session::session, // NB: Must call force_declare_tydescs before emit_tydescs to break // cyclical dependency with shape code! See shape.rs for details. force_declare_tydescs(ccx); - emit_tydescs(ccx); + glue::emit_tydescs(ccx); gen_shape_tables(ccx); write_abi_version(ccx); diff --git a/src/rustc/middle/trans/block.rs b/src/rustc/middle/trans/block.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/rustc/middle/trans/build.rs b/src/rustc/middle/trans/build.rs index 12b8f58117d1c..21ced9f581393 100644 --- a/src/rustc/middle/trans/build.rs +++ b/src/rustc/middle/trans/build.rs @@ -6,8 +6,8 @@ use codemap::span; use lib::llvm::{ValueRef, TypeRef, BasicBlockRef, BuilderRef, ModuleRef}; use lib::llvm::{Opcode, IntPredicate, RealPredicate, True, False, CallConv, TypeKind, AtomicBinOp, AtomicOrdering}; -use common::*; use driver::session::session; +use common::*; fn B(cx: block) -> BuilderRef { let b = cx.fcx.ccx.builder.B; @@ -670,7 +670,7 @@ fn add_comment(bcx: block, text: ~str) { } } -fn Call(cx: block, Fn: ValueRef, Args: ~[ValueRef]) -> ValueRef { +fn Call(cx: block, Fn: ValueRef, Args: &[ValueRef]) -> ValueRef { if cx.unreachable { return _UndefReturn(cx, Fn); } unsafe { count_insn(cx, "call"); @@ -679,8 +679,9 @@ fn Call(cx: block, Fn: ValueRef, Args: ~[ValueRef]) -> ValueRef { val_str(cx.ccx().tn, Fn), Args.map(|arg| val_str(cx.ccx().tn, arg))); - return llvm::LLVMBuildCall(B(cx), Fn, vec::unsafe::to_ptr(Args), - Args.len() as c_uint, noname()); + do vec::as_buf(Args) |ptr, len| { + llvm::LLVMBuildCall(B(cx), Fn, ptr, len as c_uint, noname()) + } } } diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs new file mode 100644 index 0000000000000..db6e9832ffdb9 --- /dev/null +++ b/src/rustc/middle/trans/callee.rs @@ -0,0 +1,665 @@ +//! +// +// Handles translation of callees as well as other call-related +// things. Callees are a superset of normal rust values and sometimes +// have different representations. In particular, top-level fn items +// and methods are represented as just a fn ptr and not a full +// closure. + +use lib::llvm::ValueRef; +use syntax::ast; +use datum::Datum; +use common::{block, node_id_type_params}; +use build::*; +use base::{get_item_val, trans_external_path}; +use syntax::visit; +use syntax::print::pprust::{expr_to_str, stmt_to_str, path_to_str}; +use datum::*; +use util::common::indenter; + +// Represents a (possibly monomorphized) top-level fn item or method +// item. Note that this is just the fn-ptr and is not a Rust closure +// value (which is a pair). +struct FnData { + llfn: ValueRef; +} + +struct MethodData { + llfn: ValueRef; + llself: ValueRef; + self_ty: ty::t; + self_mode: ast::rmode; +} + +enum CalleeData { + Closure(Datum), + Fn(FnData), + Method(MethodData) +} + +struct Callee { + bcx: block; + data: CalleeData; +} + +fn trans(bcx: block, expr: @ast::expr) -> Callee { + let _icx = bcx.insn_ctxt("trans_callee"); + + // pick out special kinds of expressions that can be called: + match expr.node { + ast::expr_path(_) => { + return trans_def(bcx, bcx.def(expr.id), expr); + } + ast::expr_field(base, _, _) => { + match bcx.ccx().maps.method_map.find(expr.id) { + Some(origin) => { // An impl method + return impl::trans_method_callee(bcx, expr.id, + base, origin); + } + None => {} // not a method, just a field + } + } + _ => {} + } + + // any other expressions are closures: + return closure_callee(&expr::trans_to_datum(bcx, expr)); + + fn closure_callee(db: &DatumBlock) -> Callee { + return Callee {bcx: db.bcx, data: Closure(db.datum)}; + } + + fn fn_callee(bcx: block, fd: FnData) -> Callee { + return Callee {bcx: bcx, data: Fn(fd)}; + } + + fn trans_def(bcx: block, def: ast::def, ref_expr: @ast::expr) -> Callee { + match def { + ast::def_fn(did, _) => { + fn_callee(bcx, trans_fn_ref(bcx, did, ref_expr.id)) + } + ast::def_static_method(did, _) => { + fn_callee(bcx, impl::trans_static_method_callee(bcx, did, + ref_expr.id)) + } + ast::def_variant(tid, vid) => { + // nullary variants are not callable + assert ty::enum_variant_with_id(bcx.tcx(), + tid, + vid).args.len() > 0u; + fn_callee(bcx, trans_fn_ref(bcx, vid, ref_expr.id)) + } + ast::def_arg(*) | + ast::def_local(*) | + ast::def_binding(*) | + ast::def_upvar(*) | + ast::def_self(*) => { + closure_callee(&expr::trans_to_datum(bcx, ref_expr)) + } + ast::def_mod(*) | ast::def_foreign_mod(*) | + ast::def_const(*) | ast::def_ty(*) | ast::def_prim_ty(*) | + ast::def_use(*) | ast::def_class(*) | ast::def_typaram_binder(*) | + ast::def_region(*) | ast::def_label(*) | ast::def_ty_param(*) => { + bcx.tcx().sess.span_bug( + ref_expr.span, + fmt!("Cannot translate def %? \ + to a callable thing!", def)); + } + } + } +} + +fn trans_fn_ref_to_callee(bcx: block, + def_id: ast::def_id, + ref_id: ast::node_id) -> Callee +{ + Callee {bcx: bcx, + data: Fn(trans_fn_ref(bcx, def_id, ref_id))} +} + +fn trans_fn_ref(bcx: block, + def_id: ast::def_id, + ref_id: ast::node_id) -> FnData { + //! + // + // Translates a reference (with id `ref_id`) to the fn/method + // with id `def_id` into a function pointer. This may require + // monomorphization or inlining. + + let _icx = bcx.insn_ctxt("trans_fn"); + + let type_params = node_id_type_params(bcx, ref_id); + + let raw_vtables = bcx.ccx().maps.vtable_map.find(ref_id); + let resolved_vtables = raw_vtables.map( + |vts| impl::resolve_vtables_in_fn_ctxt(bcx.fcx, vts)); + trans_fn_ref_with_vtables(bcx, def_id, ref_id, type_params, + resolved_vtables) +} + +fn trans_fn_ref_with_vtables_to_callee(bcx: block, + def_id: ast::def_id, + ref_id: ast::node_id, + type_params: ~[ty::t], + vtables: Option) + -> Callee +{ + Callee {bcx: bcx, + data: Fn(trans_fn_ref_with_vtables(bcx, def_id, ref_id, + type_params, vtables))} +} + +fn trans_fn_ref_with_vtables( + bcx: block, // + def_id: ast::def_id, // def id of fn + ref_id: ast::node_id, // node id of use of fn + type_params: ~[ty::t], // values for fn's ty params + vtables: Option) + -> FnData +{ + //! + // + // Translates a reference to a fn/method item, monomorphizing and + // inlining as it goes. + // + // # Parameters + // + // - `bcx`: the current block where the reference to the fn occurs + // - `def_id`: def id of the fn or method item being referenced + // - `ref_id`: node id of the reference to the fn/method + // - `type_params`: values for each of the fn/method's type parameters + // - `vtables`: values for each bound on each of the type parameters + + let _icx = bcx.insn_ctxt("trans_fn_with_vtables"); + let ccx = bcx.ccx(); + let tcx = ccx.tcx; + + // Polytype of the function item (may have type params) + let fn_tpt = ty::lookup_item_type(tcx, def_id); + + // Check whether this fn has an inlined copy and, if so, redirect + // def_id to the local id of the inlined copy. + let def_id = { + if def_id.crate != ast::local_crate { + inline::maybe_instantiate_inline(ccx, def_id) + } else { + def_id + } + }; + + // We must monomorphise if the fn has type parameters or is a rust + // intrinsic. In particular, if we see an intrinsic that is + // inlined from a different crate, we want to reemit the intrinsic + // instead of trying to call it in the other crate. + let must_monomorphise = type_params.len() > 0 || { + if def_id.crate == ast::local_crate { + let map_node = session::expect( + ccx.sess, + ccx.tcx.items.find(def_id.node), + || fmt!("local item should be in ast map")); + + match map_node { + ast_map::node_foreign_item( + _, ast::foreign_abi_rust_intrinsic, _) => true, + _ => false + } + } else { + false + } + }; + + // Create a monomorphic verison of generic functions + if must_monomorphise { + // Should be either intra-crate or inlined. + assert def_id.crate == ast::local_crate; + + let mut {val, must_cast} = + monomorphize::monomorphic_fn(ccx, def_id, type_params, + vtables, Some(ref_id)); + if must_cast { + // Monotype of the REFERENCE to the function (type params + // are subst'd) + let ref_ty = common::node_id_type(bcx, ref_id); + + val = PointerCast( + bcx, val, T_ptr(type_of::type_of_fn_from_ty(ccx, ref_ty))); + } + return FnData {llfn: val}; + } + + // Find the actual function pointer. + let mut val = { + if def_id.crate == ast::local_crate { + // Internal reference. + get_item_val(ccx, def_id.node) + } else { + // External reference. + trans_external_path(ccx, def_id, fn_tpt.ty) + } + }; + + //NDM I think this is dead. Commenting out to be sure! + //NDM + //NDM if tys.len() > 0u { + //NDM val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty( + //NDM ccx, node_id_type(bcx, id)))); + //NDM } + + return FnData {llfn: val}; +} + +// ______________________________________________________________________ +// Translating calls + +fn trans_call(in_cx: block, + call_ex: @ast::expr, + f: @ast::expr, + args: CallArgs, + id: ast::node_id, + dest: expr::Dest) + -> block +{ + let _icx = in_cx.insn_ctxt("trans_call"); + trans_call_inner( + in_cx, call_ex.info(), expr_ty(in_cx, f), node_id_type(in_cx, id), + |cx| trans(cx, f), args, dest) +} + +fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: expr::Dest) + -> block +{ + let did = bcx.ccx().rtcalls[name]; + let fty = if did.crate == ast::local_crate { + ty::node_id_to_type(bcx.ccx().tcx, did.node) + } else { + csearch::get_type(bcx.ccx().tcx, did).ty + }; + let rty = ty::ty_fn_ret(fty); + return callee::trans_call_inner( + bcx, None, fty, rty, + |bcx| trans_fn_ref_with_vtables_to_callee(bcx, did, 0, ~[], None), + ArgVals(args), dest); +} + +fn body_contains_ret(body: ast::blk) -> bool { + let cx = {mut found: false}; + visit::visit_block(body, cx, visit::mk_vt(@{ + visit_item: |_i, _cx, _v| { }, + visit_expr: |e: @ast::expr, cx: {mut found: bool}, v| { + if !cx.found { + match e.node { + ast::expr_ret(_) => cx.found = true, + _ => visit::visit_expr(e, cx, v), + } + } + }, + ..*visit::default_visitor() + })); + cx.found +} + +// See [Note-arg-mode] +fn trans_call_inner( + ++in_cx: block, + call_info: Option, + fn_expr_ty: ty::t, + ret_ty: ty::t, + get_callee: fn(block) -> Callee, + args: CallArgs, + dest: expr::Dest) -> block +{ + do base::with_scope(in_cx, call_info, ~"call") |cx| { + let ret_in_loop = match args { + ArgExprs(args) => { + args.len() > 0u && match vec::last(args).node { + ast::expr_loop_body(@{ + node: ast::expr_fn_block(_, body, _), + _ + }) => body_contains_ret(body), + _ => false + } + } + _ => false + }; + + let callee = get_callee(cx); + let mut bcx = callee.bcx; + let ccx = cx.ccx(); + let ret_flag = if ret_in_loop { + let flag = alloca(bcx, T_bool()); + Store(bcx, C_bool(false), flag); + Some(flag) + } else { None }; + + let (llfn, llenv) = match callee.data { + Fn(d) => { + (d.llfn, llvm::LLVMGetUndef(T_opaque_box_ptr(ccx))) + } + Method(d) => { + // Weird but true: we pass self in the *environment* slot! + let llself = PointerCast(bcx, d.llself, + T_opaque_box_ptr(ccx)); + (d.llfn, 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 args_res = trans_args(bcx, llenv, args, fn_expr_ty, + dest, ret_flag); + bcx = args_res.bcx; + let mut llargs = args_res.args; + + let llretslot = args_res.retslot; + + // Now that the arguments have finished evaluating, we need to revoke + // the cleanup for the self argument, if it exists + match callee.data { + Method(d) if d.self_mode == ast::by_copy => { + revoke_clean(bcx, d.llself); + } + _ => {} + } + + // If the block is terminated, then one or more of the args + // has type _|_. Since that means it diverges, the code for + // the call itself is unreachable. + bcx = base::invoke(bcx, llfn, llargs); + match dest { // drop the value if it is not being saved. + expr::Ignore => { + if llvm::LLVMIsUndef(llretslot) != lib::llvm::True { + bcx = glue::drop_ty(bcx, llretslot, ret_ty); + } + } + expr::SaveIn(_) => { } + } + if ty::type_is_bot(ret_ty) { + Unreachable(bcx); + } else if ret_in_loop { + bcx = do with_cond(bcx, Load(bcx, option::get(ret_flag))) |bcx| { + do option::iter(copy bcx.fcx.loop_ret) |lret| { + Store(bcx, C_bool(true), lret.flagptr); + Store(bcx, C_bool(false), bcx.fcx.llretptr); + } + base::cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + Unreachable(bcx); + bcx + } + } + bcx + } +} + + +enum CallArgs { + ArgExprs(~[@ast::expr]), + ArgVals(~[ValueRef]) +} + +fn trans_args(cx: block, llenv: ValueRef, args: CallArgs, fn_ty: ty::t, + dest: expr::Dest, ret_flag: Option) + -> {bcx: block, args: ~[ValueRef], retslot: ValueRef} +{ + let _icx = cx.insn_ctxt("trans_args"); + let mut temp_cleanups = ~[]; + let arg_tys = ty::ty_fn_args(fn_ty); + let mut llargs: ~[ValueRef] = ~[]; + + let mut bcx = cx; + + let retty = ty::ty_fn_ret(fn_ty); + + // Arg 0: Output pointer. + let llretslot = match dest { + expr::SaveIn(dst) => dst, + expr::Ignore => { + if ty::type_is_nil(retty) { + llvm::LLVMGetUndef(T_ptr(T_nil())) + } else { + alloc_ty(bcx, retty) + } + } + }; + vec::push(llargs, llretslot); + + // Arg 1: Env (closure-bindings / self value) + vec::push(llargs, llenv); + + // ... then explicit args. + + // First we figure out the caller's view of the types of the arguments. + // This will be needed if this is a generic call, because the callee has + // to cast her view of the arguments to the caller's view. + match args { + ArgExprs(arg_exprs) => { + let last = arg_exprs.len() - 1u; + do vec::iteri(arg_exprs) |i, arg_expr| { + let arg_val = unpack_result!(bcx, { + trans_arg_expr(bcx, arg_tys[i], arg_expr, &mut temp_cleanups, + if i == last { ret_flag } else { None }, + 0u) + }); + vec::push(llargs, arg_val); + } + } + ArgVals(vs) => { + vec::push_all(llargs, vs); + } + } + + // 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). + do vec::iter(temp_cleanups) |c| { + revoke_clean(bcx, c) + } + + return {bcx: bcx, args: llargs, retslot: llretslot}; +} + +// temp_cleanups: cleanups that should run only if failure occurs before the +// call takes place: +fn trans_arg_expr(bcx: block, + formal_ty: ty::arg, + arg_expr: @ast::expr, + temp_cleanups: &mut ~[ValueRef], + ret_flag: Option, + derefs: uint) + -> Result +{ + let _icx = bcx.insn_ctxt("trans_arg_expr"); + let ccx = bcx.ccx(); + + debug!("trans_arg_expr(formal_ty=(%?,%s), arg_expr=%s, \ + ret_flag=%?, derefs=%?)", + formal_ty.mode, bcx.ty_to_str(formal_ty.ty), + bcx.expr_to_str(arg_expr), + ret_flag.map(|v| bcx.val_str(v)), derefs); + let _indenter = indenter(); + + // translate the arg expr to a datum + let arg_datumblock = match ret_flag { + None => expr::trans_to_datum(bcx, arg_expr), + + // If there is a ret_flag, this *must* be a loop body + Some(_) => { + match arg_expr.node { + ast::expr_loop_body( + blk @ @{node:ast::expr_fn_block(decl, body, cap), _}) => + { + let scratch_ty = expr_ty(bcx, blk); + let scratch = alloc_ty(bcx, scratch_ty); + let arg_ty = expr_ty(bcx, arg_expr); + let proto = ty::ty_fn_proto(arg_ty); + let bcx = closure::trans_expr_fn( + bcx, proto, decl, body, blk.id, + cap, Some(ret_flag), expr::SaveIn(scratch)); + DatumBlock {bcx: bcx, + datum: Datum {val: scratch, + ty: scratch_ty, + mode: ByRef, + source: FromRvalue}} + } + _ => { + bcx.sess().impossible_case( + arg_expr.span, ~"ret_flag with non-loop-\ + body expr"); + } + } + } + }; + let mut arg_datum = arg_datumblock.datum; + let mut bcx = arg_datumblock.bcx; + + debug!(" initial value: %s", arg_datum.to_str(bcx.ccx())); + + // auto-deref value as required (this only applies to method + // call receivers) of method + if derefs != 0 { + arg_datum = arg_datum.autoderef(bcx, arg_expr.id, derefs); + debug!(" deref'd value: %s", arg_datum.to_str(bcx.ccx())); + }; + + // borrow value (convert from @T to &T and so forth) + let arg_datum = unpack_datum!(bcx, { + adapt_borrowed_value(bcx, arg_datum, arg_expr) + }); + debug!(" borrowed value: %s", arg_datum.to_str(bcx.ccx())); + + // finally, deal with the various modes + let arg_mode = ty::resolved_mode(ccx.tcx, formal_ty.mode); + let mut val; + 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 + // to have type lldestty (the callee's expected type). + let llformal_ty = type_of::type_of(ccx, formal_ty.ty); + val = llvm::LLVMGetUndef(llformal_ty); + } else { + match arg_mode { + ast::by_ref | ast::by_mutbl_ref => { + val = arg_datum.to_ref_llval(bcx); + } + + ast::by_val => { + // NB: avoid running the take glue. + val = arg_datum.to_value_llval(bcx); + } + + ast::by_copy | ast::by_move => { + let scratch = scratch_datum(bcx, arg_datum.ty, false); + + if arg_mode == ast::by_move { + // NDM---Doesn't seem like this should be necessary + if !arg_datum.store_will_move() { + bcx.sess().span_err( + arg_expr.span, + fmt!("move mode but datum will not store: %s", + arg_datum.to_str(bcx.ccx()))); + } + } + + 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); + vec::push(*temp_cleanups, scratch.val); + val = scratch.val; + } + } + + if formal_ty.ty != arg_datum.ty { + // this could happen due to e.g. subtyping + let llformal_ty = type_of::type_of_explicit_arg(ccx, formal_ty); + debug!("casting actual type (%s) to match formal (%s)", + bcx.val_str(val), bcx.llty_str(llformal_ty)); + val = PointerCast(bcx, val, llformal_ty); + } + } + + debug!("--- trans_arg_expr passing %s", val_str(bcx.ccx().tn, val)); + return rslt(bcx, val); +} + +// when invoking a method, an argument of type @T or ~T can be implicltly +// converted to an argument of type &T. Similarly, ~[T] can be converted to +// &[T] and so on. If such a conversion (called borrowing) is necessary, +// then the borrowings table will have an appropriate entry inserted. This +// routine consults this table and performs these adaptations. It returns a +// new location for the borrowed result as well as a new type for the argument +// that reflects the borrowed value and not the original. +fn adapt_borrowed_value(bcx: block, + datum: Datum, + expr: @ast::expr) -> DatumBlock +{ + if !expr_is_borrowed(bcx, expr) { + return DatumBlock {bcx: bcx, datum: datum}; + } + + debug!("adapt_borrowed_value(datum=%s, expr=%s)", + datum.to_str(bcx.ccx()), + bcx.expr_to_str(expr)); + + match ty::get(datum.ty).struct { + ty::ty_uniq(_) | ty::ty_box(_) => { + let body_datum = datum.box_body(bcx); + let rptr_datum = body_datum.to_rptr(bcx); + return DatumBlock {bcx: bcx, datum: rptr_datum}; + } + + ty::ty_estr(_) | ty::ty_evec(_, _) => { + let ccx = bcx.ccx(); + let val = datum.to_appropriate_llval(bcx); + + let unit_ty = ty::sequence_element_type(ccx.tcx, datum.ty); + let llunit_ty = type_of::type_of(ccx, unit_ty); + let (base, len) = datum.get_base_and_len(bcx); + let p = alloca(bcx, T_struct(~[T_ptr(llunit_ty), ccx.int_type])); + + debug!("adapt_borrowed_value: adapting %s to %s", + val_str(bcx.ccx().tn, val), + val_str(bcx.ccx().tn, p)); + + Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base])); + Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len])); + + // this isn't necessarily the type that rust would assign + // but it's close enough for trans purposes, as it will + // have the same runtime representation + let slice_ty = ty::mk_evec(bcx.tcx(), + {ty: unit_ty, mutbl: ast::m_imm}, + ty::vstore_slice(ty::re_static)); + + return DatumBlock {bcx: bcx, + datum: Datum {val: p, + mode: ByRef, + ty: slice_ty, + source: FromRvalue}}; + } + + _ => { + // Just take a reference. This is basically like trans_addr_of. + // + // NDM---this code is almost certainly wrong. I presume its + // purpose is auto-ref? What if an @T is autoref'd? No good. + let rptr_datum = datum.to_rptr(bcx); + return DatumBlock {bcx: bcx, datum: rptr_datum}; + } + } + + fn expr_is_borrowed(bcx: block, e: @ast::expr) -> bool { + bcx.tcx().borrowings.contains_key(e.id) + } +} + diff --git a/src/rustc/middle/trans/closure.rs b/src/rustc/middle/trans/closure.rs index 4e46286894d34..9cf4393346b77 100644 --- a/src/rustc/middle/trans/closure.rs +++ b/src/rustc/middle/trans/closure.rs @@ -17,6 +17,7 @@ use util::ppaux::ty_to_str; use syntax::ast_map::{path, path_mod, path_name}; use driver::session::session; use std::map::hashmap; +use datum::{Datum, INIT, ByRef, ByValue, FromLvalue}; // ___Good to know (tm)__________________________________________________ // @@ -87,25 +88,35 @@ use std::map::hashmap; // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -enum environment_value { - // Copy the value from this llvm ValueRef into the environment. - env_copy(ValueRef, ty::t, lval_kind), +enum EnvAction { + /// Copy the value from this llvm ValueRef into the environment. + EnvStore, - // Move the value from this llvm ValueRef into the environment. - env_move(ValueRef, ty::t, lval_kind), + /// Move the value from this llvm ValueRef into the environment. + EnvMove, - // Access by reference (used for blocks). - env_ref(ValueRef, ty::t, lval_kind), + /// Access by reference (used for stack closures). + EnvRef } -fn ev_to_str(ccx: @crate_ctxt, ev: environment_value) -> ~str { - match ev { - env_copy(v, t, _) => fmt!("copy(%s,%s)", val_str(ccx.tn, v), - ty_to_str(ccx.tcx, t)), - env_move(v, t, _) => fmt!("move(%s,%s)", val_str(ccx.tn, v), - ty_to_str(ccx.tcx, t)), - env_ref(v, t, _) => fmt!("ref(%s,%s)", val_str(ccx.tn, v), - ty_to_str(ccx.tcx, t)) +struct EnvValue { + action: EnvAction; + datum: Datum; +} + +impl EnvAction { + fn to_str() -> ~str { + match self { + EnvStore => ~"EnvStore", + EnvMove => ~"EnvMove", + EnvRef => ~"EnvRef" + } + } +} + +impl EnvValue { + fn to_str(ccx: @crate_ctxt) -> ~str { + fmt!("%s(%s)", self.action.to_str(), self.datum.to_str(ccx)) } } @@ -116,18 +127,18 @@ fn mk_tuplified_uniq_cbox_ty(tcx: ty::ctxt, cdata_ty: ty::t) -> ty::t { // Given a closure ty, emits a corresponding tuple ty fn mk_closure_tys(tcx: ty::ctxt, - bound_values: ~[environment_value]) + bound_values: ~[EnvValue]) -> ty::t { - let mut bound_tys = ~[]; - - // Compute the closed over data - for vec::each(bound_values) |bv| { - vec::push(bound_tys, match bv { - env_copy(_, t, _) => t, - env_move(_, t, _) => t, - env_ref(_, t, _) => t - }); - } + // determine the types of the values in the env. Note that this + // is the actual types that will be stored in the map, not the + // logical types as the user sees them, so by-ref upvars must be + // converted to ptrs. + let bound_tys = bound_values.map(|bv| { + match bv.action { + EnvStore | EnvMove => bv.datum.ty, + EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty) + } + }); let cdata_ty = ty::mk_tup(tcx, bound_tys); debug!("cdata_ty=%s", ty_to_str(tcx, cdata_ty)); return cdata_ty; @@ -136,7 +147,8 @@ fn mk_closure_tys(tcx: ty::ctxt, fn allocate_cbox(bcx: block, ck: ty::closure_kind, cdata_ty: ty::t) - -> result { + -> Result +{ let _icx = bcx.insn_ctxt("closure::allocate_cbox"); let ccx = bcx.ccx(), tcx = ccx.tcx; @@ -151,18 +163,16 @@ fn allocate_cbox(bcx: block, } // Allocate and initialize the box: - let {bcx, val} = match ck { + match ck { ty::ck_box => malloc_raw(bcx, cdata_ty, heap_shared), ty::ck_uniq => malloc_raw(bcx, cdata_ty, heap_exchange), ty::ck_block => { - let cbox_ty = tuplify_box_ty(tcx, cdata_ty); - let llbox = base::alloc_ty(bcx, cbox_ty); - nuke_ref_count(bcx, llbox); - {bcx: bcx, val: llbox} + let cbox_ty = tuplify_box_ty(tcx, cdata_ty); + let llbox = base::alloc_ty(bcx, cbox_ty); + nuke_ref_count(bcx, llbox); + rslt(bcx, llbox) } - }; - - return {bcx: bcx, val: val}; + } } type closure_result = { @@ -176,7 +186,7 @@ type closure_result = { // heap allocated closure that copies the upvars into environment. // Otherwise, it is stack allocated and copies pointers to the upvars. fn store_environment(bcx: block, - bound_values: ~[environment_value], + bound_values: ~[EnvValue], ck: ty::closure_kind) -> closure_result { let _icx = bcx.insn_ctxt("closure::store_environment"); let ccx = bcx.ccx(), tcx = ccx.tcx; @@ -185,7 +195,7 @@ fn store_environment(bcx: block, let cdata_ty = mk_closure_tys(tcx, bound_values); // allocate closure in the heap - let {bcx: bcx, val: llbox} = allocate_cbox(bcx, ck, cdata_ty); + let Result {bcx: bcx, val: llbox} = allocate_cbox(bcx, ck, cdata_ty); let mut temp_cleanups = ~[]; // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a @@ -200,43 +210,27 @@ fn store_environment(bcx: block, // Copy expr values into boxed bindings. let mut bcx = bcx; do vec::iteri(bound_values) |i, bv| { - debug!("Copy %s into closure", ev_to_str(ccx, bv)); + debug!("Copy %s into closure", bv.to_str(ccx)); if !ccx.sess.no_asm_comments() { add_comment(bcx, fmt!("Copy %s into closure", - ev_to_str(ccx, bv))); + bv.to_str(ccx))); } let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]); - match bv { - env_copy(val, ty, lv_owned) => { - let val1 = load_if_immediate(bcx, val, ty); - bcx = base::copy_val(bcx, INIT, bound_data, val1, ty); - } - env_copy(val, ty, lv_owned_imm) => { - bcx = base::copy_val(bcx, INIT, bound_data, val, ty); - } - env_copy(_, _, lv_temporary) => { - fail ~"cannot capture temporary upvar"; - } - env_move(val, ty, kind) => { - let src = {bcx:bcx, val:val, kind:kind}; - bcx = move_val(bcx, INIT, bound_data, src, ty); - } - env_ref(val, _, lv_owned) => { - debug!("> storing %s into %s", - val_str(bcx.ccx().tn, val), - val_str(bcx.ccx().tn, bound_data)); - Store(bcx, val, bound_data); - } - env_ref(val, _, lv_owned_imm) => { - let addr = do_spill_noroot(bcx, val); - Store(bcx, addr, bound_data); - } - env_ref(_, _, lv_temporary) => { - fail ~"cannot capture temporary upvar"; - } + + match bv.action { + EnvStore => { + bcx = bv.datum.store_to(bcx, INIT, bound_data); + } + EnvMove => { + bcx = bv.datum.move_to(bcx, INIT, bound_data); + } + EnvRef => { + Store(bcx, bv.datum.to_ref_llval(bcx), bound_data); + } } + } for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, cleanup); } @@ -252,56 +246,57 @@ fn build_closure(bcx0: block, include_ret_handle: Option) -> closure_result { let _icx = bcx0.insn_ctxt("closure::build_closure"); // If we need to, package up the iterator body to call - let mut env_vals = ~[]; - let mut bcx = bcx0; + let mut bcx = bcx0;; let ccx = bcx.ccx(), tcx = ccx.tcx; // Package up the captured upvars + let mut env_vals = ~[]; do vec::iter(cap_vars) |cap_var| { debug!("Building closure: captured variable %?", cap_var); - let lv = trans_local_var(bcx, cap_var.def); - let nid = ast_util::def_id_of_def(cap_var.def).node; - debug!("Node id is %s", - syntax::ast_map::node_id_to_str - (bcx.ccx().tcx.items, nid, - bcx.ccx().sess.parse_sess.interner)); - let mut ty = node_id_type(bcx, nid); + let datum = expr::trans_local_var(bcx, id, cap_var.def); match cap_var.mode { - capture::cap_ref => { - assert ck == ty::ck_block; - ty = ty::mk_mut_ptr(tcx, ty); - vec::push(env_vals, env_ref(lv.val, ty, lv.kind)); - } - capture::cap_copy => { - let mv = match ccx.maps.last_use_map.find(id) { - None => false, - Some(vars) => (*vars).contains(nid) - }; - if mv { vec::push(env_vals, env_move(lv.val, ty, lv.kind)); } - else { vec::push(env_vals, env_copy(lv.val, ty, lv.kind)); } - } - capture::cap_move => { - vec::push(env_vals, env_move(lv.val, ty, lv.kind)); - } - capture::cap_drop => { - assert lv.kind == lv_owned; - bcx = drop_ty(bcx, lv.val, ty); - bcx = zero_mem(bcx, lv.val, ty); - } + capture::cap_ref => { + assert ck == ty::ck_block; + vec::push(env_vals, EnvValue {action: EnvRef, + datum: datum}); + } + capture::cap_copy => { + vec::push(env_vals, EnvValue {action: EnvStore, + datum: datum}); + } + capture::cap_move => { + vec::push(env_vals, EnvValue {action: EnvMove, + datum: datum}); + } + capture::cap_drop => { + bcx = datum.drop_val(bcx); + datum.cancel_clean(bcx); + } } } + + // If this is a `for` loop body, add two special environment + // variables: do option::iter(include_ret_handle) |flagptr| { - let our_ret = match bcx.fcx.loop_ret { - Some({retptr, _}) => retptr, - None => bcx.fcx.llretptr + // Flag indicating we have returned (a by-ref bool): + let flag_datum = Datum {val: flagptr, ty: ty::mk_bool(tcx), + mode: ByRef, source: FromLvalue}; + vec::push(env_vals, EnvValue {action: EnvRef, + datum: flag_datum}); + + // Return value (we just pass a by-ref () and cast it later to + // the right thing): + let ret_true = match bcx.fcx.loop_ret { + Some({retptr, _}) => retptr, + None => bcx.fcx.llretptr }; - let nil_ret = PointerCast(bcx, our_ret, T_ptr(T_nil())); - vec::push(env_vals, - env_ref(flagptr, - ty::mk_mut_ptr(tcx, ty::mk_bool(tcx)), lv_owned)); - vec::push(env_vals, - env_ref(nil_ret, ty::mk_nil_ptr(tcx), lv_owned)); + let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil())); + let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx), + mode: ByRef, source: FromLvalue}; + vec::push(env_vals, EnvValue {action: EnvRef, + datum: ret_datum}); } + return store_environment(bcx, env_vals, ck); } @@ -351,9 +346,16 @@ fn trans_expr_fn(bcx: block, id: ast::node_id, cap_clause: ast::capture_clause, is_loop_body: Option>, - dest: dest) -> block { + dest: expr::Dest) -> block { let _icx = bcx.insn_ctxt("closure::trans_expr_fn"); - if dest == ignore { return bcx; } + + let dest_addr = match dest { + expr::SaveIn(p) => p, + expr::Ignore => { + return bcx; // closure construction is non-side-effecting + } + }; + let ccx = bcx.ccx(); let fty = node_id_type(bcx, id); let llfnty = type_of_fn_from_ty(ccx, fty); @@ -362,7 +364,7 @@ fn trans_expr_fn(bcx: block, let s = mangle_internal_name_by_path(ccx, sub_path); let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty); - let trans_closure_env = fn@(ck: ty::closure_kind) -> result { + let trans_closure_env = fn@(ck: ty::closure_kind) -> Result { let cap_vars = capture::compute_capture_vars(ccx.tcx, id, proto, cap_clause); let ret_handle = match is_loop_body { Some(x) => x, None => None }; @@ -377,25 +379,29 @@ fn trans_expr_fn(bcx: block, Store(bcx, C_bool(true), bcx.fcx.llretptr); } }); - {bcx: bcx, val: llbox} + rslt(bcx, llbox) }; - let {bcx: bcx, val: closure} = match proto { - ty::proto_vstore(ty::vstore_slice(_)) => - trans_closure_env(ty::ck_block), - ty::proto_vstore(ty::vstore_box) => - trans_closure_env(ty::ck_box), - ty::proto_vstore(ty::vstore_uniq) => - trans_closure_env(ty::ck_uniq), - ty::proto_bare => { - trans_closure(ccx, sub_path, decl, body, llfn, no_self, None, - id, |_fcx| { }, |_bcx| { }); - {bcx: bcx, val: C_null(T_opaque_box_ptr(ccx))} - } - ty::proto_vstore(ty::vstore_fixed(_)) => - fail ~"vstore_fixed unexpected" + let Result {bcx: bcx, val: closure} = match proto { + ty::proto_vstore(ty::vstore_slice(_)) => { + trans_closure_env(ty::ck_block) + } + ty::proto_vstore(ty::vstore_box) => { + trans_closure_env(ty::ck_box) + } + ty::proto_vstore(ty::vstore_uniq) => { + trans_closure_env(ty::ck_uniq) + } + ty::proto_bare => { + trans_closure(ccx, sub_path, decl, body, llfn, no_self, None, + id, |_fcx| { }, |_bcx| { }); + rslt(bcx, C_null(T_opaque_box_ptr(ccx))) + } + ty::proto_vstore(ty::vstore_fixed(_)) => { + fail ~"vstore_fixed unexpected" + } }; - fill_fn_pair(bcx, get_dest_addr(dest), llfn, closure); + fill_fn_pair(bcx, dest_addr, llfn, closure); return bcx; } @@ -440,12 +446,12 @@ fn make_opaque_cbox_take_glue( // Easy cases: let _icx = bcx.insn_ctxt("closure::make_opaque_cbox_take_glue"); match ck { - ty::ck_block => return bcx, - ty::ck_box => { - incr_refcnt_of_boxed(bcx, Load(bcx, cboxptr)); - return bcx; - } - ty::ck_uniq => { /* hard case: */ } + ty::ck_block => return bcx, + ty::ck_box => { + glue::incr_refcnt_of_boxed(bcx, Load(bcx, cboxptr)); + return bcx; + } + ty::ck_uniq => { /* hard case: */ } } // Hard case, a deep copy: @@ -467,20 +473,20 @@ fn make_opaque_cbox_take_glue( let malloc = ~"exchange_malloc"; let opaque_tydesc = PointerCast(bcx, tydesc, T_ptr(T_i8())); let rval = alloca_zeroed(bcx, T_ptr(T_i8())); - let bcx = trans_rtcall(bcx, malloc, ~[opaque_tydesc, sz], - save_in(rval)); + let bcx = callee::trans_rtcall(bcx, malloc, ~[opaque_tydesc, sz], + expr::SaveIn(rval)); let cbox_out = PointerCast(bcx, Load(bcx, rval), llopaquecboxty); call_memmove(bcx, cbox_out, cbox_in, sz); Store(bcx, cbox_out, cboxptr); // Take the (deeply cloned) type descriptor let tydesc_out = GEPi(bcx, cbox_out, [0u, abi::box_field_tydesc]); - let bcx = take_ty(bcx, tydesc_out, ty::mk_type(tcx)); + let bcx = glue::take_ty(bcx, tydesc_out, ty::mk_type(tcx)); // Take the data in the tuple let cdata_out = GEPi(bcx, cbox_out, [0u, abi::box_field_body]); - call_tydesc_glue_full(bcx, cdata_out, tydesc, - abi::tydesc_field_take_glue, None); + glue::call_tydesc_glue_full(bcx, cdata_out, tydesc, + abi::tydesc_field_take_glue, None); bcx } } @@ -492,15 +498,17 @@ fn make_opaque_cbox_drop_glue( -> block { let _icx = bcx.insn_ctxt("closure::make_opaque_cbox_drop_glue"); match ck { - ty::ck_block => bcx, - ty::ck_box => { - decr_refcnt_maybe_free(bcx, Load(bcx, cboxptr), - ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) - } - ty::ck_uniq => { - free_ty(bcx, cboxptr, + ty::ck_block => bcx, + ty::ck_box => { + glue::decr_refcnt_maybe_free( + bcx, Load(bcx, cboxptr), ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) - } + } + ty::ck_uniq => { + glue::free_ty( + bcx, cboxptr, + ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) + } } } @@ -526,14 +534,14 @@ fn make_opaque_cbox_free_glue( // Drop the tuple data then free the descriptor let cdata = GEPi(bcx, cbox, [0u, abi::box_field_body]); - call_tydesc_glue_full(bcx, cdata, tydesc, - abi::tydesc_field_drop_glue, None); + glue::call_tydesc_glue_full(bcx, cdata, tydesc, + abi::tydesc_field_drop_glue, None); // Free the ty descr (if necc) and the box itself match ck { - ty::ck_block => fail ~"Impossible", - ty::ck_box => trans_free(bcx, cbox), - ty::ck_uniq => trans_unique_free(bcx, cbox) + ty::ck_block => fail ~"Impossible", + ty::ck_box => glue::trans_free(bcx, cbox), + ty::ck_uniq => glue::trans_unique_free(bcx, cbox) } } } diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index 6edccf0edec1b..6fb547d757dd3 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -20,6 +20,7 @@ use metadata::{csearch}; use metadata::common::link_meta; use syntax::ast_map::path; use util::ppaux::ty_to_str; +use syntax::print::pprust::expr_to_str; use syntax::parse::token::ident_interner; use syntax::ast::ident; @@ -165,7 +166,11 @@ type crate_ctxt = { mut do_not_commit_warning_issued: bool}; // Types used for llself. -type val_self_data = {v: ValueRef, t: ty::t, is_owned: bool}; +struct ValSelfData { + v: ValueRef; + t: ty::t; + is_owned: bool; +} enum local_val { local_mem(ValueRef), local_imm(ValueRef), } @@ -201,7 +206,7 @@ type fn_ctxt = @{ mut llreturn: BasicBlockRef, // The 'self' value currently in use in this function, if there // is one. - mut llself: Option, + mut llself: Option, // The a value alloca'd for calls to upcalls.rust_personality. Used when // outputting the resume instruction. mut personality: Option, @@ -257,6 +262,25 @@ enum cleanup { clean_temp(ValueRef, fn@(block) -> block, cleantype), } +impl cleantype : cmp::Eq { + pure fn eq(&&other: cleantype) -> bool { + match self { + normal_exit_only => { + match other { + normal_exit_only => true, + _ => false + } + } + normal_exit_and_unwind => { + match other { + normal_exit_and_unwind => true, + _ => false + } + } + } + } +} + // Used to remember and reuse existing cleanup paths // target: none means the path ends in an resume instruction type cleanup_path = {target: Option, @@ -275,12 +299,12 @@ fn cleanup_type(cx: ty::ctxt, ty: ty::t) -> cleantype { } } -// This is not the same as base::root_value, which appears to be the vestigial -// remains of the previous GC regime. In the new GC, we can identify -// immediates on the stack without difficulty, but have trouble knowing where -// non-immediates are on the stack. For non-immediates, we must add an -// additional level of indirection, which allows us to alloca a pointer with -// the right addrspace. +// This is not the same as datum::Datum::root(), which is used to keep copies +// of @ values live for as long as a borrowed pointer to the interior exists. +// In the new GC, we can identify immediates on the stack without difficulty, +// but have trouble knowing where non-immediates are on the stack. For +// non-immediates, we must add an additional level of indirection, which +// allows us to alloca a pointer with the right addrspace. fn root_for_cleanup(bcx: block, v: ValueRef, t: ty::t) -> {root: ValueRef, rooted: bool} { let ccx = bcx.ccx(); @@ -305,11 +329,12 @@ fn add_clean(bcx: block, val: ValueRef, t: ty::t) { let cleanup_type = cleanup_type(bcx.tcx(), t); do in_scope_cx(bcx) |info| { vec::push(info.cleanups, - clean(|a| base::drop_ty_root(a, root, rooted, t), + clean(|a| glue::drop_ty_root(a, root, rooted, t), cleanup_type)); scope_clean_changed(info); } } + fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(cx.tcx(), ty) { return; } debug!("add_clean_temp_immediate(%s, %s, %s)", @@ -318,7 +343,7 @@ fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) { let cleanup_type = cleanup_type(cx.tcx(), ty); do in_scope_cx(cx) |info| { vec::push(info.cleanups, - clean_temp(val, |a| base::drop_ty_immediate(a, val, ty), + clean_temp(val, |a| glue::drop_ty_immediate(a, val, ty), cleanup_type)); scope_clean_changed(info); } @@ -332,15 +357,15 @@ fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) { let cleanup_type = cleanup_type(bcx.tcx(), t); do in_scope_cx(bcx) |info| { vec::push(info.cleanups, - clean_temp(val, |a| base::drop_ty_root(a, root, rooted, t), + clean_temp(val, |a| glue::drop_ty_root(a, root, rooted, t), cleanup_type)); scope_clean_changed(info); } } fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) { let free_fn = match heap { - heap_shared => |a| base::trans_free(a, ptr), - heap_exchange => |a| base::trans_unique_free(a, ptr) + heap_shared => |a| glue::trans_free(a, ptr), + heap_exchange => |a| glue::trans_unique_free(a, ptr) }; do in_scope_cx(cx) |info| { vec::push(info.cleanups, clean_temp(ptr, free_fn, @@ -355,12 +380,13 @@ fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) { // drop glue checks whether it is zero. fn revoke_clean(cx: block, val: ValueRef) { do in_scope_cx(cx) |info| { - do option::iter(vec::position(info.cleanups, |cu| { - match cu { - clean_temp(v, _, _) if v == val => true, - _ => false - } - })) |i| { + let cleanup_pos = vec::position( + info.cleanups, + |cu| match cu { + clean_temp(v, _, _) if v == val => true, + _ => false + }); + for cleanup_pos.each |i| { info.cleanups = vec::append(vec::slice(info.cleanups, 0u, i), vec::view(info.cleanups, @@ -384,6 +410,7 @@ enum block_kind { // to an implicit scope, for example, calls introduce an implicit scope in // which the arguments are evaluated and cleaned up. block_scope(scope_info), + // A non-scope block is a basic block created as a translation artifact // from translating code that expresses conditional logic rather than by // explicit { ... } block structure in the source language. It's called a @@ -480,11 +507,20 @@ fn mk_block(llbb: BasicBlockRef, parent: Option, -kind: block_kind, // First two args are retptr, env const first_real_arg: uint = 2u; -type result = {bcx: block, val: ValueRef}; -type result_t = {bcx: block, val: ValueRef, ty: ty::t}; +struct Result { + bcx: block; + val: ValueRef; +} + +fn rslt(bcx: block, val: ValueRef) -> Result { + Result {bcx: bcx, val: val} +} -fn rslt(bcx: block, val: ValueRef) -> result { - {bcx: bcx, val: val} +impl Result { + fn unpack(bcx: &mut block) -> ValueRef { + *bcx = self.bcx; + return self.val; + } } fn ty_str(tn: type_names, t: TypeRef) -> ~str { @@ -510,7 +546,12 @@ fn in_scope_cx(cx: block, f: fn(scope_info)) { let mut cur = cx; loop { match cur.kind { - block_scope(inf) => { f(inf); return; } + block_scope(inf) => { + debug!("in_scope_cx: selected cur=%s (cx=%s)", + cur.to_str(), cx.to_str()); + f(inf); + return; + } _ => () } cur = block_parent(cur); @@ -532,9 +573,40 @@ impl block { pure fn tcx() -> ty::ctxt { self.fcx.ccx.tcx } pure fn sess() -> session { self.fcx.ccx.sess } + fn node_id_to_str(id: ast::node_id) -> ~str { + ast_map::node_id_to_str(self.tcx().items, id, self.sess().intr()) + } + + fn expr_to_str(e: @ast::expr) -> ~str { + fmt!("expr(%d: %s)", e.id, expr_to_str(e, self.sess().intr())) + } + + fn expr_is_lval(e: @ast::expr) -> bool { + ty::expr_is_lval(self.tcx(), self.ccx().maps.method_map, e) + } + + fn expr_kind(e: @ast::expr) -> ty::ExprKind { + ty::expr_kind(self.tcx(), self.ccx().maps.method_map, e) + } + + fn def(nid: ast::node_id) -> ast::def { + match self.tcx().def_map.find(nid) { + Some(v) => v, + None => { + self.tcx().sess.bug(fmt!( + "No def associated with node id %?", nid)); + } + } + } + fn val_str(val: ValueRef) -> ~str { val_str(self.ccx().tn, val) } + + fn llty_str(llty: TypeRef) -> ~str { + ty_str(self.ccx().tn, llty) + } + fn ty_to_str(t: ty::t) -> ~str { ty_to_str(self.tcx(), t) } @@ -954,14 +1026,16 @@ fn C_zero_byte_arr(size: uint) -> ValueRef unsafe { elts.len() as c_uint); } -fn C_struct(elts: ~[ValueRef]) -> ValueRef unsafe { - return llvm::LLVMConstStruct(vec::unsafe::to_ptr(elts), - elts.len() as c_uint, False); +fn C_struct(elts: &[ValueRef]) -> ValueRef { + do vec::as_buf(elts) |ptr, len| { + llvm::LLVMConstStruct(ptr, len as c_uint, False) + } } -fn C_named_struct(T: TypeRef, elts: ~[ValueRef]) -> ValueRef unsafe { - return llvm::LLVMConstNamedStruct(T, vec::unsafe::to_ptr(elts), - elts.len() as c_uint); +fn C_named_struct(T: TypeRef, elts: &[ValueRef]) -> ValueRef { + do vec::as_buf(elts) |ptr, len| { + llvm::LLVMConstNamedStruct(T, ptr, len as c_uint) + } } fn C_array(ty: TypeRef, elts: ~[ValueRef]) -> ValueRef unsafe { @@ -1100,40 +1174,22 @@ fn node_id_type_params(bcx: block, id: ast::node_id) -> ~[ty::t] { } } -fn field_idx_strict(cx: ty::ctxt, sp: span, ident: ast::ident, - fields: ~[ty::field]) - -> uint { - match ty::field_idx(ident, fields) { - None => cx.sess.span_bug( - sp, fmt!("base expr doesn't appear to \ - have a field named %s", cx.sess.str_of(ident))), - Some(i) => i - } -} - fn dummy_substs(tps: ~[ty::t]) -> ty::substs { {self_r: Some(ty::re_bound(ty::br_self)), self_ty: None, tps: tps} } -impl cleantype : cmp::Eq { - pure fn eq(&&other: cleantype) -> bool { - match self { - normal_exit_only => { - match other { - normal_exit_only => true, - _ => false - } - } - normal_exit_and_unwind => { - match other { - normal_exit_and_unwind => true, - _ => false - } - } - } - } +fn struct_field(index: uint) -> [uint]/3 { + //! The GEPi sequence to access a field of a record/struct. + + [0, 0, index] +} + +fn struct_dtor() -> [uint]/2 { + //! The GEPi sequence to access the dtor of a struct. + + [0, 1] } // diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs index ec3e55fb2ae46..f877a5a24b2c3 100644 --- a/src/rustc/middle/trans/consts.rs +++ b/src/rustc/middle/trans/consts.rs @@ -59,9 +59,15 @@ fn const_deref(cx: @crate_ctxt, v: ValueRef) -> ValueRef { v } -fn const_get_elt(v: ValueRef, u: uint) -> ValueRef { - let u = u; - llvm::LLVMConstExtractValue(v, ptr::addr_of(u), 1 as c_uint) +fn const_get_elt(cx: @crate_ctxt, v: ValueRef, us: &[c_uint]) -> ValueRef { + let r = do vec::as_buf(us) |p, len| { + llvm::LLVMConstExtractValue(v, p, len as c_uint) + }; + + debug!("const_get_elt(v=%s, us=%?, r=%s)", + val_str(cx.tn, v), us, val_str(cx.tn, r)); + + return r; } fn const_autoderef(cx: @crate_ctxt, ty: ty::t, v: ValueRef) @@ -83,7 +89,7 @@ fn const_autoderef(cx: @crate_ctxt, ty: ty::t, v: ValueRef) fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { let _icx = cx.insn_ctxt("const_expr"); - match e.node { + return match e.node { ast::expr_lit(lit) => consts::const_lit(cx, e, *lit), ast::expr_binary(b, e1, e2) => { let te1 = const_expr(cx, e1); @@ -156,15 +162,15 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { let bt = ty::expr_ty(cx.tcx, base); let bv = const_expr(cx, base); let (bt, bv) = const_autoderef(cx, bt, bv); - let fields = match ty::get(bt).struct { - ty::ty_rec(fs) => fs, - ty::ty_class(did, ref substs) => - ty::class_items_as_mutable_fields(cx.tcx, did, substs), - _ => cx.sess.span_bug(e.span, - ~"field access on unknown type in const"), - }; - let ix = field_idx_strict(cx.tcx, e.span, field, fields); - const_get_elt(bv, ix) + do expr::with_field_tys(cx.tcx, bt) |_has_dtor, field_tys| { + let ix = ty::field_idx_strict(cx.tcx, field, field_tys); + + // Note: ideally, we'd use `struct_field()` here instead + // of hardcoding [0, ix], but we can't because it yields + // the wrong type and also inserts an extra 0 that is + // not needed in the constant variety: + const_get_elt(cx, bv, [0, ix as c_uint]) + } } ast::expr_index(base, index) => { @@ -189,8 +195,8 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { let llunitty = type_of::type_of(cx, unit_ty); let unit_sz = shape::llsize_of(cx, llunitty); - (const_deref(cx, const_get_elt(bv, 0)), - llvm::LLVMConstUDiv(const_get_elt(bv, 1), + (const_deref(cx, const_get_elt(cx, bv, [0])), + llvm::LLVMConstUDiv(const_get_elt(cx, bv, [1]), unit_sz)) }, _ => cx.sess.span_bug(base.span, @@ -240,27 +246,27 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { cx.sess.span_err(e.span, ~"const index-expr is out of bounds"); } - const_get_elt(arr, iv as uint) + const_get_elt(cx, arr, [iv as c_uint]) } ast::expr_cast(base, _) => { let ety = ty::expr_ty(cx.tcx, e), llty = type_of::type_of(cx, ety); let basety = ty::expr_ty(cx.tcx, base); let v = const_expr(cx, base); - match (base::cast_type_kind(basety), - base::cast_type_kind(ety)) { + match (expr::cast_type_kind(basety), + expr::cast_type_kind(ety)) { - (base::cast_integral, base::cast_integral) => { + (expr::cast_integral, expr::cast_integral) => { let s = if ty::type_is_signed(basety) { True } else { False }; llvm::LLVMConstIntCast(v, llty, s) } - (base::cast_integral, base::cast_float) => { + (expr::cast_integral, expr::cast_float) => { if ty::type_is_signed(basety) { llvm::LLVMConstSIToFP(v, llty) } else { llvm::LLVMConstUIToFP(v, llty) } } - (base::cast_float, base::cast_float) => { + (expr::cast_float, expr::cast_float) => { llvm::LLVMConstFPCast(v, llty) } - (base::cast_float, base::cast_integral) => { + (expr::cast_float, expr::cast_integral) => { if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) } else { llvm::LLVMConstFPToUI(v, llty) } } @@ -282,34 +288,25 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { ast::expr_tup(es) => { C_struct(es.map(|e| const_expr(cx, e))) } - ast::expr_struct(_, fs, _) => { + ast::expr_rec(fs, None) => { + C_struct([C_struct( + fs.map(|f| const_expr(cx, f.node.expr)))]) + } + ast::expr_struct(_, ref fs, _) => { let ety = ty::expr_ty(cx.tcx, e); - let llty = type_of::type_of(cx, ety); - let class_fields = - match ty::get(ety).struct { - ty::ty_class(clsid, _) => - ty::lookup_class_fields(cx.tcx, clsid), - _ => - cx.tcx.sess.span_bug(e.span, - ~"didn't resolve to a struct") - }; - let mut cs = ~[]; - for class_fields.each |class_field| { - let mut found = false; - for fs.each |field| { - if class_field.ident == field.node.ident { - found = true; - vec::push(cs, const_expr(cx, field.node.expr)); + let cs = do expr::with_field_tys(cx.tcx, ety) |_hd, field_tys| { + field_tys.map(|field_ty| { + match fs.find(|f| field_ty.ident == f.node.ident) { + Some(f) => const_expr(cx, f.node.expr), + None => { + cx.tcx.sess.span_bug( + e.span, ~"missing struct field"); + } } - } - if !found { - cx.tcx.sess.span_bug(e.span, ~"missing struct field"); - } - } - C_named_struct(llty, cs) - } - ast::expr_rec(fs, None) => { - C_struct(fs.map(|f| const_expr(cx, f.node.expr))) + }) + }; + let llty = type_of::type_of(cx, ety); + C_named_struct(llty, [C_struct(cs)]) } ast::expr_vec(es, ast::m_imm) => { let (v, _, _) = const_vec(cx, e, es); @@ -364,7 +361,7 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { } _ => cx.sess.span_bug(e.span, ~"bad constant expression type in consts::const_expr") - } + }; } fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) { diff --git a/src/rustc/middle/trans/controlflow.rs b/src/rustc/middle/trans/controlflow.rs new file mode 100644 index 0000000000000..83c5eb5f5f3c6 --- /dev/null +++ b/src/rustc/middle/trans/controlflow.rs @@ -0,0 +1,346 @@ +use lib::llvm::ValueRef; +use common::*; +use datum::*; +use base::*; + +fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. + +fn trans_block(bcx: block, b: ast::blk, dest: expr::Dest) -> block { + let _icx = bcx.insn_ctxt("trans_block"); + let mut bcx = bcx; + do block_locals(b) |local| { + bcx = alloc_local(bcx, local); + }; + for vec::each(b.node.stmts) |s| { + debuginfo::update_source_pos(bcx, b.span); + bcx = trans_stmt(bcx, *s); + } + match b.node.expr { + Some(e) => { + debuginfo::update_source_pos(bcx, e.span); + bcx = expr::trans_into(bcx, e, dest); + } + None => { + assert dest == expr::Ignore || bcx.unreachable; + } + } + return bcx; +} + +fn trans_if(bcx: block, + cond: @ast::expr, + thn: ast::blk, + els: Option<@ast::expr>, + dest: expr::Dest) + -> block +{ + debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)", + bcx.to_str(), bcx.expr_to_str(cond), thn.node.id, + dest.to_str(bcx.ccx())); + let _indenter = indenter(); + + let _icx = bcx.insn_ctxt("trans_if"); + let Result {bcx, val: cond_val} = + expr::trans_to_appropriate_llval(bcx, cond); + + let then_bcx_in = scope_block(bcx, thn.info(), ~"then"); + let else_bcx_in = scope_block(bcx, els.info(), ~"else"); + CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); + + debug!("then_bcx_in=%s, else_bcx_in=%s", + then_bcx_in.to_str(), else_bcx_in.to_str()); + + let then_bcx_out = trans_block(then_bcx_in, thn, dest); + 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_out = match els { + Some(elexpr) => { + match elexpr.node { + ast::expr_if(_, _, _) => { + let elseif_blk = ast_util::block_from_expr(elexpr); + trans_block(else_bcx_in, elseif_blk, dest) + } + ast::expr_block(blk) => { + trans_block(else_bcx_in, blk, dest) + } + // would be nice to have a constraint on ifs + _ => bcx.tcx().sess.bug(~"strange alternative in if") + } + } + _ => else_bcx_in + }; + let else_bcx_out = trans_block_cleanups(else_bcx_out, + block_cleanups(else_bcx_in)); + return join_blocks(bcx, ~[then_bcx_out, else_bcx_out]); + +} + +fn join_blocks(parent_bcx: block, in_cxs: ~[block]) -> block { + let out = sub_block(parent_bcx, ~"join"); + let mut reachable = false; + for vec::each(in_cxs) |bcx| { + if !bcx.unreachable { + Br(bcx, out.llbb); + reachable = true; + } + } + if !reachable { + Unreachable(out); + } + return out; +} + +fn trans_while(bcx: block, cond: @ast::expr, body: ast::blk) + -> block { + let _icx = bcx.insn_ctxt("trans_while"); + let next_bcx = sub_block(bcx, ~"while next"); + + // bcx + // | + // loop_bcx + // | + // cond_bcx_in <--------+ + // | | + // cond_bcx_out | + // | | | + // | body_bcx_in | + // +------+ | | + // | body_bcx_out --+ + // next_bcx + + let loop_bcx = loop_scope_block(bcx, next_bcx, ~"`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); + + // compile the condition + let Result {bcx: cond_bcx_out, val: cond_val} = + expr::trans_to_appropriate_llval(cond_bcx_in, cond); + 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); + + // 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); + + return next_bcx; +} + +fn trans_loop(bcx:block, body: ast::blk) -> block { + let _icx = bcx.insn_ctxt("trans_loop"); + let next_bcx = sub_block(bcx, ~"next"); + let body_bcx_in = loop_scope_block(bcx, next_bcx, ~"`loop`", body.info()); + 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; +} + +fn trans_log(log_ex: @ast::expr, + lvl: @ast::expr, + bcx: block, + e: @ast::expr) -> block +{ + let _icx = bcx.insn_ctxt("trans_log"); + let ccx = bcx.ccx(); + let mut bcx = bcx; + if ty::type_is_bot(expr_ty(bcx, lvl)) { + return expr::trans_into(bcx, lvl, expr::Ignore); + } + + let modpath = vec::append( + ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))], + vec::filter(bcx.fcx.path, |e| + match e { path_mod(_) => true, _ => false } + )); + let modname = path_str(ccx.sess, modpath); + + let global = if ccx.module_data.contains_key(modname) { + ccx.module_data.get(modname) + } else { + let s = link::mangle_internal_name_by_path_and_seq( + ccx, modpath, ~"loglevel"); + let global = str::as_c_str(s, |buf| { + llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf) + }); + llvm::LLVMSetGlobalConstant(global, False); + llvm::LLVMSetInitializer(global, C_null(T_i32())); + lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage); + ccx.module_data.insert(modname, global); + global + }; + let current_level = Load(bcx, global); + let level = unpack_result!(bcx, { + do with_scope_result(bcx, lvl.info(), ~"level") |bcx| { + expr::trans_to_appropriate_llval(bcx, lvl) + } + }); + + let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level); + do with_cond(bcx, llenabled) |bcx| { + do with_scope(bcx, log_ex.info(), ~"log") |bcx| { + let mut bcx = bcx; + + // Translate the value to be logged + let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e)); + let tydesc = get_tydesc_simple(ccx, val_datum.ty); + + // Call the polymorphic log function + let val = val_datum.to_ref_llval(bcx); + let val = PointerCast(bcx, val, T_ptr(T_i8())); + Call(bcx, ccx.upcalls.log_type, [tydesc, val, level]); + bcx + } + } +} + +fn trans_break_cont(bcx: block, to_end: bool) + -> block { + let _icx = bcx.insn_ctxt("trans_break_cont"); + // Locate closest loop block, outputting cleanup as we go. + let mut unwind = bcx; + let mut target; + loop { + match unwind.kind { + block_scope({loop_break: Some(brk), _}) => { + target = if to_end { + brk + } else { + unwind + }; + break; + } + _ => () + } + 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); + cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + Unreachable(bcx); + return bcx; + } + }; + } + cleanup_and_Br(bcx, unwind, target.llbb); + Unreachable(bcx); + return bcx; +} + +fn trans_break(bcx: block) -> block { + return trans_break_cont(bcx, true); +} + +fn trans_cont(bcx: block) -> block { + return trans_break_cont(bcx, false); +} + +fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block { + let _icx = bcx.insn_ctxt("trans_ret"); + let mut bcx = bcx; + let retptr = match copy bcx.fcx.loop_ret { + Some({flagptr, retptr}) => { + // This is a loop body return. Must set continue flag (our retptr) + // to false, return flag to true, and then store the value in the + // parent's retptr. + Store(bcx, C_bool(true), flagptr); + Store(bcx, C_bool(false), bcx.fcx.llretptr); + match e { + Some(x) => PointerCast(bcx, retptr, + T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))), + None => retptr + } + } + None => bcx.fcx.llretptr + }; + match e { + Some(x) => { + bcx = expr::trans_into(bcx, x, expr::SaveIn(retptr)); + } + _ => () + } + cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + Unreachable(bcx); + return bcx; +} +fn trans_check_expr(bcx: block, chk_expr: @ast::expr, + pred_expr: @ast::expr, s: ~str) -> block { + let _icx = bcx.insn_ctxt("trans_check_expr"); + let expr_str = s + ~" " + expr_to_str(pred_expr, bcx.ccx().sess.intr()) + + ~" failed"; + let Result {bcx, val} = { + do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| { + expr::trans_to_appropriate_llval(bcx, pred_expr) + } + }; + do with_cond(bcx, Not(bcx, val)) |bcx| { + trans_fail(bcx, Some(pred_expr.span), expr_str) + } +} + +fn trans_fail_expr(bcx: block, + sp_opt: Option, + fail_expr: Option<@ast::expr>) -> block { + let _icx = bcx.insn_ctxt("trans_fail_expr"); + let mut bcx = bcx; + match fail_expr { + Some(arg_expr) => { + let ccx = bcx.ccx(), tcx = ccx.tcx; + let arg_datum = unpack_datum!( + bcx, expr::trans_to_datum(bcx, arg_expr)); + + if ty::type_is_str(arg_datum.ty) { + let (lldata, _lllen) = arg_datum.get_base_and_len(bcx); + return trans_fail_value(bcx, sp_opt, lldata); + } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) { + return bcx; + } else { + bcx.sess().span_bug( + arg_expr.span, ~"fail called with unsupported type " + + ppaux::ty_to_str(tcx, arg_datum.ty)); + } + } + _ => return trans_fail(bcx, sp_opt, ~"explicit failure") + } +} + +fn trans_fail(bcx: block, sp_opt: Option, fail_str: ~str) + -> block +{ + let _icx = bcx.insn_ctxt("trans_fail"); + let V_fail_str = C_cstr(bcx.ccx(), fail_str); + return trans_fail_value(bcx, sp_opt, V_fail_str); +} + +fn trans_fail_value(bcx: block, sp_opt: Option, V_fail_str: ValueRef) + -> block +{ + let _icx = bcx.insn_ctxt("trans_fail_value"); + let ccx = bcx.ccx(); + let {V_filename, V_line} = match sp_opt { + Some(sp) => { + let sess = bcx.sess(); + let loc = codemap::lookup_char_pos(sess.parse_sess.cm, sp.lo); + {V_filename: C_cstr(bcx.ccx(), loc.file.name), + V_line: loc.line as int} + } + None => { + {V_filename: C_cstr(bcx.ccx(), ~""), + V_line: 0} + } + }; + let V_str = PointerCast(bcx, V_fail_str, T_ptr(T_i8())); + let V_filename = PointerCast(bcx, V_filename, T_ptr(T_i8())); + let args = ~[V_str, V_filename, C_int(ccx, V_line)]; + let bcx = callee::trans_rtcall(bcx, ~"fail", args, expr::Ignore); + Unreachable(bcx); + return bcx; +} diff --git a/src/rustc/middle/trans/datum.rs b/src/rustc/middle/trans/datum.rs new file mode 100644 index 0000000000000..4f3591d99cac0 --- /dev/null +++ b/src/rustc/middle/trans/datum.rs @@ -0,0 +1,703 @@ +/*! + * + * 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. Intead 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 sources + * + * Each datum carries with it an idea of its "source". This indicates + * the kind of expression from which the datum originated. The source + * affects what happens when the datum is stored or moved. + * + * There are three options: + * + * 1. `FromRvalue`: This value originates from some temporary rvalue. + * This is therefore the owning reference to the datum. If the + * datum is stored, then, it will be *moved* into its new home. + * Furthermore, we will not zero out the datum but rather use + * `revoke_clean()` to cancel any cleanup. + * + * 2. `FromLvalue`: This value originates from an lvalue. If the datum + * is stored, it will be *copied* into its new home. If the datum + * is moved, it will be zeroed out. + * + * 3. `FromLastUseLvalue`: The same as FromLvalue, except that it + * originates from the *last use* of an lvalue. If the datum is + * stored, then, it will be moved (and zeroed out). + * + * # Storing, copying, and moving + * + * There are three kinds of methods for moving the value into a new + * location. *Storing* a datum is probably the one you want to reach + * for first: it is used when you will no longer use the datum and + * would like to place it somewhere. It may translate to a copy or a + * move, depending on the source of the datum. After a store, the + * datum may or may not be usable anymore, so you must assume it is + * not. + * + * Sometimes, though, you want to use an explicit copy or move. A + * copy copies the data from the datum into a new location and + * executes the take glue on that location, thus leaving the datum + * valid for further use. Moving, in contrast, copies the data into + * the new location and then cancels any cleanups on the current datum + * (as appropriate for the source). No glue code is executed. After + * a move, the datum is no longer usable. + * + * # 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. */ + +use lib::llvm::ValueRef; +use base::*; +use common::*; +use build::*; +use util::ppaux::ty_to_str; +use util::common::indenter; + +enum CopyAction { + INIT, + DROP_EXISTING +} + +struct Datum { + /// The llvm value. This is either a pointer to the Rust value or + /// the value itself, depending on `mode` below. + val: ValueRef; + + /// The rust type of the value. + ty: ty::t; + + /// Indicates whether this is by-ref or by-value. + mode: DatumMode; + + /// How did this value originate? This is particularly important + /// if the value is MOVED or prematurely DROPPED, because it + /// describes how to cancel the cleanup that was scheduled before. + /// See the def'n of the `DatumSource` type. + source: DatumSource; +} + +struct DatumBlock { + bcx: block; + datum: Datum; +} + +enum DatumMode { + /// `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 { + fn is_by_ref() -> bool { + match self { ByRef => true, ByValue => false } + } + + fn is_by_value() -> bool { + match self { ByRef => false, ByValue => true } + } +} + +/// See `Datum Sources` section at the head of this module. +enum DatumSource { + FromRvalue, + FromLvalue, + FromLastUseLvalue, +} + +impl DatumSource { + fn is_rvalue() -> bool { + match self { + FromRvalue => true, + FromLvalue | FromLastUseLvalue => false + } + } + + fn is_any_lvalue() -> bool { + match self { + FromRvalue => false, + FromLvalue | FromLastUseLvalue => true + } + } +} + +fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { + return Datum {val: val, ty: ty, + mode: ByValue, source: FromRvalue}; +} + +fn immediate_rvalue_bcx(bcx: block, val: ValueRef, ty: ty::t) -> DatumBlock { + return DatumBlock {bcx: bcx, datum: immediate_rvalue(val, ty)}; +} + +fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum { + /*! + * + * Allocates temporary space on the stack using alloca() and + * returns a by-ref Datum pointing to it. You must arrange + * any cleanups etc yourself! */ + + let scratch = alloc_ty(bcx, ty); + if zero { zero_mem(bcx, scratch, ty); } + Datum { val: scratch, ty: ty, mode: ByRef, source: FromRvalue } +} + +impl Datum { + fn store_will_move() -> bool { + match self.source { + FromRvalue | FromLastUseLvalue => true, + FromLvalue => false + } + } + + fn store_to(bcx: block, action: CopyAction, dst: ValueRef) -> block { + /*! + * + * Stores this value into its final home. This moves if + * possible, but copies otherwise. */ + + if self.store_will_move() { + self.move_to(bcx, action, dst) + } else { + self.copy_to(bcx, action, dst) + } + } + + fn store_to_dest(bcx: block, dest: expr::Dest) -> block { + match dest { + expr::Ignore => { + return bcx; + } + expr::SaveIn(addr) => { + return self.store_to(bcx, INIT, addr); + } + } + } + + fn store_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block { + debug!("store_to_datum(self=%s, action=%?, datum=%s)", + self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx())); + assert datum.mode.is_by_ref(); + self.store_to(bcx, action, datum.val) + } + + fn move_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block { + assert datum.mode.is_by_ref(); + self.move_to(bcx, action, datum.val) + } + + fn copy_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block { + assert datum.mode.is_by_ref(); + self.copy_to(bcx, action, datum.val) + } + + fn copy_to(bcx: block, action: CopyAction, dst: ValueRef) -> block { + /*! + * + * 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. */ + + let _icx = bcx.insn_ctxt("copy_to"); + + debug!("copy_to(self=%s, action=%?, dst=%s)", + self.to_str(bcx.ccx()), action, bcx.val_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); + do 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) + } + } + + fn copy_to_no_check(bcx: block, action: CopyAction, + dst: ValueRef) -> block + { + /*! + * + * A helper for `copy_to()` which does not check to see if we + * are copying to/from the same value. */ + + let _icx = bcx.insn_ctxt("copy_to_no_check"); + let mut bcx = bcx; + + if action == DROP_EXISTING { + bcx = glue::drop_ty(bcx, dst, self.ty); + } + + match self.mode { + ByValue => { + Store(bcx, self.val, dst); + } + ByRef => { + memmove_ty(bcx, dst, self.val, self.ty); + } + } + + 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. + // + // FIXME (#839): We always zero out the source. Ideally we would + // detect the case where a variable is always deinitialized by + // block exit and thus doesn't need to be dropped. + fn move_to(bcx: block, action: CopyAction, dst: ValueRef) -> block { + let _icx = bcx.insn_ctxt("move_to"); + let mut bcx = bcx; + + debug!("move_to(self=%s, action=%?, dst=%s)", + self.to_str(bcx.ccx()), action, bcx.val_str(dst)); + + if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) { + return bcx; + } + + if action == DROP_EXISTING { + bcx = glue::drop_ty(bcx, dst, self.ty); + } + + match self.mode { + ByRef => { + glue::memmove_ty(bcx, dst, self.val, self.ty); + } + ByValue => { + Store(bcx, self.val, dst); + } + } + + self.cancel_clean(bcx); + + return bcx; + } + + fn add_clean(bcx: block) { + /*! + * + * Schedules this datum for cleanup in `bcx`. The datum + * must be an rvalue. */ + + assert self.source.is_rvalue(); + match self.mode { + ByValue => { + add_clean_temp_immediate(bcx, self.val, self.ty); + } + ByRef => { + add_clean_temp_mem(bcx, self.val, self.ty); + } + } + } + + fn cancel_clean(bcx: block) { + if ty::type_needs_drop(bcx.tcx(), self.ty) { + match self.source { + FromRvalue => { + revoke_clean(bcx, self.val); + } + FromLvalue | FromLastUseLvalue => { + // Lvalues which potentially need to be dropped + // must be passed by ref, so that we can zero them + // out. + assert self.mode.is_by_ref(); + zero_mem(bcx, self.val, self.ty); + } + } + } + } + + fn to_str(ccx: &crate_ctxt) -> ~str { + fmt!("Datum { val=%s, ty=%s, mode=%?, source=%? }", + val_str(ccx.tn, self.val), + ty_to_str(ccx.tcx, self.ty), + self.mode, + self.source) + } + + fn to_value_llval(bcx: block) -> ValueRef { + /*! + * + * Yields the value itself. */ + + match self.mode { + ByValue => self.val, + ByRef => Load(bcx, self.val) + } + } + + fn to_ref(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. */ + + match self.mode { + ByRef => self, + ByValue => { + Datum {val: self.to_ref_llval(bcx), mode: ByRef, + ty: self.ty, source: FromRvalue} + } + } + } + + fn to_ref_llval(bcx: block) -> ValueRef { + match self.mode { + ByRef => self.val, + ByValue => { + let slot = alloc_ty(bcx, self.ty); + Store(bcx, self.val, slot); + slot + } + } + } + + fn to_appropriate_llval(bcx: block) -> ValueRef { + /*! + * + * Yields something that is by value if the type is immediate + * and by ref otherwise. */ + + if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) { + self.to_value_llval(bcx) + } else if ty::type_is_immediate(self.ty) { + self.to_value_llval(bcx) + } else { + self.to_ref_llval(bcx) + } + } + + fn GEPi(bcx: block, ixs: &[uint], ty: ty::t) -> Datum { + let base_val = self.to_ref_llval(bcx); + Datum { + val: GEPi(bcx, base_val, ixs), + mode: ByRef, + ty: ty, + source: FromLvalue + } + } + + fn root(bcx: block, scope_id: ast::node_id) { + /*! + * + * 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 + * away until we exit the scope `scope_id`. */ + + debug!("root(scope_id=%?, self=%?)", + scope_id, self.to_str(bcx.ccx())); + + if bcx.sess().trace() { + trans_trace( + bcx, None, + fmt!("preserving until end of scope %d", scope_id)); + } + + let scratch = scratch_datum(bcx, self.ty, true); + self.copy_to_datum(bcx, INIT, scratch); + base::add_root_cleanup(bcx, scope_id, scratch.val, scratch.ty); + } + + fn drop_val(bcx: block) -> block { + 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) + }; + } + + fn box_body(bcx: block) -> Datum { + /*! + * + * 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 = match ty::get(self.ty).struct { + ty::ty_box(mt) | ty::ty_uniq(mt) => mt.ty, + _ => { + bcx.tcx().sess.bug(fmt!( + "box_body() invoked on non-box type %s", + ty_to_str(bcx.tcx(), self.ty))); + } + }; + + let ptr = self.to_value_llval(bcx); + let body = opaque_box_body(bcx, content_ty, ptr); + Datum {val: body, ty: content_ty, mode: ByRef, source: FromLvalue} + } + + fn to_rptr(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::re_static, + self.ty); + Datum {val: llval, ty: rptr_ty, + mode: ByValue, source: FromRvalue} + } + + fn try_deref( + bcx: block, // block wherein to generate insn's + expr_id: ast::node_id, // id of expr being deref'd + derefs: uint, // number of times deref'd already + is_auto: bool) // if true, only deref if auto-derefable + -> Option + { + let ccx = bcx.ccx(); + + debug!("try_deref(expr_id=%d, derefs=%?, is_auto=%b, self=%?)", + expr_id, derefs, is_auto, self.to_str(bcx.ccx())); + let _indenter = indenter(); + + // root the autoderef'd value, if necessary: + // + // (Note: root'd values are always boxes) + match ccx.maps.root_map.find({id:expr_id, derefs:derefs}) { + None => (), + Some(scope_id) => { + self.root(bcx, scope_id); + } + } + + match ty::get(self.ty).struct { + ty::ty_box(_) | ty::ty_uniq(_) => { + return Some(self.box_body(bcx)); + } + ty::ty_ptr(mt) => { + if is_auto { // unsafe ptrs are not AUTO-derefable + return None; + } else { + return Some(deref_ptr(bcx, &self, mt.ty)); + } + } + ty::ty_rptr(_, mt) => { + return Some(deref_ptr(bcx, &self, mt.ty)); + } + ty::ty_enum(did, ref substs) => { + // Check whether this enum is a newtype enum: + let variants = ty::enum_variants(ccx.tcx, did); + if (*variants).len() != 1u || variants[0].args.len() != 1u { + return None; + } + + let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]); + return match self.mode { + ByRef => { + // Recast lv.val as a pointer to the newtype + // rather than a ptr to the enum type. + let llty = T_ptr(type_of::type_of(ccx, ty)); + Some(Datum { + val: PointerCast(bcx, self.val, llty), + ty: ty, + mode: ByRef, + source: FromLvalue + }) + } + ByValue => { + // Actually, this case cannot happen right + // now, because enums are never immediate. + // But in principle newtype'd immediate + // values should be immediate, and in that + // case the * would be a no-op except for + // changing the type, so I am putting this + // code in place here to do the right + // thing if this change ever goes through. + assert ty::type_is_immediate(ty); + Some(Datum {ty: ty, ..self}) + } + }; + } + _ => { // not derefable. + return None; + } + } + + fn deref_ptr(bcx: block, lv: &Datum, ty: ty::t) -> Datum { + Datum { + val: lv.to_value_llval(bcx), + ty: ty, + mode: ByRef, + source: FromLvalue // *p is an lvalue + } + } + } + + fn deref(bcx: block, + expr: @ast::expr, // the expression whose value is being deref'd + derefs: uint) -> Datum { + match self.try_deref(bcx, expr.id, derefs, false) { + Some(lvres) => lvres, + None => { + bcx.ccx().sess.span_bug( + expr.span, ~"Cannot deref this expression"); + } + } + } + + fn autoderef(bcx: block, + expr_id: ast::node_id, + max: uint) -> Datum { + let _icx = bcx.insn_ctxt("autoderef"); + + debug!("autoderef(expr_id=%d, max=%?, self=%?)", + expr_id, max, self.to_str(bcx.ccx())); + let _indenter = indenter(); + + let mut datum = self; + let mut derefs = 0u; + while derefs < max { + derefs += 1u; + match datum.try_deref(bcx, expr_id, derefs, true) { + None => break, + Some(datum_deref) => { + datum = datum_deref; + } + } + } + + // 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; + datum + } + + fn get_base_and_len(bcx: block) -> (ValueRef, ValueRef) { + tvec::get_base_and_len(bcx, self.to_appropriate_llval(bcx), self.ty) + } + + fn to_result(bcx: block) -> common::Result { + rslt(bcx, self.to_appropriate_llval(bcx)) + } +} + +impl DatumBlock { + fn unpack(bcx: &mut block) -> Datum { + *bcx = self.bcx; + return self.datum; + } + + fn assert_by_ref() -> DatumBlock { + assert self.datum.mode.is_by_ref(); + self + } + + fn drop_val() -> block { + self.datum.drop_val(self.bcx) + } + + fn store_to(action: CopyAction, dst: ValueRef) -> block { + self.datum.store_to(self.bcx, action, dst) + } + + fn copy_to(action: CopyAction, dst: ValueRef) -> block { + self.datum.copy_to(self.bcx, action, dst) + } + + fn move_to(action: CopyAction, dst: ValueRef) -> block { + self.datum.move_to(self.bcx, action, dst) + } + + fn to_value_llval() -> ValueRef { + self.datum.to_value_llval(self.bcx) + } + + fn to_result() -> common::Result { + rslt(self.bcx, self.datum.to_appropriate_llval(self.bcx)) + } + + fn ccx() -> @crate_ctxt { + self.bcx.ccx() + } + + fn tcx() -> ty::ctxt { + self.bcx.tcx() + } + + fn to_str() -> ~str { + self.datum.to_str(self.ccx()) + } +} + +impl CopyAction : cmp::Eq { + pure fn eq(&&other: CopyAction) -> bool { + match (self, other) { + (INIT, INIT) => true, + (DROP_EXISTING, DROP_EXISTING) => true, + (INIT, _) => false, + (DROP_EXISTING, _) => false, + } + } +} diff --git a/src/rustc/middle/trans/expr.rs b/src/rustc/middle/trans/expr.rs new file mode 100644 index 0000000000000..8b2acf1c0b5aa --- /dev/null +++ b/src/rustc/middle/trans/expr.rs @@ -0,0 +1,1371 @@ +/*! + +# Translation of expressions. + +## User's guide + +If you wish to translate an expression, there are two basic modes: + +1. `trans_into(block, expr, Dest) -> block` +2. `trans_to_datum(block, expr) -> DatumBlock` + +`trans_into()` is the preferred form to use whenever possible. It +evaluates the expression and stores its 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. + +Sometimes, though, you just want to evaluate the expression into +some memory location so you can go and inspect it (e.g., a `match` +expression). In that case, `trans_to_datum()` is your friend. It +will 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 (almost, see [1]). 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). + +## More specific functions + +The two functions above are the most general and can handle any +situation, but there are a few other functions that are useful +in specific scenarios: + +- `trans_to_appropriate_llval()` can be used when you just want + an LLVM ValueRef. It will return by value if the value in + question is immediate, or by ref otherwise. +- `trans_lvalue()` is exactly like `trans_to_datum()` but it only + works on lvalues. This is mostly used as an assertion for those + places where only an lvalue is expected. It also guarantees that + you will get a by-ref Datum back (almost, see [1]). +- `trans_local_var()` can be used to trans a ref to a local variable + that is not an expression. + +## 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()` or `trans_to_appropriate_llval()` +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`). + +## Caveats + +[1] Actually, some lvalues are only stored by value and not by +reference. An example (as of this writing) would be immutable +arguments or pattern bindings of immediate type. However, mutable +lvalues are *never* stored by value. + +*/ + +use ty::class_items_as_mutable_fields; +use lib::llvm::ValueRef; +use common::*; +use datum::*; +use base::*; +use syntax::print::pprust::{expr_to_str}; +use util::ppaux::ty_to_str; +use util::common::indenter; + +// The primary two functions for translating expressions: +export trans_to_datum, trans_into; +export Dest, SaveIn, Ignore; +export cast_type_kind; +export cast_kind, cast_pointer, cast_integral, cast_float; +export cast_enum, cast_other; + +// More specific variants than trans_to_datum/trans_into that are useful +// in some scenarios: +export trans_to_appropriate_llval, trans_lvalue, trans_local_var; + +// Other helpers: +export with_field_tys; + +// Destinations + +// These are passed around by the code generating functions to track the +// destination of a computation's value. + +fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. + +enum Dest { + SaveIn(ValueRef), + Ignore, +} + +impl Dest { + fn to_str(ccx: @crate_ctxt) -> ~str { + match self { + SaveIn(v) => fmt!("SaveIn(%s)", val_str(ccx.tn, v)), + Ignore => ~"Ignore" + } + } +} + +impl Dest : cmp::Eq { + pure fn eq(&&other: Dest) -> bool { + match (self, other) { + (SaveIn(e0a), SaveIn(e0b)) => e0a == e0b, + (Ignore, Ignore) => true, + (SaveIn(*), _) => false, + (Ignore, _) => false, + } + } +} + +fn trans_to_appropriate_llval(bcx: block, + expr: @ast::expr) -> common::Result { + let mut bcx = bcx; + let datum = unpack_datum!(bcx, trans_to_datum(bcx, expr)); + debug!("trans_to_appropriate_llval(): datum=%s", datum.to_str(bcx.ccx())); + rslt(bcx, datum.to_appropriate_llval(bcx)) +} + +fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { + /*! + * + * Translates an expression into a datum. If this expression + * is an rvalue, this will result in a temporary value being + * created. If you already know where the result should be stored, + * you should use `trans_into()` instead. */ + + let mut bcx = bcx; + + debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr)); + let _indenter = indenter(); + + debuginfo::update_source_pos(bcx, expr.span); + + match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) { + ty::LvalueExpr => { + return trans_lvalue(bcx, expr); + } + + ty::RvalueDatumExpr => { + let datum = unpack_datum!(bcx, trans_rvalue_datum(bcx, expr)); + datum.add_clean(bcx); + return DatumBlock {bcx: bcx, datum: datum}; + } + + ty::RvalueStmtExpr => { + bcx = trans_rvalue_stmt(bcx, expr); + return nil(bcx, expr_ty(bcx, expr)); + } + + ty::RvalueDpsExpr => { + let ty = expr_ty(bcx, expr); + if ty::type_is_nil(ty) || ty::type_is_bot(ty) { + bcx = trans_rvalue_dps(bcx, expr, Ignore); + return nil(bcx, ty); + } else { + let scratch = scratch_datum(bcx, ty, false); + bcx = trans_rvalue_dps(bcx, expr, SaveIn(scratch.val)); + scratch.add_clean(bcx); + return DatumBlock {bcx: bcx, datum: scratch}; + } + } + } + + fn nil(bcx: block, ty: ty::t) -> DatumBlock { + let datum = immediate_rvalue(C_nil(), ty); + DatumBlock {bcx: bcx, datum: datum} + } +} + +fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block { + let ty = expr_ty(bcx, expr); + + debug!("trans_into(expr=%s, dest=%s)", + bcx.expr_to_str(expr), + dest.to_str(bcx.ccx())); + let _indenter = indenter(); + + debuginfo::update_source_pos(bcx, expr.span); + + let dest = { + if ty::type_is_nil(ty) || ty::type_is_bot(ty) { + Ignore + } else { + dest + } + }; + + let kind = bcx.expr_kind(expr); + debug!("expr kind = %?", kind); + match kind { + ty::LvalueExpr => { + let datumblock = trans_lvalue(bcx, expr); + match dest { + Ignore => datumblock.bcx, + SaveIn(lldest) => datumblock.store_to(INIT, lldest) + } + } + ty::RvalueDatumExpr => { + let datumblock = trans_rvalue_datum(bcx, expr); + match dest { + Ignore => datumblock.drop_val(), + SaveIn(lldest) => datumblock.store_to(INIT, lldest) + } + } + ty::RvalueDpsExpr => { + return trans_rvalue_dps(bcx, expr, dest); + } + ty::RvalueStmtExpr => { + return trans_rvalue_stmt(bcx, expr); + } + } +} + +fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock { + let _icx = bcx.insn_ctxt("trans_rvalue_datum"); + + trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); + + match expr.node { + ast::expr_vstore(contents, ast::vstore_box) => { + return tvec::trans_uniq_or_managed_vstore(bcx, heap_shared, + expr, contents); + } + ast::expr_vstore(contents, ast::vstore_uniq) => { + return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange, + expr, contents); + } + ast::expr_lit(lit) => { + return trans_immediate_lit(bcx, expr, *lit); + } + ast::expr_binary(op, lhs, rhs) => { + // if overloaded, would be RvalueDpsExpr + assert !bcx.ccx().maps.method_map.contains_key(expr.id); + + return trans_binary(bcx, expr, op, lhs, rhs); + } + ast::expr_unary(op, x) => { + return trans_unary_datum(bcx, expr, op, x); + } + ast::expr_addr_of(_, x) => { + return trans_addr_of(bcx, expr, x); + } + ast::expr_cast(val, _) => { + return trans_imm_cast(bcx, val, expr.id); + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + fmt!("trans_rvalue_datum reached fall-through case: %?", + expr.node)); + } + } +} + +fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block { + let mut bcx = bcx; + let _icx = bcx.insn_ctxt("trans_rvalue_stmt"); + + trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); + + match expr.node { + ast::expr_break(label_opt) => { + if label_opt.is_some() { + bcx.tcx().sess.span_unimpl(expr.span, ~"labeled break"); + } + return controlflow::trans_break(bcx); + } + ast::expr_again(label_opt) => { + if label_opt.is_some() { + bcx.tcx().sess.span_unimpl(expr.span, ~"labeled again"); + } + return controlflow::trans_cont(bcx); + } + ast::expr_ret(ex) => { + return controlflow::trans_ret(bcx, ex); + } + ast::expr_fail(why) => { + return controlflow::trans_fail_expr(bcx, Some(expr.span), why); + } + ast::expr_log(_, lvl, a) => { + return controlflow::trans_log(expr, lvl, bcx, a); + } + ast::expr_assert(a) => { + return controlflow::trans_check_expr(bcx, expr, a, ~"Assertion"); + } + ast::expr_while(cond, body) => { + return controlflow::trans_while(bcx, cond, body); + } + ast::expr_loop(body, _) => { + return controlflow::trans_loop(bcx, body); + } + ast::expr_assign(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); + } + ast::expr_move(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.move_to_datum(bcx, DROP_EXISTING, dst_datum); + } + ast::expr_swap(dst, src) => { + let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst)); + let src_datum = unpack_datum!(bcx, trans_lvalue(bcx, src)); + let scratch = scratch_datum(bcx, dst_datum.ty, false); + + let bcx = dst_datum.move_to_datum(bcx, INIT, scratch); + let bcx = src_datum.move_to_datum(bcx, INIT, dst_datum); + return scratch.move_to_datum(bcx, INIT, src_datum); + } + ast::expr_assign_op(op, dst, src) => { + return trans_assign_op(bcx, expr, op, dst, src); + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + fmt!("trans_rvalue_stmt reached fall-through case: %?", + expr.node)); + } + }; +} + +fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block { + let mut bcx = bcx; + let _icx = bcx.insn_ctxt("trans_rvalue_dps"); + let tcx = bcx.tcx(); + + trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); + + match expr.node { + ast::expr_path(_) => { + return trans_def_dps(bcx, expr, bcx.def(expr.id), dest); + } + ast::expr_if(cond, thn, els) => { + return controlflow::trans_if(bcx, cond, thn, els, dest); + } + ast::expr_match(discr, arms) => { + return alt::trans_alt(bcx, expr, discr, arms, dest); + } + ast::expr_block(blk) => { + return do base::with_scope(bcx, blk.info(), + ~"block-expr body") |bcx| { + controlflow::trans_block(bcx, blk, dest) + }; + } + ast::expr_rec(fields, base) | ast::expr_struct(_, fields, base) => { + return trans_rec_or_struct(bcx, fields, base, expr.id, dest); + } + ast::expr_tup(args) => { + return trans_tup(bcx, args, dest); + } + ast::expr_lit(@{node: ast::lit_str(s), _}) => { + return tvec::trans_lit_str(bcx, expr, s, dest); + } + ast::expr_vstore(contents, ast::vstore_slice(_)) => { + return tvec::trans_slice_vstore(bcx, expr, contents, dest); + } + ast::expr_vstore(contents, ast::vstore_fixed(_)) => { + return tvec::trans_fixed_vstore(bcx, expr, contents, dest); + } + ast::expr_vec(*) | ast::expr_repeat(*) => { + return tvec::trans_fixed_vstore(bcx, expr, expr, dest); + } + ast::expr_fn(proto, decl, body, cap_clause) => { + // Don't use this function for anything real. Use the one in + // astconv instead. + fn ast_proto_to_proto_simple(ast_proto: ast::proto) + -> ty::fn_proto { + match ast_proto { + ast::proto_bare => ty::proto_bare, + ast::proto_uniq => ty::proto_vstore(ty::vstore_uniq), + ast::proto_box => ty::proto_vstore(ty::vstore_box), + ast::proto_block => { + ty::proto_vstore(ty::vstore_slice(ty::re_static)) + } + } + } + + return closure::trans_expr_fn(bcx, + ast_proto_to_proto_simple(proto), + decl, body, expr.id, cap_clause, + None, dest); + } + ast::expr_fn_block(decl, body, cap_clause) => { + let expr_ty = expr_ty(bcx, expr); + match ty::get(expr_ty).struct { + ty::ty_fn({proto, _}) => { + debug!("translating fn_block %s with type %s", + expr_to_str(expr, tcx.sess.intr()), + ty_to_str(tcx, expr_ty)); + return closure::trans_expr_fn(bcx, proto, decl, body, + expr.id, cap_clause, None, + dest); + } + _ => { + bcx.sess().impossible_case( + expr.span, "fn_block has body with a non-fn type"); + } + } + } + ast::expr_loop_body(blk) => { + match ty::get(expr_ty(bcx, expr)).struct { + ty::ty_fn({proto, _}) => { + match blk.node { + ast::expr_fn_block(decl, body, cap) => { + return closure::trans_expr_fn( + bcx, proto, decl, body, blk.id, + cap, Some(None), dest); + } + _ => { + bcx.sess().impossible_case( + expr.span, + "loop_body has the wrong kind of contents") + } + } + } + _ => { + bcx.sess().impossible_case( + expr.span, "loop_body has body with a non-fn type") + } + } + } + ast::expr_do_body(blk) => { + return trans_into(bcx, blk, dest); + } + ast::expr_copy(a) => { + return trans_into(bcx, a, dest); + } + ast::expr_unary_move(a) => { + if bcx.expr_is_lval(a) { + let datum = unpack_datum!(bcx, trans_to_datum(bcx, a)); + return match dest { + Ignore => datum.drop_val(bcx), + SaveIn(addr) => datum.move_to(bcx, INIT, addr) + }; + } else { + return trans_into(bcx, a, dest); + } + } + ast::expr_call(f, args, _) => { + return callee::trans_call( + bcx, expr, f, callee::ArgExprs(args), expr.id, dest); + } + ast::expr_binary(_, lhs, rhs) => { + // if not overloaded, would be RvalueDatumExpr + return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest); + } + ast::expr_unary(_, subexpr) => { + // if not overloaded, would be RvalueDatumExpr + return trans_overloaded_op(bcx, expr, subexpr, ~[], dest); + } + ast::expr_index(base, idx) => { + // if not overloaded, would be RvalueDatumExpr + return trans_overloaded_op(bcx, expr, base, ~[idx], dest); + } + ast::expr_cast(val, _) => { + return impl::trans_trait_cast(bcx, val, expr.id, dest); + } + ast::expr_assign_op(op, dst, src) => { + return trans_assign_op(bcx, expr, op, dst, src); + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + fmt!("trans_rvalue_dps reached fall-through case: %?", + expr.node)); + } + } +} + +fn trans_def_dps(bcx: block, ref_expr: @ast::expr, + def: ast::def, dest: Dest) -> block { + let _icx = bcx.insn_ctxt("trans_def_dps"); + let ccx = bcx.ccx(); + + let lldest = match dest { + SaveIn(lldest) => lldest, + Ignore => { return bcx; } + }; + + match def { + ast::def_fn(did, _) => { + let fn_data = callee::trans_fn_ref(bcx, did, ref_expr.id); + return fn_data_to_datum(bcx, did, fn_data, lldest); + } + ast::def_static_method(did, _) => { + let fn_data = impl::trans_static_method_callee(bcx, did, + ref_expr.id); + return fn_data_to_datum(bcx, did, fn_data, lldest); + } + ast::def_variant(tid, vid) => { + if ty::enum_variant_with_id(ccx.tcx, tid, vid).args.len() > 0u { + // N-ary variant. + let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id); + return fn_data_to_datum(bcx, vid, fn_data, lldest); + } else { + // Nullary variant. + let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]); + let lldiscrim_gv = base::lookup_discriminant(ccx, vid); + let lldiscrim = Load(bcx, lldiscrim_gv); + Store(bcx, lldiscrim, lldiscrimptr); + return bcx; + } + } + _ => { + bcx.tcx().sess.span_bug(ref_expr.span, fmt!( + "Non-DPS def %? referened by %s", + def, bcx.node_id_to_str(ref_expr.id))); + } + } +} + +fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock { + //! + // + // 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. + + let _icx = bcx.insn_ctxt("trans_lval"); + let mut bcx = bcx; + + debug!("trans_lvalue(expr=%s)", bcx.expr_to_str(expr)); + let _indenter = indenter(); + + trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); + + let unrooted_datum = unpack_datum!(bcx, unrooted(bcx, expr)); + + // If the lvalue must remain rooted, create a scratch datum, copy + // the lvalue in there, and then arrange for it to be cleaned up + // at the end of the scope with id `scope_id`: + let root_key = {id:expr.id, derefs:0u}; + for bcx.ccx().maps.root_map.find(root_key).each |scope_id| { + unrooted_datum.root(bcx, scope_id); + } + + return DatumBlock {bcx: bcx, datum: unrooted_datum}; + + fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock { + let mut bcx = bcx; + + match expr.node { + ast::expr_path(_) => { + return trans_def_lvalue(bcx, expr, bcx.def(expr.id)); + } + ast::expr_field(base, ident, _) => { + return trans_rec_field(bcx, base, ident); + } + ast::expr_index(base, idx) => { + return trans_index(bcx, expr, base, idx); + } + ast::expr_unary(ast::deref, base) => { + let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base)); + let derefdatum = basedatum.deref(bcx, base, 0); + return DatumBlock {bcx: bcx, datum: derefdatum}; + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + fmt!("trans_lvalue reached fall-through case: %?", + expr.node)); + } + } + } +} + +fn trans_def_lvalue(bcx: block, ref_expr: @ast::expr, + def: ast::def) -> DatumBlock { + let _icx = bcx.insn_ctxt("trans_def_lvalue"); + let ccx = bcx.ccx(); + match def { + ast::def_const(did) => { + let const_ty = expr_ty(bcx, ref_expr); + let val = if did.crate == ast::local_crate { + base::get_item_val(ccx, did.node) + } else { + base::trans_external_path(ccx, did, const_ty) + }; + DatumBlock { + bcx: bcx, + datum: Datum {val: val, + ty: const_ty, + mode: ByRef, + source: FromLvalue} + } + } + _ => { + DatumBlock { + bcx: bcx, + datum: trans_local_var(bcx, ref_expr.id, def) + } + } + } +} + +fn trans_local_var(bcx: block, ref_id: ast::node_id, def: ast::def) -> Datum { + let _icx = bcx.insn_ctxt("trans_local_var"); + + return match def { + ast::def_upvar(nid, _, _, _) => { + let local_ty = node_id_type(bcx, nid); + match bcx.fcx.llupvars.find(nid) { + Some(val) => { + Datum { + val: val, + ty: local_ty, + mode: ByRef, + source: FromLvalue + } + } + None => { + bcx.sess().bug(fmt!( + "trans_local_var: no llval for upvar %? found", nid)); + } + } + } + ast::def_arg(nid, _) => { + take_local(bcx, ref_id, bcx.fcx.llargs, nid) + } + ast::def_local(nid, _) | ast::def_binding(nid, _) => { + take_local(bcx, ref_id, bcx.fcx.lllocals, nid) + } + ast::def_self(nid) => { + let self_info: ValSelfData = match bcx.fcx.llself { + Some(ref self_info) => *self_info, + None => { + bcx.sess().bug(fmt!( + "trans_local_var: reference to self \ + out of context with id %?", nid)); + } + }; + + // This cast should not be necessary. We should cast self *once*, + // but right now this conflicts with default methods. + let llselfty = T_ptr(type_of::type_of(bcx.ccx(), self_info.t)); + let casted_val = PointerCast(bcx, self_info.v, llselfty); + Datum { + val: casted_val, + ty: self_info.t, + mode: ByRef, + source: FromLvalue + } + } + _ => { + bcx.sess().unimpl(fmt!( + "unsupported def type in trans_local_var: %?", def)); + } + }; + + fn take_local(bcx: block, + ref_id: ast::node_id, + table: hashmap, + nid: ast::node_id) -> Datum { + let is_last_use = match bcx.ccx().maps.last_use_map.find(ref_id) { + None => false, + Some(vars) => (*vars).contains(nid) + }; + + let source = if is_last_use {FromLastUseLvalue} else {FromLvalue}; + + let (v, mode) = match table.find(nid) { + Some(local_mem(v)) => (v, ByRef), + Some(local_imm(v)) => (v, ByValue), + None => { + bcx.sess().bug(fmt!( + "trans_local_var: no llval for local/arg %? found", nid)); + } + }; + let ty = node_id_type(bcx, nid); + + debug!("take_local(nid=%?, last_use=%b, v=%s, mode=%?, ty=%s)", + nid, is_last_use, bcx.val_str(v), mode, bcx.ty_to_str(ty)); + + Datum { val: v, ty: ty, mode: mode, source: source } + } +} + +fn fn_data_to_datum(bcx: block, + def_id: ast::def_id, + fn_data: callee::FnData, + lldest: ValueRef) -> block { + //! + // + // Translates a reference to a top-level fn item into a rust + // value. This is generally a Rust closure pair: (fn ptr, env) + // where the environment is NULL. However, extern functions for + // interfacing with C are represted as just the fn ptr with type + // *u8. + // + // Strictly speaking, references to extern fns ought to be + // RvalueDatumExprs, but it's not worth the complexity to avoid the + // extra stack slot that LLVM probably optimizes away anyhow. + + let fn_tpt = ty::lookup_item_type(bcx.tcx(), def_id); + if ty::ty_fn_purity(fn_tpt.ty) == ast::extern_fn { + let val = PointerCast(bcx, fn_data.llfn, T_ptr(T_i8())); + Store(bcx, val, lldest); + return bcx; + } + + let llfn = GEPi(bcx, lldest, [0u, abi::fn_field_code]); + Store(bcx, fn_data.llfn, llfn); + let llenv = GEPi(bcx, lldest, [0u, abi::fn_field_box]); + Store(bcx, base::null_env_ptr(bcx), llenv); + return bcx; +} + +fn with_field_tys(tcx: ty::ctxt, ty: ty::t, + op: fn(bool, (&[ty::field])) -> R) -> R { + match ty::get(ty).struct { + ty::ty_rec(ref fields) => { + op(false, *fields) + } + + ty::ty_class(did, ref substs) => { + let has_dtor = ty::ty_dtor(tcx, did).is_some(); + op(has_dtor, class_items_as_mutable_fields(tcx, did, substs)) + } + + _ => { + tcx.sess.bug(fmt!( + "cannot get field types from the type %s", + ty_to_str(tcx, ty))); + } + } +} + +fn trans_rec_field(bcx: block, + base: @ast::expr, + field: ast::ident) -> DatumBlock { + let mut bcx = bcx; + let _icx = bcx.insn_ctxt("trans_rec_field"); + + // Translate and autoderef the base expression. We should have a + // record or a struct when we're done, both of which are currently + // non-immediate and hence always tracked by reference. + let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base)); + let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value); + + do with_field_tys(bcx.tcx(), base_datum.ty) |_has_dtor, field_tys| { + let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys); + DatumBlock { + datum: base_datum.GEPi(bcx, [0u, 0u, ix], field_tys[ix].mt.ty), + bcx: bcx + } + } +} + +fn trans_index(bcx: block, + index_expr: @ast::expr, + base: @ast::expr, + idx: @ast::expr) -> DatumBlock { + let _icx = bcx.insn_ctxt("trans_index"); + let ccx = bcx.ccx(); + let base_ty = expr_ty(bcx, base); + let mut bcx = bcx; + + // Translate and autoderef the base expression. We should have some sort + // of vector (@[], &[], ~[], []/_, etc) when we're done. + let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base)); + let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value); + + // 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_appropriate_llval(bcx, idx); + let ix_size = shape::llsize_of_real(bcx.ccx(), val_ty(ix_val)); + let int_size = shape::llsize_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 scaled_ix = Mul(bcx, ix_val, vt.llunit_size); + base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix"); + + let mut (base, len) = base_datum.get_base_and_len(bcx); + + if ty::type_is_str(base_ty) { + // acccount for null terminator in the case of string + len = Sub(bcx, len, C_uint(bcx.ccx(), 1u)); + } + + debug!("trans_index: base %s", val_str(bcx.ccx().tn, base)); + debug!("trans_index: len %s", val_str(bcx.ccx().tn, len)); + + let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len); + let bcx = do with_cond(bcx, bounds_check) |bcx| { + controlflow::trans_fail(bcx, Some(index_expr.span), ~"bounds check") + }; + let elt = InBoundsGEP(bcx, base, ~[ix_val]); + let elt = PointerCast(bcx, elt, T_ptr(vt.llunit_ty)); + return DatumBlock { + bcx: bcx, + datum: Datum {val: elt, ty: vt.unit_ty, + mode: ByRef, source: FromLvalue} + }; +} + +fn trans_rec_or_struct(bcx: block, + fields: &[ast::field], + base: Option<@ast::expr>, + id: ast::node_id, + dest: Dest) -> block +{ + let _icx = bcx.insn_ctxt("trans_rec"); + let mut bcx = bcx; + + // Handle the case where the result is ignored. + let addr; + match dest { + SaveIn(p) => { + addr = p; + } + Ignore => { + // just evaluate the values for each field and drop them + // on the floor + for vec::each(fields) |fld| { + bcx = trans_into(bcx, fld.node.expr, Ignore); + } + return bcx; + } + } + + let ty = node_id_type(bcx, id); + let tcx = bcx.tcx(); + do with_field_tys(tcx, ty) |has_dtor, field_tys| { + // evaluate each of the fields and store them into their + // correct locations + let mut temp_cleanups = ~[]; + for fields.each |field| { + let ix = ty::field_idx_strict(tcx, field.node.ident, field_tys); + let dest = GEPi(bcx, addr, struct_field(ix)); + bcx = trans_into(bcx, field.node.expr, SaveIn(dest)); + add_clean_temp_mem(bcx, dest, field_tys[ix].mt.ty); + vec::push(temp_cleanups, dest); + } + + // copy over any remaining fields from the base (for + // functional record update) + for base.each |base_expr| { + let base_datum = unpack_datum!( + bcx, trans_to_datum(bcx, base_expr)); + + // Copy over inherited fields + for field_tys.eachi |i, field_ty| { + if !fields.any(|f| f.node.ident == field_ty.ident) { + let dest = GEPi(bcx, addr, struct_field(i)); + let base_field = + base_datum.GEPi(bcx, struct_field(i), field_ty.mt.ty); + bcx = base_field.store_to(bcx, INIT, dest); + } + } + } + + // Add the drop flag if necessary. + if has_dtor { + let dest = GEPi(bcx, addr, struct_dtor()); + Store(bcx, C_u8(1), dest); + } + + // Now revoke the cleanups as we pass responsibility for the data + // structure on to the caller + for temp_cleanups.each |cleanup| { revoke_clean(bcx, cleanup); } + bcx + } +} + +fn trans_tup(bcx: block, elts: ~[@ast::expr], dest: Dest) -> block { + let _icx = bcx.insn_ctxt("trans_tup"); + let mut bcx = bcx; + let addr = match dest { + Ignore => { + for vec::each(elts) |ex| { bcx = trans_into(bcx, ex, Ignore); } + return bcx; + } + SaveIn(pos) => pos, + }; + let mut temp_cleanups = ~[]; + for vec::eachi(elts) |i, e| { + let dest = GEPi(bcx, addr, [0u, i]); + let e_ty = expr_ty(bcx, e); + bcx = trans_into(bcx, e, SaveIn(dest)); + add_clean_temp_mem(bcx, dest, e_ty); + vec::push(temp_cleanups, dest); + } + for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, cleanup); } + return bcx; +} + +fn trans_immediate_lit(bcx: block, expr: @ast::expr, + lit: ast::lit) -> DatumBlock { + // must not be a string constant, that is a RvalueDpsExpr + let _icx = bcx.insn_ctxt("trans_immediate_lit"); + let ty = expr_ty(bcx, expr); + immediate_rvalue_bcx(bcx, consts::const_lit(bcx.ccx(), expr, lit), ty) +} + +fn trans_unary_datum(bcx: block, + un_expr: @ast::expr, + op: ast::unop, + sub_expr: @ast::expr) -> DatumBlock { + + let _icx = bcx.insn_ctxt("trans_unary_datum"); + + // if deref, would be LvalueExpr + assert op != ast::deref; + + // if overloaded, would be RvalueDpsExpr + assert !bcx.ccx().maps.method_map.contains_key(un_expr.id); + + let un_ty = expr_ty(bcx, un_expr); + let sub_ty = expr_ty(bcx, sub_expr); + + return match op { + ast::not => { + let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr); + immediate_rvalue_bcx(bcx, Not(bcx, val), un_ty) + } + ast::neg => { + let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr); + let llneg = { + if ty::type_is_fp(un_ty) { + FNeg(bcx, val) + } else { + Neg(bcx, val) + } + }; + immediate_rvalue_bcx(bcx, llneg, un_ty) + } + ast::box(_) => { + trans_boxed_expr(bcx, un_ty, sub_expr, sub_ty, heap_shared) + } + ast::uniq(_) => { + trans_boxed_expr(bcx, un_ty, sub_expr, sub_ty, heap_exchange) + } + ast::deref => { + bcx.sess().bug(~"deref expressions should have been \ + translated using trans_lvalue(), not \ + trans_unary_datum()") + } + }; + + fn trans_boxed_expr(bcx: block, + box_ty: ty::t, + contents: @ast::expr, + contents_ty: ty::t, + heap: heap) -> DatumBlock { + let _icx = bcx.insn_ctxt("trans_boxed_expr"); + let {bcx, box, body} = + base::malloc_general(bcx, contents_ty, heap); + add_clean_free(bcx, box, heap); + let bcx = trans_into(bcx, contents, SaveIn(body)); + revoke_clean(bcx, box); + return immediate_rvalue_bcx(bcx, box, box_ty); + } +} + +fn trans_addr_of(bcx: block, expr: @ast::expr, + subexpr: @ast::expr) -> DatumBlock { + let _icx = bcx.insn_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)); +} + +// Important to get types for both lhs and rhs, because one might be _|_ +// and the other not. +fn trans_eager_binop(bcx: block, + binop_expr: @ast::expr, + binop_ty: ty::t, + op: ast::binop, + lhs_datum: &Datum, + rhs_datum: &Datum) -> DatumBlock +{ + let mut bcx = bcx; + let _icx = bcx.insn_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 intype = { + if ty::type_is_bot(lhs_t) { rhs_t } + else { lhs_t } + }; + let is_float = ty::type_is_fp(intype); + + let rhs = base::cast_shift_expr_rhs(bcx, op, lhs, rhs); + + let mut bcx = bcx; + let val = match op { + ast::add => { + if is_float { FAdd(bcx, lhs, rhs) } + else { Add(bcx, lhs, rhs) } + } + ast::subtract => { + if is_float { FSub(bcx, lhs, rhs) } + else { Sub(bcx, lhs, rhs) } + } + ast::mul => { + if is_float { FMul(bcx, lhs, rhs) } + else { Mul(bcx, lhs, rhs) } + } + ast::div => { + if is_float { + FDiv(bcx, lhs, rhs) + } else { + // Only zero-check integers; fp /0 is NaN + bcx = base::fail_if_zero(bcx, binop_expr.span, + op, rhs, rhs_t); + if ty::type_is_signed(intype) { + SDiv(bcx, lhs, rhs) + } else { + UDiv(bcx, lhs, rhs) + } + } + } + ast::rem => { + if is_float { + FRem(bcx, lhs, rhs) + } else { + // Only zero-check integers; fp %0 is NaN + bcx = base::fail_if_zero(bcx, binop_expr.span, + op, rhs, rhs_t); + if ty::type_is_signed(intype) { + SRem(bcx, lhs, rhs) + } else { + URem(bcx, lhs, rhs) + } + } + } + ast::bitor => Or(bcx, lhs, rhs), + ast::bitand => And(bcx, lhs, rhs), + ast::bitxor => Xor(bcx, lhs, rhs), + ast::shl => Shl(bcx, lhs, rhs), + ast::shr => { + if ty::type_is_signed(intype) { + AShr(bcx, lhs, rhs) + } else { LShr(bcx, lhs, rhs) } + } + _ => { + let cmpr = base::trans_compare(bcx, op, + lhs, lhs_t, + rhs, rhs_t); + bcx = cmpr.bcx; + cmpr.val + } + }; + + return immediate_rvalue_bcx(bcx, val, binop_ty); +} + +// refinement types would obviate the need for this +enum lazy_binop_ty { lazy_and, lazy_or } + +fn trans_lazy_binop(bcx: block, + binop_expr: @ast::expr, + op: lazy_binop_ty, + a: @ast::expr, + b: @ast::expr) -> DatumBlock +{ + let _icx = bcx.insn_ctxt("trans_lazy_binop"); + let binop_ty = expr_ty(bcx, binop_expr); + let mut bcx = bcx; + + let Result {bcx: past_lhs, val: lhs} = { + do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| { + trans_to_appropriate_llval(bcx, a) + } + }; + + if past_lhs.unreachable { + return immediate_rvalue_bcx(past_lhs, lhs, binop_ty); + } + + let join = base::sub_block(bcx, ~"join"); + let before_rhs = base::sub_block(bcx, ~"rhs"); + + match op { + lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb), + lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb) + } + let Result {bcx: past_rhs, val: rhs} = { + do base::with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| { + trans_to_appropriate_llval(bcx, b) + } + }; + + if past_rhs.unreachable { + return immediate_rvalue_bcx(join, lhs, binop_ty); + } + + Br(past_rhs, join.llbb); + let phi = Phi(join, T_bool(), ~[lhs, rhs], ~[past_lhs.llbb, + past_rhs.llbb]); + + return immediate_rvalue_bcx(join, phi, binop_ty); +} + +fn trans_binary(bcx: block, + binop_expr: @ast::expr, + op: ast::binop, + lhs: @ast::expr, + rhs: @ast::expr) -> DatumBlock +{ + let _icx = bcx.insn_ctxt("trans_binary"); + + match op { + ast::and => { + trans_lazy_binop(bcx, binop_expr, lazy_and, lhs, rhs) + } + ast::or => { + trans_lazy_binop(bcx, binop_expr, lazy_or, lhs, rhs) + } + _ => { + 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 binop_ty = expr_ty(bcx, binop_expr); + trans_eager_binop(bcx, binop_expr, binop_ty, op, + &lhs_datum, &rhs_datum) + } + } +} + +fn trans_overloaded_op(bcx: block, + expr: @ast::expr, + rcvr: @ast::expr, + +args: ~[@ast::expr], + dest: Dest) -> block +{ + let origin = bcx.ccx().maps.method_map.get(expr.id); + let fty = node_id_type(bcx, expr.callee_id); + return callee::trans_call_inner( + bcx, expr.info(), fty, + expr_ty(bcx, expr), + |bcx| impl::trans_method_callee(bcx, expr.callee_id, rcvr, origin), + callee::ArgExprs(args), dest); +} + +fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, + llsrc: ValueRef, signed: bool) -> ValueRef { + let _icx = bcx.insn_ctxt("int_cast"); + let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype); + let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype); + return if dstsz == srcsz { + BitCast(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + TruncOrBitCast(bcx, llsrc, lldsttype) + } else if signed { + SExtOrBitCast(bcx, llsrc, lldsttype) + } else { ZExtOrBitCast(bcx, llsrc, lldsttype) }; +} + +fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, + llsrc: ValueRef) -> ValueRef { + let _icx = bcx.insn_ctxt("float_cast"); + let srcsz = lib::llvm::float_width(llsrctype); + let dstsz = lib::llvm::float_width(lldsttype); + return if dstsz > srcsz { + FPExt(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + FPTrunc(bcx, llsrc, lldsttype) + } else { llsrc }; +} + +enum cast_kind { + cast_pointer, + cast_integral, + cast_float, + cast_enum, + cast_other, +} + +impl cast_kind : cmp::Eq { + pure fn eq(&&other: cast_kind) -> bool { + match (self, other) { + (cast_pointer, cast_pointer) => true, + (cast_integral, cast_integral) => true, + (cast_float, cast_float) => true, + (cast_enum, cast_enum) => true, + (cast_other, cast_other) => true, + (cast_pointer, _) => false, + (cast_integral, _) => false, + (cast_float, _) => false, + (cast_enum, _) => false, + (cast_other, _) => false, + } + } +} + +fn cast_type_kind(t: ty::t) -> cast_kind { + match ty::get(t).struct { + ty::ty_float(*) => cast_float, + ty::ty_ptr(*) => cast_pointer, + ty::ty_rptr(*) => cast_pointer, + ty::ty_int(*) => cast_integral, + ty::ty_uint(*) => cast_integral, + ty::ty_bool => cast_integral, + ty::ty_enum(*) => cast_enum, + _ => cast_other + } +} + +fn trans_imm_cast(bcx: block, expr: @ast::expr, + id: ast::node_id) -> DatumBlock { + let _icx = bcx.insn_ctxt("trans_cast"); + let ccx = bcx.ccx(); + + let t_out = node_id_type(bcx, id); + + let mut bcx = bcx; + let llexpr = unpack_result!(bcx, trans_to_appropriate_llval(bcx, expr)); + 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 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 newval = + match {in: k_in, out: k_out} { + {in: cast_integral, out: cast_integral} => { + int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) + } + {in: cast_float, out: cast_float} => { + float_cast(bcx, ll_t_out, ll_t_in, llexpr) + } + {in: cast_integral, out: cast_float} => { + if s_in { + SIToFP(bcx, llexpr, ll_t_out) + } else { UIToFP(bcx, llexpr, ll_t_out) } + } + {in: cast_float, out: cast_integral} => { + if ty::type_is_signed(t_out) { + FPToSI(bcx, llexpr, ll_t_out) + } else { FPToUI(bcx, llexpr, ll_t_out) } + } + {in: cast_integral, out: cast_pointer} => { + IntToPtr(bcx, llexpr, ll_t_out) + } + {in: cast_pointer, out: cast_integral} => { + PtrToInt(bcx, llexpr, ll_t_out) + } + {in: cast_pointer, out: cast_pointer} => { + PointerCast(bcx, llexpr, ll_t_out) + } + {in: cast_enum, out: cast_integral} | + {in: cast_enum, out: cast_float} => { + let bcx = bcx; + let llenumty = T_opaque_enum_ptr(ccx); + let av_enum = PointerCast(bcx, llexpr, llenumty); + let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]); + let lldiscrim_a = Load(bcx, lldiscrim_a_ptr); + 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(~"translating unsupported cast.") + } + } + _ => ccx.sess.bug(~"translating unsupported cast.") + }; + return immediate_rvalue_bcx(bcx, newval, t_out); +} + +fn trans_assign_op(bcx: block, + expr: @ast::expr, + op: ast::binop, + dst: @ast::expr, + src: @ast::expr) -> block +{ + let _icx = bcx.insn_ctxt("trans_assign_op"); + let mut bcx = bcx; + + debug!("trans_assign_op(expr=%s)", bcx.expr_to_str(expr)); + + // Evaluate LHS (destination), which should be an lvalue + let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst)); + + // A user-defined operator method + if bcx.ccx().maps.method_map.find(expr.id).is_some() { + // FIXME(#2582) evaluates the receiver twice!! + let scratch = scratch_datum(bcx, dst_datum.ty, false); + let bcx = trans_overloaded_op(bcx, expr, dst, ~[src], + SaveIn(scratch.val)); + return scratch.move_to_datum(bcx, DROP_EXISTING, dst_datum); + } + + // Evaluate RHS (source) + let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src)); + + // 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.store_to_datum(bcx, DROP_EXISTING, dst_datum); +} + +fn shorten(+x: ~str) -> ~str { + if x.len() > 60 { x.substr(0, 60) } else { x } +} \ No newline at end of file diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs index 504aea892141c..cd2be09517867 100644 --- a/src/rustc/middle/trans/foreign.rs +++ b/src/rustc/middle/trans/foreign.rs @@ -17,6 +17,9 @@ use base::*; use type_of::*; use std::map::hashmap; use util::ppaux::ty_to_str; +use datum::*; +use callee::*; +use expr::{Dest, Ignore}; export link_name, trans_foreign_mod, register_foreign_fn, trans_foreign_fn, trans_intrinsic; @@ -87,8 +90,8 @@ fn classify_ty(ty: TypeRef) -> ~[x86_64_reg_class] { Double => 8, Struct => { do vec::foldl(0, struct_tys(ty)) |a, t| { - uint::max(a, ty_align(t)) - } + uint::max(a, ty_align(t)) + } } Array => { let elt = llvm::LLVMGetElementType(ty); @@ -785,210 +788,209 @@ fn trans_foreign_mod(ccx: @crate_ctxt, fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item, path: ast_map::path, substs: param_substs, - ref_id: Option) { + ref_id: Option) +{ + debug!("trans_intrinsic(item.ident=%s)", ccx.sess.str_of(item.ident)); + let fcx = new_fn_ctxt_w_id(ccx, path, decl, item.id, Some(substs), Some(item.span)); let mut bcx = top_scope_block(fcx, None), lltop = bcx.llbb; match ccx.sess.str_of(item.ident) { - ~"atomic_xchg" => { - let old = AtomicRMW(bcx, Xchg, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - SequentiallyConsistent); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xchg_acq" => { - let old = AtomicRMW(bcx, Xchg, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - Acquire); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xchg_rel" => { - let old = AtomicRMW(bcx, Xchg, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - Release); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xadd" => { - let old = AtomicRMW(bcx, lib::llvm::Add, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - SequentiallyConsistent); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xadd_acq" => { - let old = AtomicRMW(bcx, lib::llvm::Add, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - Acquire); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xadd_rel" => { - let old = AtomicRMW(bcx, lib::llvm::Add, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - Release); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xsub" => { - let old = AtomicRMW(bcx, lib::llvm::Sub, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - SequentiallyConsistent); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xsub_acq" => { - let old = AtomicRMW(bcx, lib::llvm::Sub, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - Acquire); - Store(bcx, old, fcx.llretptr); - } - ~"atomic_xsub_rel" => { - let old = AtomicRMW(bcx, lib::llvm::Sub, - get_param(decl, first_real_arg), - get_param(decl, first_real_arg + 1u), - Release); - Store(bcx, old, fcx.llretptr); - } - ~"size_of" => { - let tp_ty = substs.tys[0]; - let lltp_ty = type_of::type_of(ccx, tp_ty); - Store(bcx, C_uint(ccx, shape::llsize_of_real(ccx, lltp_ty)), - fcx.llretptr); - } - ~"move_val" => { - let tp_ty = substs.tys[0]; - let src = {bcx: bcx, - val: get_param(decl, first_real_arg + 1u), - kind: lv_owned}; - bcx = move_val(bcx, DROP_EXISTING, - get_param(decl, first_real_arg), - src, - tp_ty); - } - ~"move_val_init" => { - let tp_ty = substs.tys[0]; - let src = {bcx: bcx, - val: get_param(decl, first_real_arg + 1u), - kind: lv_owned}; - bcx = move_val(bcx, INIT, - get_param(decl, first_real_arg), - src, - tp_ty); - } - ~"min_align_of" => { - let tp_ty = substs.tys[0]; - let lltp_ty = type_of::type_of(ccx, tp_ty); - Store(bcx, C_uint(ccx, shape::llalign_of_min(ccx, lltp_ty)), - fcx.llretptr); - } - ~"pref_align_of"=> { - let tp_ty = substs.tys[0]; - let lltp_ty = type_of::type_of(ccx, tp_ty); - Store(bcx, C_uint(ccx, shape::llalign_of_pref(ccx, lltp_ty)), - fcx.llretptr); - } - ~"get_tydesc" => { - let tp_ty = substs.tys[0]; - let static_ti = get_tydesc(ccx, tp_ty); - lazily_emit_all_tydesc_glue(ccx, static_ti); - // FIXME (#2712): change this to T_ptr(ccx.tydesc_ty) when the - // core::sys copy of the get_tydesc interface dies off. - let td = PointerCast(bcx, static_ti.tydesc, T_ptr(T_nil())); - Store(bcx, td, fcx.llretptr); - } - ~"init" => { - let tp_ty = substs.tys[0]; - let lltp_ty = type_of::type_of(ccx, tp_ty); - if !ty::type_is_nil(tp_ty) { - Store(bcx, C_null(lltp_ty), fcx.llretptr); + ~"atomic_xchg" => { + let old = AtomicRMW(bcx, Xchg, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + SequentiallyConsistent); + Store(bcx, old, fcx.llretptr); } - } - ~"forget" => {} - ~"reinterpret_cast" => { - let tp_ty = substs.tys[0]; - let lltp_ty = type_of::type_of(ccx, tp_ty); - let llout_ty = type_of::type_of(ccx, substs.tys[1]); - let tp_sz = shape::llsize_of_real(ccx, lltp_ty), - out_sz = shape::llsize_of_real(ccx, llout_ty); - if tp_sz != out_sz { - let sp = match ccx.tcx.items.get(option::get(ref_id)) { - ast_map::node_expr(e) => e.span, - _ => fail ~"reinterpret_cast or forget has non-expr arg" - }; - ccx.sess.span_fatal( - sp, fmt!("reinterpret_cast called on types \ - with different size: %s (%u) to %s (%u)", - ty_to_str(ccx.tcx, tp_ty), tp_sz, - ty_to_str(ccx.tcx, substs.tys[1]), out_sz)); + ~"atomic_xchg_acq" => { + let old = AtomicRMW(bcx, Xchg, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + Acquire); + Store(bcx, old, fcx.llretptr); } - if !ty::type_is_nil(substs.tys[1]) { - // NB: Do not use a Load and Store here. This causes massive code - // bloat when reinterpret_cast is used on large structural types. - let llretptr = PointerCast(bcx, fcx.llretptr, T_ptr(T_i8())); - let llcast = get_param(decl, first_real_arg); - let llcast = PointerCast(bcx, llcast, T_ptr(T_i8())); - call_memmove(bcx, llretptr, llcast, llsize_of(ccx, lltp_ty)); + ~"atomic_xchg_rel" => { + let old = AtomicRMW(bcx, Xchg, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + Release); + Store(bcx, old, fcx.llretptr); } + ~"atomic_xadd" => { + let old = AtomicRMW(bcx, lib::llvm::Add, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + SequentiallyConsistent); + Store(bcx, old, fcx.llretptr); + } + ~"atomic_xadd_acq" => { + let old = AtomicRMW(bcx, lib::llvm::Add, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + Acquire); + Store(bcx, old, fcx.llretptr); + } + ~"atomic_xadd_rel" => { + let old = AtomicRMW(bcx, lib::llvm::Add, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + Release); + Store(bcx, old, fcx.llretptr); + } + ~"atomic_xsub" => { + let old = AtomicRMW(bcx, lib::llvm::Sub, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + SequentiallyConsistent); + Store(bcx, old, fcx.llretptr); + } + ~"atomic_xsub_acq" => { + let old = AtomicRMW(bcx, lib::llvm::Sub, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + Acquire); + Store(bcx, old, fcx.llretptr); + } + ~"atomic_xsub_rel" => { + let old = AtomicRMW(bcx, lib::llvm::Sub, + get_param(decl, first_real_arg), + get_param(decl, first_real_arg + 1u), + Release); + Store(bcx, old, fcx.llretptr); + } + ~"size_of" => { + let tp_ty = substs.tys[0]; + let lltp_ty = type_of::type_of(ccx, tp_ty); + Store(bcx, C_uint(ccx, shape::llsize_of_real(ccx, lltp_ty)), + fcx.llretptr); + } + ~"move_val" => { + let tp_ty = substs.tys[0]; + let src = Datum {val: get_param(decl, first_real_arg + 1u), + ty: tp_ty, mode: ByRef, source: FromLvalue}; + bcx = src.move_to(bcx, DROP_EXISTING, + get_param(decl, first_real_arg)); + } + ~"move_val_init" => { + let tp_ty = substs.tys[0]; + let src = Datum {val: get_param(decl, first_real_arg + 1u), + ty: tp_ty, mode: ByRef, source: FromLvalue}; + bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg)); + } + ~"min_align_of" => { + let tp_ty = substs.tys[0]; + let lltp_ty = type_of::type_of(ccx, tp_ty); + Store(bcx, C_uint(ccx, shape::llalign_of_min(ccx, lltp_ty)), + fcx.llretptr); + } + ~"pref_align_of"=> { + let tp_ty = substs.tys[0]; + let lltp_ty = type_of::type_of(ccx, tp_ty); + Store(bcx, C_uint(ccx, shape::llalign_of_pref(ccx, lltp_ty)), + fcx.llretptr); + } + ~"get_tydesc" => { + let tp_ty = substs.tys[0]; + let static_ti = get_tydesc(ccx, tp_ty); + glue::lazily_emit_all_tydesc_glue(ccx, static_ti); + + // FIXME (#2712): change this to T_ptr(ccx.tydesc_ty) when the + // core::sys copy of the get_tydesc interface dies off. + let td = PointerCast(bcx, static_ti.tydesc, T_ptr(T_nil())); + Store(bcx, td, fcx.llretptr); + } + ~"init" => { + let tp_ty = substs.tys[0]; + let lltp_ty = type_of::type_of(ccx, tp_ty); + if !ty::type_is_nil(tp_ty) { + Store(bcx, C_null(lltp_ty), fcx.llretptr); + } + } + ~"forget" => {} + ~"reinterpret_cast" => { + let tp_ty = substs.tys[0]; + let lltp_ty = type_of::type_of(ccx, tp_ty); + let llout_ty = type_of::type_of(ccx, substs.tys[1]); + let tp_sz = shape::llsize_of_real(ccx, lltp_ty), + out_sz = shape::llsize_of_real(ccx, llout_ty); + if tp_sz != out_sz { + let sp = match ccx.tcx.items.get(option::get(ref_id)) { + ast_map::node_expr(e) => e.span, + _ => fail ~"reinterpret_cast or forget has non-expr arg" + }; + ccx.sess.span_fatal( + sp, fmt!("reinterpret_cast called on types \ + with different size: %s (%u) to %s (%u)", + ty_to_str(ccx.tcx, tp_ty), tp_sz, + ty_to_str(ccx.tcx, substs.tys[1]), out_sz)); + } + if !ty::type_is_nil(substs.tys[1]) { + // NB: Do not use a Load and Store here. This causes + // massive code bloat when reinterpret_cast is used on + // large structural types. + let llretptr = PointerCast(bcx, fcx.llretptr, T_ptr(T_i8())); + let llcast = get_param(decl, first_real_arg); + let llcast = PointerCast(bcx, llcast, T_ptr(T_i8())); + call_memmove(bcx, llretptr, llcast, llsize_of(ccx, lltp_ty)); + } } - ~"addr_of" => { - Store(bcx, get_param(decl, first_real_arg), fcx.llretptr); - } - ~"needs_drop" => { - let tp_ty = substs.tys[0]; - Store(bcx, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)), - fcx.llretptr); - } - ~"visit_tydesc" => { - let td = get_param(decl, first_real_arg); - let visitor = get_param(decl, first_real_arg + 1u); - let td = PointerCast(bcx, td, T_ptr(ccx.tydesc_type)); - call_tydesc_glue_full(bcx, visitor, td, - abi::tydesc_field_visit_glue, None); - } - ~"frame_address" => { - let frameaddress = ccx.intrinsics.get(~"llvm.frameaddress"); - let frameaddress_val = Call(bcx, frameaddress, ~[C_i32(0i32)]); - let fty = ty::mk_fn(bcx.tcx(), { - purity: ast::impure_fn, - proto: - ty::proto_vstore(ty::vstore_slice( - ty::re_bound(ty::br_anon(0)))), - bounds: @~[], - inputs: ~[{ - mode: ast::expl(ast::by_val), - ty: ty::mk_imm_ptr( - bcx.tcx(), - ty::mk_mach_uint(bcx.tcx(), ast::ty_u8)) - }], - output: ty::mk_nil(bcx.tcx()), - ret_style: ast::return_val - }); - bcx = trans_call_inner(bcx, None, fty, ty::mk_nil(bcx.tcx()), - |bcx| lval_no_env( - bcx, - get_param(decl, first_real_arg), - lv_temporary), - arg_vals(~[frameaddress_val]), ignore); - } - ~"morestack_addr" => { - // XXX This is a hack to grab the address of this particular - // native function. There should be a general in-language - // way to do this - let llfty = type_of_fn(bcx.ccx(), ~[], ty::mk_nil(bcx.tcx())); - let morestack_addr = decl_cdecl_fn( - bcx.ccx().llmod, ~"__morestack", llfty); - let morestack_addr = PointerCast(bcx, morestack_addr, T_ptr(T_nil())); - Store(bcx, morestack_addr, fcx.llretptr); - } - _ => { - // Could we make this an enum rather than a string? does it get - // checked earlier? - ccx.sess.span_bug(item.span, ~"unknown intrinsic"); - } + ~"addr_of" => { + Store(bcx, get_param(decl, first_real_arg), fcx.llretptr); + } + ~"needs_drop" => { + let tp_ty = substs.tys[0]; + Store(bcx, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)), + fcx.llretptr); + } + ~"visit_tydesc" => { + let td = get_param(decl, first_real_arg); + let visitor = get_param(decl, first_real_arg + 1u); + let td = PointerCast(bcx, td, T_ptr(ccx.tydesc_type)); + glue::call_tydesc_glue_full(bcx, visitor, td, + abi::tydesc_field_visit_glue, None); + } + ~"frame_address" => { + let frameaddress = ccx.intrinsics.get(~"llvm.frameaddress"); + let frameaddress_val = Call(bcx, frameaddress, ~[C_i32(0i32)]); + let fty = ty::mk_fn(bcx.tcx(), { + purity: ast::impure_fn, + proto: + ty::proto_vstore(ty::vstore_slice( + ty::re_bound(ty::br_anon(0)))), + bounds: @~[], + inputs: ~[{ + mode: ast::expl(ast::by_val), + ty: ty::mk_imm_ptr( + bcx.tcx(), + ty::mk_mach_uint(bcx.tcx(), ast::ty_u8)) + }], + output: ty::mk_nil(bcx.tcx()), + ret_style: ast::return_val + }); + let datum = Datum {val: get_param(decl, first_real_arg), + mode: ByRef, ty: fty, source: FromLvalue}; + bcx = trans_call_inner( + bcx, None, fty, ty::mk_nil(bcx.tcx()), + |bcx| Callee {bcx: bcx, data: Closure(datum)}, + ArgVals(~[frameaddress_val]), Ignore); + } + ~"morestack_addr" => { + // XXX This is a hack to grab the address of this particular + // native function. There should be a general in-language + // way to do this + let llfty = type_of_fn(bcx.ccx(), ~[], ty::mk_nil(bcx.tcx())); + let morestack_addr = decl_cdecl_fn( + bcx.ccx().llmod, ~"__morestack", llfty); + let morestack_addr = PointerCast(bcx, morestack_addr, + T_ptr(T_nil())); + Store(bcx, morestack_addr, fcx.llretptr); + } + _ => { + // Could we make this an enum rather than a string? does it get + // checked earlier? + ccx.sess.span_bug(item.span, ~"unknown intrinsic"); + } } build_return(bcx); finish_fn(fcx, lltop); diff --git a/src/rustc/middle/trans/glue.rs b/src/rustc/middle/trans/glue.rs new file mode 100644 index 0000000000000..3aee04755f086 --- /dev/null +++ b/src/rustc/middle/trans/glue.rs @@ -0,0 +1,681 @@ +//! +// +// Code relating to taking, dropping, etc as well as type descriptors. + +use lib::llvm::{ValueRef, TypeRef}; +use base::*; +use common::*; +use build::*; +use type_of::type_of; + +fn trans_free(cx: block, v: ValueRef) -> block { + let _icx = cx.insn_ctxt("trans_free"); + callee::trans_rtcall(cx, ~"free", ~[PointerCast(cx, v, T_ptr(T_i8()))], + expr::Ignore) +} + +fn trans_unique_free(cx: block, v: ValueRef) -> block { + let _icx = cx.insn_ctxt("trans_unique_free"); + callee::trans_rtcall( + cx, ~"exchange_free", ~[PointerCast(cx, v, T_ptr(T_i8()))], + expr::Ignore) +} + +fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block { + // NB: v is an *alias* of type t here, not a direct value. + let _icx = cx.insn_ctxt("take_ty"); + if ty::type_needs_drop(cx.tcx(), t) { + return call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue); + } + return cx; +} + +fn drop_ty(cx: block, v: ValueRef, t: ty::t) -> block { + // NB: v is an *alias* of type t here, not a direct value. + let _icx = cx.insn_ctxt("drop_ty"); + if ty::type_needs_drop(cx.tcx(), t) { + return call_tydesc_glue(cx, v, t, abi::tydesc_field_drop_glue); + } + return cx; +} + +fn drop_ty_root(bcx: block, v: ValueRef, rooted: bool, t: ty::t) -> block { + if rooted { + // NB: v is a raw ptr to an addrspace'd ptr to the value. + let v = PointerCast(bcx, Load(bcx, v), T_ptr(type_of(bcx.ccx(), t))); + drop_ty(bcx, v, t) + } else { + drop_ty(bcx, v, t) + } +} + +fn drop_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { + let _icx = bcx.insn_ctxt("drop_ty_immediate"); + match ty::get(t).struct { + ty::ty_uniq(_) | + ty::ty_evec(_, ty::vstore_uniq) | + ty::ty_estr(ty::vstore_uniq) => { + free_ty_immediate(bcx, v, t) + } + ty::ty_box(_) | ty::ty_opaque_box | + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) => { + decr_refcnt_maybe_free(bcx, v, t) + } + _ => bcx.tcx().sess.bug(~"drop_ty_immediate: non-box ty") + } +} + +fn take_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> Result { + let _icx = bcx.insn_ctxt("take_ty_immediate"); + match ty::get(t).struct { + ty::ty_box(_) | ty::ty_opaque_box | + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) => { + incr_refcnt_of_boxed(bcx, v); + rslt(bcx, v) + } + ty::ty_uniq(_) => { + uniq::duplicate(bcx, v, t) + } + ty::ty_evec(_, ty::vstore_uniq) | + ty::ty_estr(ty::vstore_uniq) => { + tvec::duplicate_uniq(bcx, v, t) + } + _ => rslt(bcx, v) + } +} + +fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block { + // NB: v is an *alias* of type t here, not a direct value. + let _icx = cx.insn_ctxt("free_ty"); + if ty::type_needs_drop(cx.tcx(), t) { + return call_tydesc_glue(cx, v, t, abi::tydesc_field_free_glue); + } + return cx; +} + +fn free_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { + let _icx = bcx.insn_ctxt("free_ty_immediate"); + match ty::get(t).struct { + ty::ty_uniq(_) | + ty::ty_evec(_, ty::vstore_uniq) | + ty::ty_estr(ty::vstore_uniq) | + ty::ty_box(_) | ty::ty_opaque_box | + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) | + ty::ty_opaque_closure_ptr(_) => { + let vp = alloca_zeroed(bcx, type_of(bcx.ccx(), t)); + Store(bcx, v, vp); + free_ty(bcx, vp, t) + } + _ => bcx.tcx().sess.bug(~"free_ty_immediate: non-box ty") + } +} + +fn lazily_emit_all_tydesc_glue(ccx: @crate_ctxt, + static_ti: @tydesc_info) { + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, static_ti); + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_drop_glue, static_ti); + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_free_glue, static_ti); + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_visit_glue, static_ti); +} + +fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint, + ti: @tydesc_info) { + let _icx = ccx.insn_ctxt("lazily_emit_tydesc_glue"); + let llfnty = type_of_glue_fn(ccx, ti.ty); + if field == abi::tydesc_field_take_glue { + match ti.take_glue { + Some(_) => (), + None => { + debug!("+++ lazily_emit_tydesc_glue TAKE %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"take"); + ti.take_glue = Some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, make_take_glue, ~"take"); + debug!("--- lazily_emit_tydesc_glue TAKE %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + } + } + } else if field == abi::tydesc_field_drop_glue { + match ti.drop_glue { + Some(_) => (), + None => { + debug!("+++ lazily_emit_tydesc_glue DROP %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"drop"); + ti.drop_glue = Some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, make_drop_glue, ~"drop"); + debug!("--- lazily_emit_tydesc_glue DROP %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + } + } + } else if field == abi::tydesc_field_free_glue { + match ti.free_glue { + Some(_) => (), + None => { + debug!("+++ lazily_emit_tydesc_glue FREE %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"free"); + ti.free_glue = Some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, make_free_glue, ~"free"); + debug!("--- lazily_emit_tydesc_glue FREE %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + } + } + } else if field == abi::tydesc_field_visit_glue { + match ti.visit_glue { + Some(_) => (), + None => { + debug!("+++ lazily_emit_tydesc_glue VISIT %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"visit"); + ti.visit_glue = Some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, ~"visit"); + debug!("--- lazily_emit_tydesc_glue VISIT %s", + ppaux::ty_to_str(ccx.tcx, ti.ty)); + } + } + } +} + +// See [Note-arg-mode] +fn call_tydesc_glue_full(++bcx: block, v: ValueRef, tydesc: ValueRef, + field: uint, static_ti: Option<@tydesc_info>) { + let _icx = bcx.insn_ctxt("call_tydesc_glue_full"); + let ccx = bcx.ccx(); + // 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 = + ccx.sess.opts.debugging_opts & session::no_landing_pads != 0; + if bcx.unreachable && !no_lpads { return; } + + let static_glue_fn = match static_ti { + None => None, + Some(sti) => { + lazily_emit_tydesc_glue(ccx, field, sti); + if field == abi::tydesc_field_take_glue { + sti.take_glue + } else if field == abi::tydesc_field_drop_glue { + sti.drop_glue + } else if field == abi::tydesc_field_free_glue { + sti.free_glue + } else if field == abi::tydesc_field_visit_glue { + sti.visit_glue + } else { + None + } + } + }; + + // When available, use static type info to give glue the right type. + let static_glue_fn = match static_ti { + None => None, + Some(sti) => { + match static_glue_fn { + None => None, + Some(sgf) => Some( + PointerCast(bcx, sgf, T_ptr(type_of_glue_fn(ccx, sti.ty)))) + } + } + }; + + // When static type info is available, avoid casting parameter because the + // function already has the right type. Otherwise cast to generic pointer. + let llrawptr = if is_none(static_ti) || is_none(static_glue_fn) { + PointerCast(bcx, v, T_ptr(T_i8())) + } else { + v + }; + + let llfn = { + match static_glue_fn { + None => { + // Select out the glue function to call from the tydesc + let llfnptr = GEPi(bcx, tydesc, [0u, field]); + Load(bcx, llfnptr) + } + Some(sgf) => sgf + } + }; + + Call(bcx, llfn, ~[C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())), + C_null(T_ptr(T_ptr(bcx.ccx().tydesc_type))), llrawptr]); +} + +// See [Note-arg-mode] +fn call_tydesc_glue(++cx: block, v: ValueRef, t: ty::t, field: uint) + -> block { + let _icx = cx.insn_ctxt("call_tydesc_glue"); + let ti = get_tydesc(cx.ccx(), t); + call_tydesc_glue_full(cx, v, ti.tydesc, field, Some(ti)); + return cx; +} + +fn call_cmp_glue(bcx: block, lhs: ValueRef, rhs: ValueRef, t: ty::t, + llop: ValueRef) -> ValueRef { + // We can't use call_tydesc_glue_full() and friends here because compare + // glue has a special signature. + let _icx = bcx.insn_ctxt("call_cmp_glue"); + + let lllhs = spill_if_immediate(bcx, lhs, t); + let llrhs = spill_if_immediate(bcx, rhs, t); + + let llrawlhsptr = BitCast(bcx, lllhs, T_ptr(T_i8())); + let llrawrhsptr = BitCast(bcx, llrhs, T_ptr(T_i8())); + let lltydesc = get_tydesc_simple(bcx.ccx(), t); + + let llfn = bcx.ccx().upcalls.cmp_type; + + let llcmpresultptr = alloca(bcx, T_i1()); + Call(bcx, llfn, ~[llcmpresultptr, lltydesc, + llrawlhsptr, llrawrhsptr, llop]); + return Load(bcx, llcmpresultptr); +} + +fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) { + let _icx = bcx.insn_ctxt("make_visit_glue"); + let mut bcx = bcx; + let ty_visitor_name = special_idents::ty_visitor; + assert bcx.ccx().tcx.intrinsic_defs.contains_key(ty_visitor_name); + let (trait_id, ty) = bcx.ccx().tcx.intrinsic_defs.get(ty_visitor_name); + let v = PointerCast(bcx, v, T_ptr(type_of::type_of(bcx.ccx(), ty))); + bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, trait_id); + build_return(bcx); +} + +fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { + // NB: v0 is an *alias* of type t here, not a direct value. + let _icx = bcx.insn_ctxt("make_free_glue"); + let ccx = bcx.ccx(); + let bcx = match ty::get(t).struct { + ty::ty_box(body_mt) => { + let v = Load(bcx, v); + let body = GEPi(bcx, v, [0u, abi::box_field_body]); + // Cast away the addrspace of the box pointer. + let body = PointerCast(bcx, body, T_ptr(type_of(ccx, body_mt.ty))); + let bcx = drop_ty(bcx, body, body_mt.ty); + trans_free(bcx, v) + } + ty::ty_opaque_box => { + let v = Load(bcx, v); + let td = Load(bcx, GEPi(bcx, v, [0u, abi::box_field_tydesc])); + let valptr = GEPi(bcx, v, [0u, abi::box_field_body]); + // Generate code that, dynamically, indexes into the + // tydesc and calls the drop glue that got set dynamically + call_tydesc_glue_full(bcx, valptr, td, abi::tydesc_field_drop_glue, + None); + trans_free(bcx, v) + } + ty::ty_uniq(*) => { + uniq::make_free_glue(bcx, v, t) + } + ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) | + ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => { + make_free_glue(bcx, v, + tvec::expand_boxed_vec_ty(bcx.tcx(), t)); + return; + } + ty::ty_fn(_) => { + closure::make_fn_glue(bcx, v, t, free_ty) + } + ty::ty_opaque_closure_ptr(ck) => { + closure::make_opaque_cbox_free_glue(bcx, ck, v) + } + ty::ty_class(did, ref substs) => { + // Call the dtor if there is one + do option::map_default(ty::ty_dtor(bcx.tcx(), did), bcx) |dt_id| { + trans_class_drop(bcx, v, dt_id, did, substs) + } + } + _ => bcx + }; + build_return(bcx); +} + +fn trans_class_drop(bcx: block, + v0: ValueRef, + dtor_did: ast::def_id, + class_did: ast::def_id, + substs: &ty::substs) -> block { + let drop_flag = GEPi(bcx, v0, struct_dtor()); + do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| { + let mut bcx = cx; + + // Find and call the actual destructor + let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, + class_did, substs.tps); + + // The second argument is the "self" argument for drop + let params = lib::llvm::fn_ty_param_tys( + llvm::LLVMGetElementType(llvm::LLVMTypeOf(dtor_addr))); + + // Class dtors have no explicit args, so the params should + // just consist of the output pointer and the environment + // (self) + assert(params.len() == 2u); + let self_arg = PointerCast(bcx, v0, params[1u]); + let args = ~[bcx.fcx.llretptr, self_arg]; + Call(bcx, dtor_addr, args); + + // Drop the fields + let field_tys = + ty::class_items_as_mutable_fields(bcx.tcx(), class_did, + substs); + for vec::eachi(field_tys) |i, fld| { + let llfld_a = GEPi(bcx, v0, struct_field(i)); + bcx = drop_ty(bcx, llfld_a, fld.mt.ty); + } + + Store(bcx, C_u8(0u), drop_flag); + bcx + } +} + + +fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { + // NB: v0 is an *alias* of type t here, not a direct value. + let _icx = bcx.insn_ctxt("make_drop_glue"); + let ccx = bcx.ccx(); + let bcx = match ty::get(t).struct { + ty::ty_box(_) | ty::ty_opaque_box | + ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) => { + decr_refcnt_maybe_free(bcx, Load(bcx, v0), t) + } + ty::ty_uniq(_) | + ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => { + free_ty(bcx, v0, t) + } + ty::ty_unboxed_vec(_) => { + tvec::make_drop_glue_unboxed(bcx, v0, t) + } + ty::ty_class(did, ref substs) => { + let tcx = bcx.tcx(); + match ty::ty_dtor(tcx, did) { + Some(dtor) => { + trans_class_drop(bcx, v0, dtor, did, substs) + } + None => { + // No dtor? Just the default case + iter_structural_ty(bcx, v0, t, drop_ty) + } + } + } + ty::ty_fn(_) => { + closure::make_fn_glue(bcx, v0, t, drop_ty) + } + ty::ty_trait(_, _, _) => { + let llbox = Load(bcx, GEPi(bcx, v0, [0u, 1u])); + decr_refcnt_maybe_free(bcx, llbox, ty::mk_opaque_box(ccx.tcx)) + } + ty::ty_opaque_closure_ptr(ck) => { + closure::make_opaque_cbox_drop_glue(bcx, ck, v0) + } + _ => { + if ty::type_needs_drop(ccx.tcx, t) && + ty::type_is_structural(t) { + iter_structural_ty(bcx, v0, t, drop_ty) + } else { bcx } + } + }; + build_return(bcx); +} + +fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t) -> block { + let _icx = bcx.insn_ctxt("decr_refcnt_maybe_free"); + let ccx = bcx.ccx(); + + do with_cond(bcx, IsNotNull(bcx, box_ptr)) |bcx| { + let rc_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_refcnt]); + let rc = Sub(bcx, Load(bcx, rc_ptr), C_int(ccx, 1)); + Store(bcx, rc, rc_ptr); + let zero_test = ICmp(bcx, lib::llvm::IntEQ, C_int(ccx, 0), rc); + with_cond(bcx, zero_test, |bcx| free_ty_immediate(bcx, box_ptr, t)) + } +} + + +fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) { + let _icx = bcx.insn_ctxt("make_take_glue"); + // NB: v is a *pointer* to type t here, not a direct value. + let bcx = match ty::get(t).struct { + ty::ty_box(_) | ty::ty_opaque_box | + ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => { + incr_refcnt_of_boxed(bcx, Load(bcx, v)); bcx + } + ty::ty_uniq(_) => { + let Result {bcx, val} = uniq::duplicate(bcx, Load(bcx, v), t); + Store(bcx, val, v); + bcx + } + ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => { + let Result {bcx, val} = tvec::duplicate_uniq(bcx, Load(bcx, v), t); + Store(bcx, val, v); + bcx + } + ty::ty_evec(_, ty::vstore_slice(_)) + | ty::ty_estr(ty::vstore_slice(_)) => { + bcx + } + ty::ty_fn(_) => { + closure::make_fn_glue(bcx, v, t, take_ty) + } + ty::ty_trait(_, _, _) => { + let llbox = Load(bcx, GEPi(bcx, v, [0u, 1u])); + incr_refcnt_of_boxed(bcx, llbox); + bcx + } + ty::ty_opaque_closure_ptr(ck) => { + closure::make_opaque_cbox_take_glue(bcx, ck, v) + } + _ if ty::type_is_structural(t) => { + iter_structural_ty(bcx, v, t, take_ty) + } + _ => bcx + }; + + build_return(bcx); +} + +fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) { + let _icx = cx.insn_ctxt("incr_refcnt_of_boxed"); + let ccx = cx.ccx(); + let rc_ptr = GEPi(cx, box_ptr, [0u, abi::box_field_refcnt]); + let rc = Load(cx, rc_ptr); + let rc = Add(cx, rc, C_int(ccx, 1)); + Store(cx, rc, rc_ptr); +} + + +// Chooses the addrspace for newly declared types. +fn declare_tydesc_addrspace(ccx: @crate_ctxt, t: ty::t) -> addrspace { + if !ty::type_needs_drop(ccx.tcx, t) { + return default_addrspace; + } else if ty::type_is_immediate(t) { + // For immediate types, we don't actually need an addrspace, because + // e.g. boxed types include pointers to their contents which are + // already correctly tagged with addrspaces. + return default_addrspace; + } else { + return ccx.next_addrspace(); + } +} + +// Generates the declaration for (but doesn't emit) a type descriptor. +fn declare_tydesc(ccx: @crate_ctxt, t: ty::t) -> @tydesc_info { + let _icx = ccx.insn_ctxt("declare_tydesc"); + // If emit_tydescs already ran, then we shouldn't be creating any new + // tydescs. + assert !ccx.finished_tydescs; + + let llty = type_of(ccx, t); + + if ccx.sess.count_type_sizes() { + io::println(fmt!("%u\t%s", + llsize_of_real(ccx, llty), + ty_to_str(ccx.tcx, t))); + } + + let llsize = llsize_of(ccx, llty); + let llalign = llalign_of(ccx, llty); + let addrspace = declare_tydesc_addrspace(ccx, t); + //XXX this triggers duplicate LLVM symbols + let name = if false /*ccx.sess.opts.debuginfo*/ { + mangle_internal_name_by_type_only(ccx, t, ~"tydesc") + } else { mangle_internal_name_by_seq(ccx, ~"tydesc") }; + note_unique_llvm_symbol(ccx, name); + log(debug, fmt!("+++ declare_tydesc %s %s", ty_to_str(ccx.tcx, t), name)); + let gvar = str::as_c_str(name, |buf| { + llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type, buf) + }); + let inf = + @{ty: t, + tydesc: gvar, + size: llsize, + align: llalign, + addrspace: addrspace, + mut take_glue: None, + mut drop_glue: None, + mut free_glue: None, + mut visit_glue: None}; + log(debug, ~"--- declare_tydesc " + ppaux::ty_to_str(ccx.tcx, t)); + return inf; +} + +type glue_helper = fn@(block, ValueRef, ty::t); + +fn declare_generic_glue(ccx: @crate_ctxt, t: ty::t, llfnty: TypeRef, + name: ~str) -> ValueRef { + let _icx = ccx.insn_ctxt("declare_generic_glue"); + let name = name; + let mut fn_nm; + //XXX this triggers duplicate LLVM symbols + if false /*ccx.sess.opts.debuginfo*/ { + fn_nm = mangle_internal_name_by_type_only(ccx, t, (~"glue_" + name)); + } else { + fn_nm = mangle_internal_name_by_seq(ccx, (~"glue_" + name)); + } + note_unique_llvm_symbol(ccx, fn_nm); + let llfn = decl_cdecl_fn(ccx.llmod, fn_nm, llfnty); + set_glue_inlining(llfn, t); + return llfn; +} + +fn make_generic_glue_inner(ccx: @crate_ctxt, t: ty::t, + llfn: ValueRef, helper: glue_helper) -> ValueRef { + let _icx = ccx.insn_ctxt("make_generic_glue_inner"); + let fcx = new_fn_ctxt(ccx, ~[], llfn, None); + lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); + ccx.stats.n_glues_created += 1u; + // All glue functions take values passed *by alias*; this is a + // requirement since in many contexts glue is invoked indirectly and + // the caller has no idea if it's dealing with something that can be + // passed by value. + // + // llfn is expected be declared to take a parameter of the appropriate + // type, so we don't need to explicitly cast the function parameter. + + let bcx = top_scope_block(fcx, None); + let lltop = bcx.llbb; + let llrawptr0 = llvm::LLVMGetParam(llfn, 3u as c_uint); + helper(bcx, llrawptr0, t); + finish_fn(fcx, lltop); + return llfn; +} + +fn make_generic_glue(ccx: @crate_ctxt, t: ty::t, llfn: ValueRef, + helper: glue_helper, name: ~str) + -> ValueRef { + let _icx = ccx.insn_ctxt("make_generic_glue"); + if !ccx.sess.trans_stats() { + return make_generic_glue_inner(ccx, t, llfn, helper); + } + + let start = time::get_time(); + let llval = make_generic_glue_inner(ccx, t, llfn, helper); + let end = time::get_time(); + log_fn_time(ccx, ~"glue " + name + ~" " + ty_to_short_str(ccx.tcx, t), + start, end); + return llval; +} + +fn emit_tydescs(ccx: @crate_ctxt) { + let _icx = ccx.insn_ctxt("emit_tydescs"); + // As of this point, allow no more tydescs to be created. + ccx.finished_tydescs = true; + for ccx.tydescs.each |key, val| { + let glue_fn_ty = T_ptr(T_generic_glue_fn(ccx)); + let ti = val; + + // Each of the glue functions needs to be cast to a generic type + // before being put into the tydesc because we only have a singleton + // tydesc type. Then we'll recast each function to its real type when + // calling it. + let take_glue = + match copy ti.take_glue { + None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + Some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } + }; + let drop_glue = + match copy ti.drop_glue { + None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + Some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } + }; + let free_glue = + match copy ti.free_glue { + None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + Some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } + }; + let visit_glue = + match copy ti.visit_glue { + None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + Some(v) => { + ccx.stats.n_real_glues += 1u; + llvm::LLVMConstPointerCast(v, glue_fn_ty) + } + }; + + let shape = shape_of(ccx, key); + let shape_tables = + llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables, + T_ptr(T_i8())); + + let tydesc = + C_named_struct(ccx.tydesc_type, + ~[ti.size, // size + ti.align, // align + take_glue, // take_glue + drop_glue, // drop_glue + free_glue, // free_glue + visit_glue, // visit_glue + C_shape(ccx, shape), // shape + shape_tables]); // shape_tables + + let gvar = ti.tydesc; + llvm::LLVMSetInitializer(gvar, tydesc); + llvm::LLVMSetGlobalConstant(gvar, True); + lib::llvm::SetLinkage(gvar, lib::llvm::InternalLinkage); + + // Index tydesc by addrspace. + if ti.addrspace > gc_box_addrspace { + let llty = T_ptr(ccx.tydesc_type); + let addrspace_name = #fmt("_gc_addrspace_metadata_%u", + ti.addrspace as uint); + let addrspace_gvar = str::as_c_str(addrspace_name, |buf| { + llvm::LLVMAddGlobal(ccx.llmod, llty, buf) + }); + lib::llvm::SetLinkage(addrspace_gvar, lib::llvm::InternalLinkage); + llvm::LLVMSetInitializer(addrspace_gvar, gvar); + } + }; +} diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs index 81e57898091e9..ae128a10f7cb3 100644 --- a/src/rustc/middle/trans/impl.rs +++ b/src/rustc/middle/trans/impl.rs @@ -3,10 +3,9 @@ use base::*; use common::*; use type_of::*; use build::*; -use driver::session::session; +use driver::session::{session, expect}; use syntax::{ast, ast_map}; use ast_map::{path, path_mod, path_name, node_id_to_str}; -use driver::session::expect; use syntax::ast_util::local_def; use metadata::csearch; use back::{link, abi}; @@ -15,8 +14,11 @@ use lib::llvm::{ValueRef, TypeRef}; use lib::llvm::llvm::LLVMGetParam; use std::map::hashmap; use util::ppaux::{ty_to_str, tys_to_str}; - +use callee::*; use syntax::print::pprust::expr_to_str; +use expr::{SaveIn, Ignore}; + +fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. /** The main "translation" pass for methods. Generates code @@ -91,14 +93,13 @@ fn trans_method(ccx: @crate_ctxt, } fn trans_self_arg(bcx: block, base: @ast::expr, - mentry: typeck::method_map_entry) -> result { + mentry: typeck::method_map_entry) -> Result { let _icx = bcx.insn_ctxt("impl::trans_self_arg"); let basety = expr_ty(bcx, base); let mode = ast::expl(mentry.self_mode); let mut temp_cleanups = ~[]; - let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, - T_ptr(type_of::type_of(bcx.ccx(), basety)), - base, temp_cleanups, None, mentry.derefs); + let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, base, + &mut temp_cleanups, None, mentry.derefs); // by-ref self argument should not require cleanup in the case of // other arguments failing: @@ -112,57 +113,61 @@ fn trans_self_arg(bcx: block, base: @ast::expr, fn trans_method_callee(bcx: block, callee_id: ast::node_id, self: @ast::expr, mentry: typeck::method_map_entry) - -> lval_maybe_callee { + -> Callee +{ let _icx = bcx.insn_ctxt("impl::trans_method_callee"); match mentry.origin { - typeck::method_static(did) => { - - - let {bcx, val} = trans_self_arg(bcx, self, mentry); - {env: self_env(val, node_id_type(bcx, self.id), None, - mentry.self_mode), - .. lval_static_fn(bcx, did, callee_id)} - } - typeck::method_param({trait_id:trait_id, method_num:off, - param_num:p, bound_num:b}) => { - match bcx.fcx.param_substs { - Some(substs) => { - let vtbl = find_vtable_in_fn_ctxt(substs, p, b); - trans_monomorphized_callee(bcx, callee_id, self, mentry, - trait_id, off, vtbl) - } - // how to get rid of this? - None => fail ~"trans_method_callee: missing param_substs" + typeck::method_static(did) => { + let callee_fn = callee::trans_fn_ref(bcx, did, callee_id); + let Result {bcx, val} = trans_self_arg(bcx, self, mentry); + + Callee { + bcx: bcx, + data: Method(MethodData { + llfn: callee_fn.llfn, + llself: val, + self_ty: node_id_type(bcx, self.id), + self_mode: mentry.self_mode + }) + } + } + typeck::method_param({trait_id:trait_id, method_num:off, + param_num:p, bound_num:b}) => { + match bcx.fcx.param_substs { + Some(substs) => { + let vtbl = find_vtable_in_fn_ctxt(substs, p, b); + trans_monomorphized_callee(bcx, callee_id, self, mentry, + trait_id, off, vtbl) + } + // how to get rid of this? + None => fail ~"trans_method_callee: missing param_substs" + } + } + typeck::method_trait(_, off) => { + trans_trait_callee(bcx, callee_id, off, self, mentry.derefs) } - } - typeck::method_trait(_, off) => { - let {bcx, val} = trans_temp_expr(bcx, self); - let fty = node_id_type(bcx, callee_id); - let self_ty = node_id_type(bcx, self.id); - let {bcx, val, _} = autoderef(bcx, self.id, val, self_ty, - uint::max_value); - trans_trait_callee(bcx, val, fty, off) - } } } -fn trans_static_method_callee(bcx: block, method_id: ast::def_id, - callee_id: ast::node_id) -> lval_maybe_callee { +fn trans_static_method_callee(bcx: block, + method_id: ast::def_id, + callee_id: ast::node_id) -> FnData +{ let _icx = bcx.insn_ctxt("impl::trans_static_method_callee"); let ccx = bcx.ccx(); let mname = if method_id.crate == ast::local_crate { match bcx.tcx().items.get(method_id.node) { - ast_map::node_trait_method(trait_method, _, _) => { - ast_util::trait_method_to_ty_method(*trait_method).ident - } - _ => fail ~"callee is not a trait method" + ast_map::node_trait_method(trait_method, _, _) => { + ast_util::trait_method_to_ty_method(*trait_method).ident + } + _ => fail ~"callee is not a trait method" } } else { let path = csearch::get_item_path(bcx.tcx(), method_id); match path[path.len()-1] { - path_name(s) => { s } - path_mod(_) => { fail ~"path doesn't have a name?" } + path_name(s) => { s } + path_mod(_) => { fail ~"path doesn't have a name?" } } }; debug!("trans_static_method_callee: method_id=%?, callee_id=%?, \ @@ -172,26 +177,28 @@ fn trans_static_method_callee(bcx: block, method_id: ast::def_id, bcx.fcx, ccx.maps.vtable_map.get(callee_id)); match vtbls[0] { // is index 0 always the one we want? - typeck::vtable_static(impl_did, impl_substs, sub_origins) => { - - let mth_id = method_with_name(bcx.ccx(), impl_did, mname); - let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did); - let node_substs = node_id_type_params(bcx, callee_id); - let ty_substs - = vec::append(impl_substs, - vec::tailn(node_substs, - node_substs.len() - n_m_tps)); - - let lval = lval_static_fn_inner(bcx, mth_id, callee_id, ty_substs, - Some(sub_origins)); - {env: null_env, - val: PointerCast(bcx, lval.val, T_ptr(type_of_fn_from_ty( - ccx, node_id_type(bcx, callee_id)))), - .. lval} - } - _ => { - fail ~"vtable_param left in monomorphized function's vtable substs"; - } + typeck::vtable_static(impl_did, impl_substs, sub_origins) => { + + let mth_id = method_with_name(bcx.ccx(), impl_did, mname); + let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did); + let node_substs = node_id_type_params(bcx, callee_id); + let ty_substs + = vec::append(impl_substs, + vec::tailn(node_substs, + node_substs.len() - n_m_tps)); + + let FnData {llfn: lval} = + trans_fn_ref_with_vtables(bcx, mth_id, callee_id, + ty_substs, Some(sub_origins)); + + let callee_ty = node_id_type(bcx, callee_id); + let llty = T_ptr(type_of_fn_from_ty(ccx, callee_ty)); + FnData {llfn: PointerCast(bcx, lval, llty)} + } + _ => { + fail ~"vtable_param left in monomorphized \ + function's vtable substs"; + } } } @@ -231,67 +238,138 @@ fn method_ty_param_count(ccx: @crate_ctxt, m_id: ast::def_id, } } -fn trans_monomorphized_callee(bcx: block, callee_id: ast::node_id, +fn trans_monomorphized_callee(bcx: block, + callee_id: ast::node_id, base: @ast::expr, mentry: typeck::method_map_entry, - trait_id: ast::def_id, n_method: uint, + trait_id: ast::def_id, + n_method: uint, vtbl: typeck::vtable_origin) - -> lval_maybe_callee { + -> Callee +{ let _icx = bcx.insn_ctxt("impl::trans_monomorphized_callee"); match vtbl { typeck::vtable_static(impl_did, impl_substs, sub_origins) => { - let ccx = bcx.ccx(); - let mname = ty::trait_methods(ccx.tcx, trait_id)[n_method].ident; - let mth_id = method_with_name(bcx.ccx(), impl_did, mname); - let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did); - let node_substs = node_id_type_params(bcx, callee_id); - let ty_substs - = vec::append(impl_substs, - vec::tailn(node_substs, - node_substs.len() - n_m_tps)); - let {bcx, val} = trans_self_arg(bcx, base, mentry); - let lval = lval_static_fn_inner(bcx, mth_id, callee_id, ty_substs, - Some(sub_origins)); - {env: self_env(val, node_id_type(bcx, base.id), - None, mentry.self_mode), - val: PointerCast(bcx, lval.val, T_ptr(type_of_fn_from_ty( - ccx, node_id_type(bcx, callee_id)))), - .. lval} + let ccx = bcx.ccx(); + let mname = ty::trait_methods(ccx.tcx, trait_id)[n_method].ident; + let mth_id = method_with_name(bcx.ccx(), impl_did, mname); + + // obtain the `self` value: + let Result {bcx, val: llself_val} = + trans_self_arg(bcx, base, mentry); + + // create a concatenated set of substitutions which includes + // those from the impl and those from the method: + let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did); + let node_substs = node_id_type_params(bcx, callee_id); + let ty_substs + = vec::append(impl_substs, + vec::tailn(node_substs, + node_substs.len() - n_m_tps)); + debug!("n_m_tps=%?", n_m_tps); + debug!("impl_substs=%?", impl_substs.map(|t| bcx.ty_to_str(t))); + debug!("node_substs=%?", node_substs.map(|t| bcx.ty_to_str(t))); + debug!("ty_substs=%?", ty_substs.map(|t| bcx.ty_to_str(t))); + + // translate the function + let callee = trans_fn_ref_with_vtables( + bcx, mth_id, callee_id, ty_substs, Some(sub_origins)); + + // create a llvalue that represents the fn ptr + let fn_ty = node_id_type(bcx, callee_id); + let llfn_ty = T_ptr(type_of_fn_from_ty(ccx, fn_ty)); + let llfn_val = PointerCast(bcx, callee.llfn, llfn_ty); + + // combine the self environment with the rest + Callee { + bcx: bcx, + data: Method(MethodData { + llfn: llfn_val, + llself: llself_val, + self_ty: node_id_type(bcx, base.id), + self_mode: mentry.self_mode + }) + } } typeck::vtable_trait(*) => { - let {bcx, val} = trans_temp_expr(bcx, base); - let fty = node_id_type(bcx, callee_id); - trans_trait_callee(bcx, val, fty, n_method) + trans_trait_callee(bcx, callee_id, n_method, base, mentry.derefs) } typeck::vtable_param(*) => { - fail ~"vtable_param left in monomorphized function's vtable substs"; + fail ~"vtable_param left in monomorphized function's vtable substs"; } } } -// Method callee where the vtable comes from a boxed trait -fn trans_trait_callee(bcx: block, val: ValueRef, - callee_ty: ty::t, n_method: uint) - -> lval_maybe_callee { +fn trans_trait_callee(bcx: block, + callee_id: ast::node_id, + n_method: uint, + self_expr: @ast::expr, + autoderefs: uint) + -> Callee +{ + //! + // + // Create a method callee where the method is coming from a trait + // instance (e.g., @Trait type). In this case, we must pull the + // fn pointer out of the vtable that is packaged up with the + // @Trait instance. @Traits are represented as a pair, so we first + // evaluate the self expression (expected a by-ref result) and then + // extract the self data and vtable out of the pair. + + let _icx = bcx.insn_ctxt("impl::trans_trait_callee"); + let mut bcx = bcx; + let self_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr)); + let self_datum = self_datum.autoderef(bcx, self_expr.id, autoderefs); + let llpair = self_datum.to_ref_llval(bcx); + let callee_ty = node_id_type(bcx, callee_id); + trans_trait_callee_from_llval(bcx, callee_ty, n_method, llpair) +} + +fn trans_trait_callee_from_llval(bcx: block, + callee_ty: ty::t, + n_method: uint, + llpair: ValueRef) + -> Callee +{ + //! + // + // Same as `trans_trait_callee()` above, except that it is given + // a by-ref pointer to the @Trait pair. + let _icx = bcx.insn_ctxt("impl::trans_trait_callee"); let ccx = bcx.ccx(); - let vtable = Load(bcx, PointerCast(bcx, GEPi(bcx, val, [0u, 0u]), - T_ptr(T_ptr(T_vtable())))); - let llbox = Load(bcx, GEPi(bcx, val, [0u, 1u])); - // FIXME[impl] I doubt this is alignment-safe (#2534) - let self = GEPi(bcx, llbox, [0u, abi::box_field_body]); - let env = self_env(self, ty::mk_opaque_box(bcx.tcx()), Some(llbox), - // XXX: is this bogosity? - ast::by_ref); - let llfty = type_of::type_of_fn_from_ty(ccx, callee_ty); - let vtable = PointerCast(bcx, vtable, - T_ptr(T_array(T_ptr(llfty), n_method + 1u))); - let mptr = Load(bcx, GEPi(bcx, vtable, [0u, n_method])); - {bcx: bcx, val: mptr, kind: lv_owned, env: env} + let mut bcx = bcx; + + // Load the vtable from the @Trait pair + let llvtable = Load(bcx, + PointerCast(bcx, + GEPi(bcx, llpair, [0u, 0u]), + T_ptr(T_ptr(T_vtable())))); + + // Load the box from the @Trait pair and GEP over the box header: + let llbox = Load(bcx, GEPi(bcx, llpair, [0u, 1u])); + let llself = GEPi(bcx, llbox, [0u, abi::box_field_body]); + + // Load the function from the vtable and cast it to the expected type. + let llcallee_ty = type_of::type_of_fn_from_ty(ccx, callee_ty); + let mptr = Load(bcx, GEPi(bcx, llvtable, [0u, n_method])); + let mptr = PointerCast(bcx, mptr, T_ptr(llcallee_ty)); + + return Callee { + bcx: bcx, + data: Method(MethodData { + llfn: mptr, + llself: llself, + self_ty: ty::mk_opaque_box(bcx.tcx()), + self_mode: ast::by_ref, // XXX: is this bogosity? + /* XXX: Some(llbox) */ + }) + }; } fn find_vtable_in_fn_ctxt(ps: param_substs, n_param: uint, n_bound: uint) - -> typeck::vtable_origin { + -> typeck::vtable_origin +{ let mut vtable_off = n_bound, i = 0u; // Vtables are stored in a flat array, finding the right one is // somewhat awkward @@ -339,17 +417,18 @@ fn resolve_vtable_in_fn_ctxt(fcx: fn_ctxt, vt: typeck::vtable_origin) fn vtable_id(ccx: @crate_ctxt, origin: typeck::vtable_origin) -> mono_id { match origin { - typeck::vtable_static(impl_id, substs, sub_vtables) => { - make_mono_id(ccx, impl_id, substs, - if (*sub_vtables).len() == 0u { None } - else { Some(sub_vtables) }, None) - } - typeck::vtable_trait(trait_id, substs) => { - @{def: trait_id, - params: vec::map(substs, |t| mono_precise(t, None))} - } - // can't this be checked at the callee? - _ => fail ~"vtable_id" + typeck::vtable_static(impl_id, substs, sub_vtables) => { + monomorphize::make_mono_id( + ccx, impl_id, substs, + if (*sub_vtables).len() == 0u { None } + else { Some(sub_vtables) }, None) + } + typeck::vtable_trait(trait_id, substs) => { + @{def: trait_id, + params: vec::map(substs, |t| mono_precise(t, None))} + } + // can't this be checked at the callee? + _ => fail ~"vtable_id" } } @@ -385,9 +464,10 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: ~[ty::t], let tcx = ccx.tcx; // XXX: This should support multiple traits. - let trt_id = expect(ccx.sess, - ty::ty_to_def_id(ty::impl_traits(tcx, impl_id)[0]), - || ~"make_impl_vtable: non-trait-type implemented"); + let trt_id = driver::session::expect( + tcx.sess, + ty::ty_to_def_id(ty::impl_traits(tcx, impl_id)[0]), + || ~"make_impl_vtable: non-trait-type implemented"); let has_tps = (*ty::lookup_item_type(ccx.tcx, impl_id).bounds).len() > 0u; make_vtable(ccx, vec::map(*ty::trait_methods(tcx, trt_id), |im| { @@ -400,9 +480,10 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: ~[ty::t], // If the method is in another crate, need to make an inlined // copy first if m_id.crate != ast::local_crate { - m_id = maybe_instantiate_inline(ccx, m_id); + m_id = inline::maybe_instantiate_inline(ccx, m_id); } - monomorphic_fn(ccx, m_id, substs, Some(vtables), None).val + monomorphize::monomorphic_fn(ccx, m_id, substs, + Some(vtables), None).val } else if m_id.crate == ast::local_crate { get_item_val(ccx, m_id.node) } else { @@ -412,23 +493,42 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: ~[ty::t], })) } -fn trans_cast(bcx: block, val: @ast::expr, id: ast::node_id, dest: dest) - -> block { +fn trans_trait_cast(bcx: block, + val: @ast::expr, + id: ast::node_id, + dest: expr::Dest) + -> block +{ let _icx = bcx.insn_ctxt("impl::trans_cast"); - if dest == ignore { return trans_expr(bcx, val, ignore); } + + let lldest = match dest { + Ignore => { + return expr::trans_into(bcx, val, Ignore); + } + SaveIn(dest) => dest + }; + let ccx = bcx.ccx(); let v_ty = expr_ty(bcx, val); + + // Allocate an @ box and store the value into it let {bcx: bcx, box: llbox, body: body} = malloc_boxed(bcx, v_ty); add_clean_free(bcx, llbox, heap_shared); - let bcx = trans_expr_save_in(bcx, val, body); + let bcx = expr::trans_into(bcx, val, SaveIn(body)); revoke_clean(bcx, llbox); - let result = get_dest_addr(dest); - Store(bcx, llbox, PointerCast(bcx, GEPi(bcx, result, [0u, 1u]), + + // Store the @ box into the pair + Store(bcx, llbox, PointerCast(bcx, + GEPi(bcx, lldest, [0u, 1u]), T_ptr(val_ty(llbox)))); + + // Store the vtable into the pair let orig = ccx.maps.vtable_map.get(id)[0]; let orig = resolve_vtable_in_fn_ctxt(bcx.fcx, orig); let vtable = get_vtable(bcx.ccx(), orig); - Store(bcx, vtable, PointerCast(bcx, GEPi(bcx, result, [0u, 0u]), + Store(bcx, vtable, PointerCast(bcx, + GEPi(bcx, lldest, [0u, 0u]), T_ptr(val_ty(vtable)))); + bcx } diff --git a/src/rustc/middle/trans/inline.rs b/src/rustc/middle/trans/inline.rs new file mode 100644 index 0000000000000..6de7e77a39464 --- /dev/null +++ b/src/rustc/middle/trans/inline.rs @@ -0,0 +1,87 @@ +use common::*; +use syntax::ast; +use syntax::ast_util::local_def; +use syntax::ast_map::{path, path_mod, path_name}; +use base::{trans_item, get_item_val, self_arg, trans_fn, + impl_self, get_insn_ctxt}; + +fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id) + -> ast::def_id +{ + let _icx = ccx.insn_ctxt("maybe_instantiate_inline"); + match ccx.external.find(fn_id) { + Some(Some(node_id)) => { + // Already inline + debug!("maybe_instantiate_inline(%s): already inline as node id %d", + ty::item_path_str(ccx.tcx, fn_id), node_id); + local_def(node_id) + } + Some(None) => fn_id, // Not inlinable + None => { // Not seen yet + match csearch::maybe_get_item_ast( + ccx.tcx, fn_id, + |a,b,c,d| { + astencode::decode_inlined_item(a, b, ccx.maps, c, d) + }) { + + csearch::not_found => { + ccx.external.insert(fn_id, None); + fn_id + } + csearch::found(ast::ii_item(item)) => { + ccx.external.insert(fn_id, Some(item.id)); + trans_item(ccx, *item); + local_def(item.id) + } + csearch::found(ast::ii_ctor(ctor, _, _, _)) => { + ccx.external.insert(fn_id, Some(ctor.node.id)); + local_def(ctor.node.id) + } + csearch::found(ast::ii_foreign(item)) => { + ccx.external.insert(fn_id, Some(item.id)); + local_def(item.id) + } + csearch::found_parent(parent_id, ast::ii_item(item)) => { + ccx.external.insert(parent_id, Some(item.id)); + let mut my_id = 0; + match item.node { + ast::item_enum(_, _) => { + let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id)); + let vs_there = ty::enum_variants(ccx.tcx, parent_id); + do vec::iter2(*vs_here, *vs_there) |here, there| { + if there.id == fn_id { my_id = here.id.node; } + ccx.external.insert(there.id, Some(here.id.node)); + } + } + _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \ + non-enum parent") + } + trans_item(ccx, *item); + local_def(my_id) + } + csearch::found_parent(_, _) => { + ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \ + with a non-item parent"); + } + csearch::found(ast::ii_method(impl_did, mth)) => { + ccx.external.insert(fn_id, Some(mth.id)); + let {bounds: impl_bnds, region_param: _, ty: impl_ty} = + ty::lookup_item_type(ccx.tcx, impl_did); + if (*impl_bnds).len() + mth.tps.len() == 0u { + let llfn = get_item_val(ccx, mth.id); + let path = vec::append( + ty::item_path(ccx.tcx, impl_did), + ~[path_name(mth.ident)]); + trans_fn(ccx, path, mth.decl, mth.body, + llfn, impl_self(impl_ty), None, mth.id); + } + local_def(mth.id) + } + csearch::found(ast::ii_dtor(dtor, _, _, _)) => { + ccx.external.insert(fn_id, Some(dtor.node.id)); + local_def(dtor.node.id) + } + } + } + } +} diff --git a/src/rustc/middle/trans/macros.rs b/src/rustc/middle/trans/macros.rs new file mode 100644 index 0000000000000..6a813b7c1b244 --- /dev/null +++ b/src/rustc/middle/trans/macros.rs @@ -0,0 +1,45 @@ +{ + +macro_rules! unpack_datum( + ($bcx: ident, $inp: expr) => ( + { + let db = $inp; + $bcx = db.bcx; + db.datum + } + ) +); + +macro_rules! unpack_result( + ($bcx: ident, $inp: expr) => ( + { + let db = $inp; + $bcx = db.bcx; + db.val + } + ) +); + +macro_rules! trace_span( + ($bcx: ident, $sp: expr, $str: expr) => ( + { + let bcx = $bcx; + if bcx.sess().trace() { + trans_trace(bcx, Some($sp), $str); + } + } + ) +); + +macro_rules! trace( + ($bcx: ident, $str: expr) => ( + { + let bcx = $bcx; + if bcx.sess().trace() { + trans_trace(bcx, None, $str); + } + } + ) +); + +} \ No newline at end of file diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs new file mode 100644 index 0000000000000..962aed37ff1f9 --- /dev/null +++ b/src/rustc/middle/trans/monomorphize.rs @@ -0,0 +1,276 @@ +use common::*; +use syntax::ast; +use syntax::ast_util::local_def; +use syntax::ast_map::{path, path_mod, path_name}; +use base::{trans_item, get_item_val, no_self, self_arg, trans_fn, + impl_self, decl_internal_cdecl_fn, + set_inline_hint_if_appr, set_inline_hint, + trans_enum_variant, trans_class_ctor, trans_class_dtor, + get_insn_ctxt}; +use syntax::parse::token::special_idents; +use type_of::type_of_fn_from_ty; +use back::link::mangle_exported_name; + +fn monomorphic_fn(ccx: @crate_ctxt, + fn_id: ast::def_id, + real_substs: ~[ty::t], + vtables: Option, + ref_id: Option) + -> {val: ValueRef, must_cast: bool} +{ + let _icx = ccx.insn_ctxt("monomorphic_fn"); + let mut must_cast = false; + let substs = vec::map(real_substs, |t| { + match normalize_for_monomorphization(ccx.tcx, t) { + Some(t) => { must_cast = true; t } + None => t + } + }); + + for real_substs.each() |s| { assert !ty::type_has_params(s); } + for substs.each() |s| { assert !ty::type_has_params(s); } + let param_uses = type_use::type_uses_for(ccx, fn_id, substs.len()); + let hash_id = make_mono_id(ccx, fn_id, substs, vtables, Some(param_uses)); + if vec::any(hash_id.params, + |p| match p { mono_precise(_, _) => false, _ => true }) { + must_cast = true; + } + + #debug["monomorphic_fn(fn_id=%? (%s), real_substs=%?, substs=%?, \ + hash_id = %?", + fn_id, ty::item_path_str(ccx.tcx, fn_id), + real_substs.map(|s| ty_to_str(ccx.tcx, s)), + substs.map(|s| ty_to_str(ccx.tcx, s)), hash_id]; + + match ccx.monomorphized.find(hash_id) { + Some(val) => { + debug!("leaving monomorphic fn %s", + ty::item_path_str(ccx.tcx, fn_id)); + return {val: val, must_cast: must_cast}; + } + None => () + } + + let tpt = ty::lookup_item_type(ccx.tcx, fn_id); + let mut llitem_ty = tpt.ty; + + let map_node = session::expect(ccx.sess, ccx.tcx.items.find(fn_id.node), + || fmt!("While monomorphizing %?, couldn't find it in the item map \ + (may have attempted to monomorphize an item defined in a different \ + crate?)", fn_id)); + // Get the path so that we can create a symbol + let (pt, name, span) = match map_node { + ast_map::node_item(i, pt) => (pt, i.ident, i.span), + ast_map::node_variant(v, enm, pt) => (pt, v.node.name, enm.span), + ast_map::node_method(m, _, pt) => (pt, m.ident, m.span), + ast_map::node_foreign_item(i, ast::foreign_abi_rust_intrinsic, pt) + => (pt, i.ident, i.span), + ast_map::node_foreign_item(*) => { + // Foreign externs don't have to be monomorphized. + return {val: get_item_val(ccx, fn_id.node), + must_cast: true}; + } + ast_map::node_ctor(nm, _, ct, _, pt) => (pt, nm, ct.span), + ast_map::node_dtor(_, dtor, _, pt) => + (pt, special_idents::dtor, dtor.span), + ast_map::node_trait_method(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize a trait method") + } + ast_map::node_expr(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize an expr") + } + ast_map::node_stmt(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize a stmt") + } + ast_map::node_export(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize an export") + } + ast_map::node_arg(*) => ccx.tcx.sess.bug(~"Can't monomorphize an arg"), + ast_map::node_block(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize a block") + } + ast_map::node_local(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize a local") + } + }; + let mono_ty = ty::subst_tps(ccx.tcx, substs, llitem_ty); + let llfty = type_of_fn_from_ty(ccx, mono_ty); + + let depth = option::get_default(ccx.monomorphizing.find(fn_id), 0u); + // Random cut-off -- code that needs to instantiate the same function + // recursively more than ten times can probably safely be assumed to be + // causing an infinite expansion. + if depth > 10u { + ccx.sess.span_fatal( + span, ~"overly deep expansion of inlined function"); + } + ccx.monomorphizing.insert(fn_id, depth + 1u); + + let pt = vec::append(*pt, + ~[path_name(ccx.names(ccx.sess.str_of(name)))]); + let s = mangle_exported_name(ccx, pt, mono_ty); + + let mk_lldecl = || { + let lldecl = decl_internal_cdecl_fn(ccx.llmod, s, llfty); + ccx.monomorphized.insert(hash_id, lldecl); + lldecl + }; + + let psubsts = Some({tys: substs, vtables: vtables, bounds: tpt.bounds}); + let lldecl = match map_node { + ast_map::node_item(i@@{node: ast::item_fn(decl, _, _, body), _}, _) => { + let d = mk_lldecl(); + set_inline_hint_if_appr(i.attrs, d); + trans_fn(ccx, pt, decl, body, d, no_self, psubsts, fn_id.node); + d + } + ast_map::node_item(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize this kind of item") + } + ast_map::node_foreign_item(i, _, _) => { + let d = mk_lldecl(); + foreign::trans_intrinsic(ccx, d, i, pt, option::get(psubsts), + ref_id); + d + } + ast_map::node_variant(v, enum_item, _) => { + let tvs = ty::enum_variants(ccx.tcx, local_def(enum_item.id)); + let this_tv = option::get(vec::find(*tvs, |tv| { + tv.id.node == fn_id.node})); + let d = mk_lldecl(); + set_inline_hint(d); + match v.node.kind { + ast::tuple_variant_kind(args) => { + trans_enum_variant(ccx, enum_item.id, v, args, + this_tv.disr_val, (*tvs).len() == 1u, + psubsts, d); + } + ast::struct_variant_kind(_) => + ccx.tcx.sess.bug(~"can't monomorphize struct variants"), + ast::enum_variant_kind(_) => + ccx.tcx.sess.bug(~"can't monomorphize enum variants") + } + d + } + ast_map::node_method(mth, _, _) => { + let d = mk_lldecl(); + set_inline_hint_if_appr(mth.attrs, d); + impl::trans_method(ccx, pt, mth, psubsts, d); + d + } + ast_map::node_ctor(_, tps, ctor, parent_id, _) => { + // ctors don't have attrs, at least not right now + let d = mk_lldecl(); + let tp_tys = ty::ty_params_to_tys(ccx.tcx, tps); + trans_class_ctor(ccx, pt, ctor.node.dec, ctor.node.body, d, + option::get_default(psubsts, + {tys:tp_tys, vtables: None, bounds: @~[]}), + fn_id.node, parent_id, ctor.span); + d + } + ast_map::node_dtor(_, dtor, _, pt) => { + let parent_id = match ty::ty_to_def_id(ty::node_id_to_type(ccx.tcx, + dtor.node.self_id)) { + Some(did) => did, + None => ccx.sess.span_bug(dtor.span, ~"Bad self ty in \ + dtor") + }; + trans_class_dtor(ccx, *pt, dtor.node.body, + dtor.node.id, psubsts, Some(hash_id), parent_id) + } + // Ugh -- but this ensures any new variants won't be forgotten + ast_map::node_expr(*) | + ast_map::node_stmt(*) | + ast_map::node_trait_method(*) | + ast_map::node_export(*) | + ast_map::node_arg(*) | + ast_map::node_block(*) | + ast_map::node_local(*) => { + ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node)) + } + }; + ccx.monomorphizing.insert(fn_id, depth); + + debug!("leaving monomorphic fn %s", ty::item_path_str(ccx.tcx, fn_id)); + {val: lldecl, must_cast: must_cast} +} + +fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option { + // FIXME[mono] could do this recursively. is that worthwhile? (#2529) + match ty::get(ty).struct { + ty::ty_box(*) => { + Some(ty::mk_opaque_box(tcx)) + } + ty::ty_fn(ref fty) => { + Some(ty::mk_fn(tcx, {purity: ast::impure_fn, + proto: fty.proto, + bounds: @~[], + inputs: ~[], + output: ty::mk_nil(tcx), + ret_style: ast::return_val})) + } + ty::ty_trait(_, _, _) => { + Some(ty::mk_fn(tcx, {purity: ast::impure_fn, + proto: ty::proto_vstore(ty::vstore_box), + bounds: @~[], + inputs: ~[], + output: ty::mk_nil(tcx), + ret_style: ast::return_val})) + } + ty::ty_ptr(_) => Some(ty::mk_uint(tcx)), + _ => None + } +} + +fn make_mono_id(ccx: @crate_ctxt, item: ast::def_id, substs: ~[ty::t], + vtables: Option, + param_uses: Option<~[type_use::type_uses]>) -> mono_id { + let precise_param_ids = match vtables { + Some(vts) => { + let bounds = ty::lookup_item_type(ccx.tcx, item).bounds; + let mut i = 0u; + vec::map2(*bounds, substs, |bounds, subst| { + let mut v = ~[]; + for vec::each(*bounds) |bound| { + match bound { + ty::bound_trait(_) => { + vec::push(v, impl::vtable_id(ccx, vts[i])); + i += 1u; + } + _ => () + } + } + (subst, if v.len() > 0u { Some(v) } else { None }) + }) + } + None => { + vec::map(substs, |subst| (subst, None)) + } + }; + let param_ids = match param_uses { + Some(uses) => { + vec::map2(precise_param_ids, uses, |id, uses| { + match id { + (a, b@Some(_)) => mono_precise(a, b), + (subst, None) => { + if uses == 0u { mono_any } + else if uses == type_use::use_repr && + !ty::type_needs_drop(ccx.tcx, subst) { + let llty = type_of::type_of(ccx, subst); + let size = shape::llsize_of_real(ccx, llty); + let align = shape::llalign_of_pref(ccx, llty); + // Special value for nil to prevent problems with undef + // return pointers. + if size == 1u && ty::type_is_nil(subst) { + mono_repr(0u, 0u) + } else { mono_repr(size, align) } + } else { mono_precise(subst, None) } + } + } + }) + } + None => precise_param_ids.map(|x| { let (a, b) = x; + mono_precise(a, b) }) + }; + @{def: item, params: param_ids} +} diff --git a/src/rustc/middle/trans/reachable.rs b/src/rustc/middle/trans/reachable.rs index 234a81a0a2888..a9a7f4e4e9f33 100644 --- a/src/rustc/middle/trans/reachable.rs +++ b/src/rustc/middle/trans/reachable.rs @@ -144,7 +144,7 @@ fn traverse_public_item(cx: ctx, item: @item) { } fn mk_ty_visitor() -> visit::vt { - visit::mk_vt(@{visit_ty: traverse_ty,.. *visit::default_visitor()}) + visit::mk_vt(@{visit_ty: traverse_ty, ..*visit::default_visitor()}) } fn traverse_ty(ty: @ty, cx: ctx, v: visit::vt) { @@ -200,7 +200,7 @@ fn traverse_inline_body(cx: ctx, body: blk) { visit::visit_block(body, cx, visit::mk_vt(@{ visit_expr: traverse_expr, visit_item: traverse_item, - .. *visit::default_visitor() + ..*visit::default_visitor() })); } @@ -219,7 +219,7 @@ fn traverse_all_resources_and_impls(cx: ctx, crate_mod: _mod) { _ => () } }, - .. *visit::default_visitor() + ..*visit::default_visitor() })); } diff --git a/src/rustc/middle/trans/reflect.rs b/src/rustc/middle/trans/reflect.rs index 786d5eec5cbfb..c6d35e8582675 100644 --- a/src/rustc/middle/trans/reflect.rs +++ b/src/rustc/middle/trans/reflect.rs @@ -9,6 +9,9 @@ use base::*; use type_of::*; use ast::def_id; use util::ppaux::ty_to_str; +use datum::*; +use callee::ArgVals; +use expr::SaveIn; enum reflector = { visitor_val: ValueRef, @@ -44,7 +47,7 @@ impl reflector { fn c_tydesc(t: ty::t) -> ValueRef { let bcx = self.bcx; let static_ti = get_tydesc(bcx.ccx(), t); - lazily_emit_all_tydesc_glue(bcx.ccx(), static_ti); + glue::lazily_emit_all_tydesc_glue(bcx.ccx(), static_ti); PointerCast(bcx, static_ti.tydesc, T_ptr(self.tydesc_ty)) } @@ -60,25 +63,21 @@ impl reflector { *self.visitor_methods)); let mth_ty = ty::mk_fn(tcx, self.visitor_methods[mth_idx].fty); let v = self.visitor_val; - let get_lval = |bcx| { - let callee = - impl::trans_trait_callee(bcx, v, mth_ty, mth_idx); - debug!("calling mth ty %s, lltype %s", - ty_to_str(bcx.ccx().tcx, mth_ty), - val_str(bcx.ccx().tn, callee.val)); - callee - }; debug!("passing %u args:", vec::len(args)); let bcx = self.bcx; for args.eachi |i, a| { debug!("arg %u: %s", i, val_str(bcx.ccx().tn, a)); } - let d = empty_dest_cell(); - let bcx = - trans_call_inner(self.bcx, None, mth_ty, ty::mk_bool(tcx), - get_lval, arg_vals(args), by_val(d)); + let bool_ty = ty::mk_bool(tcx); + let scratch = scratch_datum(bcx, bool_ty, false); + let bcx = callee::trans_call_inner( + self.bcx, None, mth_ty, bool_ty, + |bcx| impl::trans_trait_callee_from_llval(bcx, mth_ty, + mth_idx, v), + ArgVals(args), SaveIn(scratch.val)); + let result = scratch.to_value_llval(bcx); let next_bcx = sub_block(bcx, ~"next"); - CondBr(bcx, *d, next_bcx.llbb, self.final_bcx.llbb); + CondBr(bcx, result, next_bcx.llbb, self.final_bcx.llbb); self.bcx = next_bcx } diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs index 0473c62c99c17..33072f87db9ef 100644 --- a/src/rustc/middle/trans/tvec.rs +++ b/src/rustc/middle/trans/tvec.rs @@ -2,15 +2,15 @@ use syntax::ast; use driver::session::session; use lib::llvm::{ValueRef, TypeRef}; use back::abi; -use base::{call_memmove, - INIT, copy_val, load_if_immediate, get_tydesc, - sub_block, do_spill_noroot, - dest, non_gc_box_cast, move_val, lval_owned}; use syntax::codemap::span; use shape::llsize_of; use build::*; use common::*; use util::ppaux::ty_to_str; +use expr::{Dest, SaveIn, Ignore}; +use datum::*; +use syntax::print::pprust::{expr_to_str}; +use util::common::indenter; // Boxed vector types are in some sense currently a "shorthand" for a box // containing an unboxed vector. This expands a boxed vector type into such an @@ -43,11 +43,10 @@ fn get_alloc(bcx: block, vptr: ValueRef) -> ValueRef { } fn get_bodyptr(bcx: block, vptr: ValueRef) -> ValueRef { - non_gc_box_cast(bcx, GEPi(bcx, vptr, [0u, abi::box_field_body])) + base::non_gc_box_cast(bcx, GEPi(bcx, vptr, [0u, abi::box_field_body])) } -fn get_dataptr(bcx: block, vptr: ValueRef) - -> ValueRef { +fn get_dataptr(bcx: block, vptr: ValueRef) -> ValueRef { let _icx = bcx.insn_ctxt("tvec::get_dataptr"); GEPi(bcx, vptr, [0u, abi::vec_elt_elems, 0u]) } @@ -60,7 +59,7 @@ fn pointer_add(bcx: block, ptr: ValueRef, bytes: ValueRef) -> ValueRef { } fn alloc_raw(bcx: block, unit_ty: ty::t, - fill: ValueRef, alloc: ValueRef, heap: heap) -> result { + fill: ValueRef, alloc: ValueRef, heap: heap) -> Result { let _icx = bcx.insn_ctxt("tvec::alloc_uniq"); let ccx = bcx.ccx(); @@ -71,14 +70,14 @@ fn alloc_raw(bcx: block, unit_ty: ty::t, base::malloc_general_dyn(bcx, vecbodyty, heap, vecsize); Store(bcx, fill, GEPi(bcx, body, [0u, abi::vec_elt_fill])); Store(bcx, alloc, GEPi(bcx, body, [0u, abi::vec_elt_alloc])); - return {bcx: bcx, val: box}; + return rslt(bcx, box); } fn alloc_uniq_raw(bcx: block, unit_ty: ty::t, - fill: ValueRef, alloc: ValueRef) -> result { + fill: ValueRef, alloc: ValueRef) -> Result { alloc_raw(bcx, unit_ty, fill, alloc, heap_exchange) } -fn alloc_vec(bcx: block, unit_ty: ty::t, elts: uint, heap: heap) -> result { +fn alloc_vec(bcx: block, unit_ty: ty::t, elts: uint, heap: heap) -> Result { let _icx = bcx.insn_ctxt("tvec::alloc_uniq"); let ccx = bcx.ccx(); let llunitty = type_of::type_of(ccx, unit_ty); @@ -87,23 +86,24 @@ fn alloc_vec(bcx: block, unit_ty: ty::t, elts: uint, heap: heap) -> result { let fill = Mul(bcx, C_uint(ccx, elts), unit_sz); let alloc = if elts < 4u { Mul(bcx, C_int(ccx, 4), unit_sz) } else { fill }; - let {bcx: bcx, val: vptr} = alloc_raw(bcx, unit_ty, fill, alloc, heap); - return {bcx: bcx, val: vptr}; + let Result {bcx: bcx, val: vptr} = + alloc_raw(bcx, unit_ty, fill, alloc, heap); + return rslt(bcx, vptr); } -fn duplicate_uniq(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> result { +fn duplicate_uniq(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> Result { let _icx = bcx.insn_ctxt("tvec::duplicate_uniq"); let fill = get_fill(bcx, get_bodyptr(bcx, vptr)); let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); - let {bcx, val: newptr} = alloc_uniq_raw(bcx, unit_ty, fill, fill); + let Result {bcx, val: newptr} = alloc_uniq_raw(bcx, unit_ty, fill, fill); let data_ptr = get_dataptr(bcx, get_bodyptr(bcx, vptr)); let new_data_ptr = get_dataptr(bcx, get_bodyptr(bcx, newptr)); - call_memmove(bcx, new_data_ptr, data_ptr, fill); + base::call_memmove(bcx, new_data_ptr, data_ptr, fill); let bcx = if ty::type_needs_drop(bcx.tcx(), unit_ty) { - iter_vec_raw(bcx, new_data_ptr, vec_ty, fill, base::take_ty) + iter_vec_raw(bcx, new_data_ptr, vec_ty, fill, glue::take_ty) } else { bcx }; return rslt(bcx, newptr); } @@ -113,250 +113,349 @@ fn make_drop_glue_unboxed(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> let _icx = bcx.insn_ctxt("tvec::make_drop_glue_unboxed"); let tcx = bcx.tcx(), unit_ty = ty::sequence_element_type(tcx, vec_ty); if ty::type_needs_drop(tcx, unit_ty) { - iter_vec_unboxed(bcx, vptr, vec_ty, base::drop_ty) + iter_vec_unboxed(bcx, vptr, vec_ty, glue::drop_ty) } else { bcx } } -enum evec_elements { - individual_evec(~[@ast::expr]), - repeating_evec(@ast::expr, uint) +struct VecTypes { + vec_ty: ty::t; + unit_ty: ty::t; + llunit_ty: TypeRef; + llunit_size: ValueRef; } -fn trans_evec(bcx: block, elements: evec_elements, - vst: ast::vstore, id: ast::node_id, dest: dest) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_evec"); +impl VecTypes { + fn to_str(ccx: @crate_ctxt) -> ~str { + fmt!("VecTypes {vec_ty=%s, unit_ty=%s, llunit_ty=%s, llunit_size=%s}", + ty_to_str(ccx.tcx, self.vec_ty), + ty_to_str(ccx.tcx, self.unit_ty), + ty_str(ccx.tn, self.llunit_ty), + val_str(ccx.tn, self.llunit_size)) + } +} + +fn trans_fixed_vstore(bcx: block, + vstore_expr: @ast::expr, + content_expr: @ast::expr, + dest: expr::Dest) -> block +{ + //! + // + // [...]/_ allocates a fixed-size array and moves it around "by value". + // In this case, it means that the caller has already given us a location + // to store the array of the suitable size, so all we have to do is + // generate the content. + + debug!("trans_fixed_vstore(vstore_expr=%s, dest=%?)", + bcx.expr_to_str(vstore_expr), dest.to_str(bcx.ccx())); + let _indenter = indenter(); + + let vt = vec_types_from_expr(bcx, vstore_expr); + + return match dest { + Ignore => write_content(bcx, &vt, vstore_expr, content_expr, dest), + SaveIn(lldest) => { + // lldest will have type *[T x N], but we want the type *T, + // so use GEP to convert: + let lldest = GEPi(bcx, lldest, [0, 0]); + write_content(bcx, &vt, vstore_expr, content_expr, SaveIn(lldest)) + } + }; +} + +fn trans_slice_vstore(bcx: block, + vstore_expr: @ast::expr, + content_expr: @ast::expr, + dest: expr::Dest) -> block +{ + //! + // + // &[...] 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(); - let mut bcx = bcx; - // Handle the ignored case. - if dest == base::ignore { - match elements { - individual_evec(args) => { - for vec::each(args) |arg| { - bcx = base::trans_expr(bcx, arg, base::ignore); - } - } - repeating_evec(element, _) => { - bcx = base::trans_expr(bcx, element, base::ignore); - } + debug!("trans_slice_vstore(vstore_expr=%s, dest=%s)", + bcx.expr_to_str(vstore_expr), dest.to_str(ccx)); + let _indenter = indenter(); + + // Handle the &"..." case: + match content_expr.node { + ast::expr_lit(@{node: ast::lit_str(s), span: _}) => { + return trans_lit_str(bcx, content_expr, s, dest); } - return bcx; + _ => {} } - // Figure out the number of elements we need. - let count; - match elements { - individual_evec(args) => count = args.len(), - repeating_evec(_, len) => count = len + // Handle the &[...] case: + let vt = vec_types_from_expr(bcx, vstore_expr); + let count = elements_required(bcx, content_expr); + debug!("vt=%s, 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_evec(bcx.tcx(), + {ty: vt.unit_ty, mutbl: ast::m_mutbl}, + ty::vstore_fixed(count)); + let llfixed_ty = T_ptr(type_of::type_of(bcx.ccx(), fixed_ty)); + 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, + content_expr, SaveIn(llfixed)); + + // Finally, create the slice pair itself. + match dest { + Ignore => {} + SaveIn(lldest) => { + Store(bcx, llfixed, GEPi(bcx, lldest, [0u, abi::slice_elt_base])); + let lllen = Mul(bcx, llcount, vt.llunit_size); + Store(bcx, lllen, GEPi(bcx, lldest, [0u, abi::slice_elt_len])); + } } - let vec_ty = node_id_type(bcx, id); - let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); - let llunitty = type_of::type_of(ccx, unit_ty); - let unit_sz = llsize_of(ccx, llunitty); + return bcx; +} + +fn trans_lit_str(bcx: block, + lit_expr: @ast::expr, + lit_str: @~str, + dest: Dest) -> block +{ + //! + // + // 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=%s, dest=%s)", + bcx.expr_to_str(lit_expr), + dest.to_str(bcx.ccx())); + let _indenter = indenter(); + + match dest { + Ignore => bcx, + SaveIn(lldest) => { + let bytes = lit_str.len() + 1; // count null-terminator too + let llbytes = C_uint(bcx.ccx(), bytes); + let llcstr = C_cstr(bcx.ccx(), *lit_str); + let llcstr = llvm::LLVMConstPointerCast(llcstr, T_ptr(T_i8())); + Store(bcx, llcstr, GEPi(bcx, lldest, [0u, abi::slice_elt_base])); + Store(bcx, llbytes, GEPi(bcx, lldest, [0u, abi::slice_elt_len])); + bcx + } + } +} + + +fn trans_uniq_or_managed_vstore(bcx: block, + heap: heap, + vstore_expr: @ast::expr, + content_expr: @ast::expr) -> DatumBlock +{ + //! + // + // @[...] or ~[...] (also @"..." or ~"...") allocate boxes in the + // appropriate heap and write the array elements into them. + + debug!("trans_uniq_or_managed_vstore(vstore_expr=%s, heap=%?)", + bcx.expr_to_str(vstore_expr), heap); + let _indenter = indenter(); + + let vt = vec_types_from_expr(bcx, vstore_expr); + let count = elements_required(bcx, content_expr); + + let Result {bcx, val} = alloc_vec(bcx, vt.unit_ty, count, heap); + add_clean_free(bcx, val, heap); + let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val)); + + debug!("alloc_vec() returned val=%s, dataptr=%s", + bcx.val_str(val), bcx.val_str(dataptr)); + + let bcx = write_content(bcx, &vt, vstore_expr, + content_expr, SaveIn(dataptr)); + + revoke_clean(bcx, val); - let mut {bcx, val, dataptr} = - match vst { - ast::vstore_fixed(_) => { - // Destination should be pre-allocated for us. - let v = match dest { - base::save_in(v) => { - PointerCast(bcx, v, T_ptr(llunitty)) - } - _ => { - bcx.ccx().sess.bug(~"bad dest for vstore_fixed \ - in tvec::trans_evec"); - } - }; - {bcx: bcx, val: v, dataptr: v} - } - ast::vstore_slice(_) => { - // Make a fake type to use for the cleanup - let ty = ty::mk_evec(bcx.tcx(), - {ty: unit_ty, mutbl: ast::m_mutbl}, - ty::vstore_fixed(count)); - let llty = T_ptr(type_of::type_of(bcx.ccx(), ty)); - - let n = C_uint(ccx, count); - let vp = base::arrayalloca(bcx, llunitty, n); - // Cast to the fake type we told cleanup to expect. - let vp0 = BitCast(bcx, vp, llty); - add_clean(bcx, vp0, ty); - - let len = Mul(bcx, n, unit_sz); - - let p = base::alloca(bcx, T_struct(~[T_ptr(llunitty), - ccx.int_type])); - Store(bcx, vp, GEPi(bcx, p, [0u, abi::slice_elt_base])); - Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len])); - - {bcx: bcx, val: p, dataptr: vp} - } - ast::vstore_uniq => { - let {bcx, val} = alloc_vec(bcx, unit_ty, count, heap_exchange); - add_clean_free(bcx, val, heap_exchange); - let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val)); - {bcx: bcx, val: val, dataptr: dataptr} - } - ast::vstore_box => { - let {bcx, val} = alloc_vec(bcx, unit_ty, count, heap_shared); - add_clean_free(bcx, val, heap_shared); - let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val)); - {bcx: bcx, val: val, dataptr: dataptr} - } - }; - - - // Store the individual elements. - let mut i = 0u, temp_cleanups = ~[val]; - debug!("trans_evec: v: %s, dataptr: %s", - val_str(ccx.tn, val), - val_str(ccx.tn, dataptr)); - match elements { - individual_evec(args) => { - for vec::each(args) |e| { - let lleltptr = InBoundsGEP(bcx, dataptr, ~[C_uint(ccx, i)]); - bcx = base::trans_expr_save_in(bcx, e, lleltptr); - add_clean_temp_mem(bcx, lleltptr, unit_ty); - vec::push(temp_cleanups, lleltptr); - i += 1u; + return immediate_rvalue_bcx(bcx, val, vt.vec_ty); +} + +fn write_content(bcx: block, + vt: &VecTypes, + vstore_expr: @ast::expr, + content_expr: @ast::expr, + dest: Dest) -> block +{ + let _icx = bcx.insn_ctxt("tvec::write_content"); + let mut bcx = bcx; + + debug!("write_content(vt=%s, dest=%s, 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::expr_lit(@{node: ast::lit_str(s), span: _}) => { + match dest { + Ignore => { + return bcx; + } + SaveIn(lldest) => { + let bytes = s.len() + 1; // copy null-terminator too + let llbytes = C_uint(bcx.ccx(), bytes); + let llcstr = C_cstr(bcx.ccx(), *s); + base::call_memmove(bcx, lldest, llcstr, llbytes); + return bcx; + } } } - repeating_evec(e, len) => { - // We make temporary space in the hope that this will be - // friendlier to LLVM alias analysis. - let lltmpspace = base::alloca(bcx, llunitty); - bcx = base::trans_expr_save_in(bcx, e, lltmpspace); - add_clean_temp_mem(bcx, lltmpspace, unit_ty); - vec::push(temp_cleanups, lltmpspace); - for len.timesi |i| { - let lleltptr = InBoundsGEP(bcx, dataptr, ~[C_uint(ccx, i)]); - if i == len - 1 { - // Move the last one in. - bcx = move_val(bcx, INIT, lleltptr, - lval_owned(bcx, lltmpspace), unit_ty); - } else { - // Copy all but the last one in. - let llval = load_if_immediate(bcx, lltmpspace, unit_ty); - bcx = copy_val(bcx, INIT, lleltptr, llval, unit_ty); + ast::expr_vec(elements, _) => { + match dest { + Ignore => { + for elements.each |element| { + bcx = expr::trans_into(bcx, element, Ignore); + } + } + + SaveIn(lldest) => { + let mut temp_cleanups = ~[]; + for elements.eachi |i, element| { + let lleltptr = GEPi(bcx, lldest, [i]); + debug!("writing index %? with lleltptr=%?", + i, bcx.val_str(lleltptr)); + bcx = expr::trans_into(bcx, element, + SaveIn(lleltptr)); + add_clean_temp_mem(bcx, lleltptr, vt.unit_ty); + vec::push(temp_cleanups, lleltptr); + } + for vec::each(temp_cleanups) |cleanup| { + revoke_clean(bcx, cleanup); + } } - add_clean_temp_mem(bcx, lleltptr, unit_ty); - vec::push(temp_cleanups, lleltptr); } + return bcx; + } + ast::expr_repeat(element, count_expr, _) => { + match dest { + Ignore => { + return expr::trans_into(bcx, element, Ignore); + } + SaveIn(lldest) => { + let count = ty::eval_repeat_count(bcx.tcx(), count_expr, + count_expr.span); + if count == 0 { + return bcx; + } + + let tmpdatum = unpack_datum!(bcx, { + expr::trans_to_datum(bcx, element) + }); + + let mut temp_cleanups = ~[]; + + for uint::range(0, count) |i| { + let lleltptr = GEPi(bcx, lldest, [i]); + if i < count - 1 { + // Copy all but the last one in. + bcx = tmpdatum.copy_to(bcx, INIT, lleltptr); + } else { + // Move the last one in. + bcx = tmpdatum.move_to(bcx, INIT, lleltptr); + } + add_clean_temp_mem(bcx, lleltptr, vt.unit_ty); + vec::push(temp_cleanups, lleltptr); + } + + for vec::each(temp_cleanups) |cleanup| { + revoke_clean(bcx, cleanup); + } + + return bcx; + } + } + } + _ => { + bcx.tcx().sess.span_bug(content_expr.span, + ~"Unexpected evec content"); } } +} - for vec::each(temp_cleanups) |cln| { revoke_clean(bcx, cln); } +fn vec_types_from_expr(bcx: block, vec_expr: @ast::expr) -> VecTypes { + let vec_ty = node_id_type(bcx, vec_expr.id); + vec_types(bcx, vec_ty) +} - match vst { - ast::vstore_fixed(_) => { - // We wrote into the destination in the fixed case. - return bcx; - } - ast::vstore_slice(_) => { - return base::store_in_dest(bcx, Load(bcx, val), dest); - } - _ => { - return base::store_in_dest(bcx, val, dest); - } - } +fn vec_types(bcx: block, vec_ty: ty::t) -> VecTypes { + let ccx = bcx.ccx(); + let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); + let llunit_ty = type_of::type_of(ccx, unit_ty); + let llunit_size = llsize_of(ccx, llunit_ty); + VecTypes {vec_ty: vec_ty, + unit_ty: unit_ty, + llunit_ty: llunit_ty, + llunit_size: llunit_size} } -fn trans_vstore(bcx: block, e: @ast::expr, - v: ast::vstore, dest: dest) -> block { - match e.node { - ast::expr_lit(@{node: ast::lit_str(s), span: _}) => { - return trans_estr(bcx, s, Some(v), dest); - } - ast::expr_vec(es, _) => { - return trans_evec(bcx, individual_evec(es), v, e.id, dest); - } - ast::expr_repeat(element, count_expr, _) => { - let count = ty::eval_repeat_count(bcx.tcx(), count_expr, e.span); - return trans_evec(bcx, repeating_evec(element, count), v, e.id, dest); - } - _ => { - bcx.sess().span_bug(e.span, ~"vstore on non-sequence type"); - } +fn elements_required(bcx: block, content_expr: @ast::expr) -> uint { + //! Figure out the number of elements we need to store this content + + match content_expr.node { + ast::expr_lit(@{node: ast::lit_str(s), span: _}) => s.len() + 1, + ast::expr_vec(es, _) => es.len(), + ast::expr_repeat(_, count_expr, _) => { + ty::eval_repeat_count(bcx.tcx(), count_expr, content_expr.span) + } + _ => bcx.tcx().sess.span_bug(content_expr.span, + ~"Unexpected evec content") } } -fn get_base_and_len(cx: block, v: ValueRef, e_ty: ty::t) - -> (ValueRef, ValueRef) { +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. - let ccx = cx.ccx(); - let tcx = ccx.tcx; - let vec_ty = ty::type_autoderef(tcx, e_ty); - let unit_ty = ty::sequence_element_type(tcx, vec_ty); - let llunitty = type_of::type_of(ccx, unit_ty); - let unit_sz = llsize_of(ccx, llunitty); + let ccx = bcx.ccx(); + let vt = vec_types(bcx, vec_ty); - let vstore = match ty::get(vec_ty).struct { + let vstore = match ty::get(vt.vec_ty).struct { ty::ty_estr(vst) | ty::ty_evec(_, vst) => vst, _ => ty::vstore_uniq }; match vstore { - ty::vstore_fixed(n) => { - let base = GEPi(cx, v, [0u, 0u]); - let n = if ty::type_is_str(e_ty) { n + 1u } else { n }; - let len = Mul(cx, C_uint(ccx, n), unit_sz); - (base, len) - } - ty::vstore_slice(_) => { - let base = Load(cx, GEPi(cx, v, [0u, abi::slice_elt_base])); - let len = Load(cx, GEPi(cx, v, [0u, abi::slice_elt_len])); - (base, len) - } - ty::vstore_uniq | ty::vstore_box => { - debug!("get_base_and_len: %s", val_str(ccx.tn, v)); - let body = tvec::get_bodyptr(cx, v); - (tvec::get_dataptr(cx, body), tvec::get_fill(cx, body)) - } + ty::vstore_fixed(n) => { + let base = GEPi(bcx, llval, [0u, 0u]); + let n = if ty::type_is_str(vec_ty) { n + 1u } else { n }; + let len = Mul(bcx, C_uint(ccx, n), vt.llunit_size); + (base, len) + } + ty::vstore_slice(_) => { + let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base])); + let len = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len])); + (base, len) + } + ty::vstore_uniq | ty::vstore_box => { + let body = tvec::get_bodyptr(bcx, llval); + (tvec::get_dataptr(bcx, body), tvec::get_fill(bcx, body)) + } } } -fn trans_estr(bcx: block, s: @~str, vstore: Option, - dest: dest) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_estr"); - if dest == base::ignore { return bcx; } - let ccx = bcx.ccx(); - - let c = match vstore { - Some(ast::vstore_fixed(_)) => { - // "hello"/_ => "hello"/5 => ~[i8 x 6] in llvm - debug!("trans_estr: fixed: %s", *s); - C_postr(*s) - } - - Some(ast::vstore_slice(_)) | None => { - // "hello" => (*i8, 6u) in llvm - debug!("trans_estr: slice '%s'", *s); - C_estr_slice(ccx, *s) - } - - Some(ast::vstore_uniq) => { - let cs = PointerCast(bcx, C_cstr(ccx, *s), T_ptr(T_i8())); - let len = C_uint(ccx, str::len(*s)); - let c = Call(bcx, ccx.upcalls.str_new_uniq, ~[cs, len]); - PointerCast(bcx, c, - T_unique_ptr(T_unique(ccx, T_vec(ccx, T_i8())))) - } - - Some(ast::vstore_box) => { - let cs = PointerCast(bcx, C_cstr(ccx, *s), T_ptr(T_i8())); - let len = C_uint(ccx, str::len(*s)); - let c = Call(bcx, ccx.upcalls.str_new_shared, ~[cs, len]); - PointerCast(bcx, c, - T_box_ptr(T_box(ccx, T_vec(ccx, T_i8())))) - } - }; - - debug!("trans_estr: type: %s", val_str(ccx.tn, c)); - base::store_in_dest(bcx, c, dest) -} - -type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> result; +type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> Result; type iter_vec_block = fn(block, ValueRef, ty::t) -> block; @@ -373,21 +472,21 @@ fn iter_vec_raw(bcx: block, data_ptr: ValueRef, vec_ty: ty::t, let data_end_ptr = pointer_add(bcx, data_ptr, fill); // Now perform the iteration. - let header_cx = sub_block(bcx, ~"iter_vec_loop_header"); - Br(bcx, header_cx.llbb); + let header_bcx = base::sub_block(bcx, ~"iter_vec_loop_header"); + Br(bcx, header_bcx.llbb); let data_ptr = - Phi(header_cx, val_ty(data_ptr), ~[data_ptr], ~[bcx.llbb]); + Phi(header_bcx, val_ty(data_ptr), ~[data_ptr], ~[bcx.llbb]); let not_yet_at_end = - ICmp(header_cx, lib::llvm::IntULT, data_ptr, data_end_ptr); - let body_cx = sub_block(header_cx, ~"iter_vec_loop_body"); - let next_cx = sub_block(header_cx, ~"iter_vec_next"); - CondBr(header_cx, not_yet_at_end, body_cx.llbb, next_cx.llbb); - let body_cx = f(body_cx, data_ptr, unit_ty); - AddIncomingToPhi(data_ptr, InBoundsGEP(body_cx, data_ptr, + 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"); + CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb); + let body_bcx = f(body_bcx, data_ptr, unit_ty); + AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr, ~[C_int(bcx.ccx(), 1)]), - body_cx.llbb); - Br(body_cx, header_cx.llbb); - return next_cx; + body_bcx.llbb); + Br(body_bcx, header_bcx.llbb); + return next_bcx; } diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs index 2a4322eb2150a..40bf644c6266f 100644 --- a/src/rustc/middle/trans/type_of.rs +++ b/src/rustc/middle/trans/type_of.rs @@ -7,6 +7,7 @@ use std::map::hashmap; export type_of; export type_of_dtor; +export type_of_explicit_arg; export type_of_explicit_args; export type_of_fn_from_ty; export type_of_fn; @@ -14,18 +15,19 @@ export type_of_glue_fn; export type_of_non_gc_box; export type_of_rooted; -fn type_of_explicit_args(cx: @crate_ctxt, - inputs: ~[ty::arg]) -> ~[TypeRef] { - do vec::map(inputs) |arg| { - let arg_ty = arg.ty; - let llty = type_of(cx, arg_ty); - match ty::resolved_mode(cx.tcx, arg.mode) { - ast::by_val => llty, - _ => T_ptr(llty) - } +fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef { + let arg_ty = arg.ty; + let llty = type_of(ccx, arg_ty); + match ty::resolved_mode(ccx.tcx, arg.mode) { + ast::by_val => llty, + _ => T_ptr(llty) } } +fn type_of_explicit_args(ccx: @crate_ctxt, inputs: ~[ty::arg]) -> ~[TypeRef] { + inputs.map(|arg| type_of_explicit_arg(ccx, arg)) +} + fn type_of_fn(cx: @crate_ctxt, inputs: ~[ty::arg], output: ty::t) -> TypeRef { let mut atys: ~[TypeRef] = ~[]; @@ -145,7 +147,10 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { let mt_ty = f.mt.ty; vec::push(tys, type_of(cx, mt_ty)); } - T_struct(tys) + + // n.b.: introduce an extra layer of indirection to match + // structs + T_struct(~[T_struct(tys)]) } ty::ty_fn(_) => T_fn_pair(cx, type_of_fn_from_ty(cx, t)), ty::ty_trait(_, _, _) => T_opaque_trait(cx), @@ -188,12 +193,13 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { type_of(cx, t) }; + // include a byte flag if there is a dtor so that we know when we've + // been dropped if ty::ty_dtor(cx.tcx, did) != None { - // resource type - tys = ~[T_i8(), T_struct(tys)]; + common::set_struct_body(llty, ~[T_struct(tys), T_i8()]); + } else { + common::set_struct_body(llty, ~[T_struct(tys)]); } - - common::set_struct_body(llty, tys); } _ => () } diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs index 10b7992c0f217..8048a34c219d1 100644 --- a/src/rustc/middle/trans/type_use.rs +++ b/src/rustc/middle/trans/type_use.rs @@ -40,7 +40,7 @@ fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint) None => () } let fn_id_loc = if fn_id.crate == local_crate { fn_id } - else { base::maybe_instantiate_inline(ccx, fn_id) }; + else { inline::maybe_instantiate_inline(ccx, fn_id) }; // Conservatively assume full use for recursive loops ccx.type_use_cache.insert(fn_id, vec::from_elem(n_tps, 3u)); @@ -82,25 +82,23 @@ fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint) abi, _) => { if abi == foreign_abi_rust_intrinsic { let flags = match cx.ccx.sess.str_of(i.ident) { - ~"size_of" | ~"pref_align_of" | ~"min_align_of" | - ~"init" | ~"reinterpret_cast" | - ~"move_val" | ~"move_val_init" => { - use_repr - } - ~"get_tydesc" | ~"needs_drop" => { - use_tydesc - } - ~"atomic_xchg" | ~"atomic_xadd" | ~"atomic_xsub" | - ~"atomic_xchg_acq" | ~"atomic_xadd_acq" | ~"atomic_xsub_acq" | - ~"atomic_xchg_rel" | ~"atomic_xadd_rel" | ~"atomic_xsub_rel" => - { 0u } - ~"visit_tydesc" | ~"forget" | ~"addr_of" | - ~"frame_address" | ~"morestack_addr" => { - 0u - } - // would be cool to make these an enum instead of strings! - _ => fail fmt!("unknown intrinsic in type_use %?", - cx.ccx.sess.str_of(i.ident)) + ~"size_of" | ~"pref_align_of" | ~"min_align_of" | + ~"init" | ~"reinterpret_cast" | + ~"move_val" | ~"move_val_init" => use_repr, + + ~"get_tydesc" | ~"needs_drop" => use_tydesc, + + ~"atomic_xchg" | ~"atomic_xadd" | + ~"atomic_xsub" | ~"atomic_xchg_acq" | + ~"atomic_xadd_acq" | ~"atomic_xsub_acq" | + ~"atomic_xchg_rel" | ~"atomic_xadd_rel" | + ~"atomic_xsub_rel" => 0, + + ~"visit_tydesc" | ~"forget" | ~"addr_of" | + ~"frame_address" | ~"morestack_addr" => 0, + + // would be cool to make these an enum instead of strings! + _ => fail ~"unknown intrinsic in type_use" }; for uint::range(0u, n_tps) |n| { cx.uses[n] |= flags;} } @@ -288,7 +286,7 @@ fn handle_body(cx: ctx, body: blk) { } }, visit_item: |_i, _cx, _v| { }, - .. *visit::default_visitor() + ..*visit::default_visitor() }); v.visit_block(body, cx, v); } diff --git a/src/rustc/middle/trans/uniq.rs b/src/rustc/middle/trans/uniq.rs index cd6710f274c95..6ab91c4a1d797 100644 --- a/src/rustc/middle/trans/uniq.rs +++ b/src/rustc/middle/trans/uniq.rs @@ -4,52 +4,41 @@ use common::*; use build::*; use base::*; use shape::llsize_of; +use datum::immediate_rvalue; export make_free_glue, autoderef, duplicate; -fn make_free_glue(bcx: block, vptrptr: ValueRef, t: ty::t) +fn make_free_glue(bcx: block, vptrptr: ValueRef, box_ty: ty::t) -> block { let _icx = bcx.insn_ctxt("uniq::make_free_glue"); - let vptr = Load(bcx, vptrptr); - do with_cond(bcx, IsNotNull(bcx, vptr)) |bcx| { - let content_ty = content_ty(t); - let body_ptr = opaque_box_body(bcx, content_ty, vptr); - let bcx = drop_ty(bcx, body_ptr, content_ty); - trans_unique_free(bcx, vptr) + let box_datum = immediate_rvalue(Load(bcx, vptrptr), box_ty); + + let not_null = IsNotNull(bcx, box_datum.val); + do with_cond(bcx, not_null) |bcx| { + let body_datum = box_datum.box_body(bcx); + let bcx = glue::drop_ty(bcx, body_datum.to_ref_llval(bcx), + body_datum.ty); + glue::trans_unique_free(bcx, box_datum.val) } } -fn content_ty(t: ty::t) -> ty::t { - match ty::get(t).struct { - ty::ty_uniq({ty: ct, _}) => ct, - _ => core::unreachable() - } -} +fn duplicate(bcx: block, src_box: ValueRef, src_ty: ty::t) -> Result { + let _icx = bcx.insn_ctxt("uniq::duplicate"); -fn autoderef(bcx: block, v: ValueRef, t: ty::t) -> {v: ValueRef, t: ty::t} { - let content_ty = content_ty(t); - let v = opaque_box_body(bcx, content_ty, v); - return {v: v, t: content_ty}; -} + // Load the body of the source (*src) + let src_datum = immediate_rvalue(src_box, src_ty); + let body_datum = src_datum.box_body(bcx); -fn duplicate(bcx: block, v: ValueRef, t: ty::t) -> result { - let _icx = bcx.insn_ctxt("uniq::duplicate"); - let content_ty = content_ty(t); + // Malloc space in exchange heap and copy src into it let {bcx: bcx, box: dst_box, body: dst_body} = - malloc_unique(bcx, content_ty); - - let src_box = v; - let src_body = opaque_box_body(bcx, content_ty, src_box); - let src_body = load_if_immediate(bcx, src_body, content_ty); - debug!("ST: %?", val_str(bcx.ccx().tn, src_body)); - debug!("DT: %?", val_str(bcx.ccx().tn, dst_body)); - let bcx = copy_val(bcx, INIT, dst_body, src_body, content_ty); + malloc_unique(bcx, body_datum.ty); + body_datum.copy_to(bcx, datum::INIT, dst_body); + // Copy the type descriptor let src_tydesc_ptr = GEPi(bcx, src_box, [0u, back::abi::box_field_tydesc]); let dst_tydesc_ptr = GEPi(bcx, dst_box, [0u, back::abi::box_field_tydesc]); - let td = Load(bcx, src_tydesc_ptr); Store(bcx, td, dst_tydesc_ptr); diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 5debbf6b00664..85ef0797437ea 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -11,7 +11,8 @@ use syntax::ast_util; use syntax::ast_util::{is_local, local_def, new_def_hash}; use syntax::codemap::span; use metadata::csearch; -use util::ppaux::{region_to_str, explain_region, vstore_to_str}; +use util::ppaux::{region_to_str, explain_region, vstore_to_str, + note_and_explain_region}; use middle::lint; use middle::lint::{get_lint_level, allow}; use syntax::ast::*; @@ -36,12 +37,13 @@ export def_has_ty_params; export expr_has_ty_params; export expr_ty; export expr_ty_params_and_ty; -export expr_is_lval; +export expr_is_lval, expr_kind; +export ExprKind, LvalueExpr, RvalueDatumExpr, RvalueDpsExpr, RvalueStmtExpr; export field_ty; export fold_ty, fold_sty_to_ty, fold_region, fold_regions; export fold_regions_and_ty, walk_regions_and_ty; export field; -export field_idx; +export field_idx, field_idx_strict; export get_field; export get_fields; export get_element_type; @@ -120,7 +122,7 @@ export kind_is_owned; export proto_kind, kind_lteq, type_kind; export operators; export type_err, terr_vstore_kind; -export type_err_to_str; +export type_err_to_str, note_and_explain_type_err; export expected_found; export type_needs_drop; export type_is_empty; @@ -2382,7 +2384,7 @@ fn node_id_to_type(cx: ctxt, id: ast::node_id) -> t { match smallintmap::find(*cx.node_types, id as uint) { Some(t) => t, None => cx.sess.bug( - fmt!("node_id_to_type: unbound node ID %s", + fmt!("node_id_to_type: no type for node `%s`", ast_map::node_id_to_str(cx.items, id, cx.sess.parse_sess.interner))) } @@ -2535,13 +2537,156 @@ fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map, } } -fn expr_is_lval(method_map: typeck::method_map, e: @ast::expr) -> bool { - match e.node { - ast::expr_path(_) | ast::expr_unary(ast::deref, _) => true, - ast::expr_field(_, _, _) | ast::expr_index(_, _) => { - !method_map.contains_key(e.id) - } - _ => false +fn resolve_expr(tcx: ctxt, expr: @ast::expr) -> ast::def { + match tcx.def_map.find(expr.id) { + Some(def) => def, + None => { + tcx.sess.span_bug(expr.span, fmt!( + "No def-map entry for expr %?", expr.id)); + } + } +} + +fn expr_is_lval(tcx: ctxt, + method_map: typeck::method_map, + e: @ast::expr) -> bool { + match expr_kind(tcx, method_map, e) { + LvalueExpr => true, + RvalueDpsExpr | RvalueDatumExpr | RvalueStmtExpr => false + } +} + +/// We categorize expressions into three kinds. The distinction between +/// lvalue/rvalue is fundamental to the language. The distinction between the +/// two kinds of rvalues is an artifact of trans which reflects how we will +/// generate code for that kind of expression. See trans/expr.rs for more +/// information. +enum ExprKind { + LvalueExpr, + RvalueDpsExpr, + RvalueDatumExpr, + RvalueStmtExpr +} + +fn expr_kind(tcx: ctxt, + method_map: typeck::method_map, + expr: @ast::expr) -> ExprKind { + if method_map.contains_key(expr.id) { + // Overloaded operations are generally calls, and hence they are + // generated via DPS. However, assign_op (e.g., `x += y`) is an + // exception, as its result is always unit. + return match expr.node { + ast::expr_assign_op(*) => RvalueStmtExpr, + _ => RvalueDpsExpr + }; + } + + match expr.node { + ast::expr_path(*) => { + match resolve_expr(tcx, expr) { + ast::def_fn(*) | ast::def_static_method(*) | + ast::def_variant(*) => RvalueDpsExpr, + + // Note: there is actually a good case to be made that + // def_args, particularly those of immediate type, ought to + // considered rvalues. + ast::def_const(*) | + ast::def_binding(*) | + ast::def_upvar(*) | + ast::def_arg(*) | + ast::def_local(*) | + ast::def_self(*) => LvalueExpr, + + move def => { + tcx.sess.span_bug(expr.span, fmt!( + "Uncategorized def for expr %?: %?", + expr.id, def)); + } + } + } + + ast::expr_unary(ast::deref, _) | + ast::expr_field(*) | + ast::expr_index(*) => { + LvalueExpr + } + + ast::expr_call(*) | + ast::expr_rec(*) | + ast::expr_struct(*) | + ast::expr_tup(*) | + ast::expr_if(*) | + ast::expr_match(*) | + ast::expr_fn(*) | + ast::expr_fn_block(*) | + ast::expr_loop_body(*) | + ast::expr_do_body(*) | + ast::expr_block(*) | + ast::expr_copy(*) | + ast::expr_unary_move(*) | + ast::expr_repeat(*) | + ast::expr_lit(@{node: lit_str(_), _}) | + ast::expr_vstore(_, ast::vstore_slice(_)) | + ast::expr_vstore(_, ast::vstore_fixed(_)) | + ast::expr_vec(*) => { + RvalueDpsExpr + } + + ast::expr_cast(*) => { + match smallintmap::find(*tcx.node_types, expr.id as uint) { + Some(t) => { + if ty::type_is_immediate(t) { + RvalueDatumExpr + } else { + RvalueDpsExpr + } + } + None => { + // Technically, it should not happen that the expr is not + // present within the table. However, it DOES happen + // during type check, because the final types from the + // expressions are not yet recorded in the tcx. At that + // time, though, we are only interested in knowing lvalue + // vs rvalue. It would be better to base this decision on + // the AST type in cast node---but (at the time of this + // writing) it's not easy to distinguish casts to traits + // from other casts based on the AST. This should be + // easier in the future, when casts to traits would like + // like @Foo, ~Foo, or &Foo. + RvalueDatumExpr + } + } + } + + ast::expr_break(*) | + ast::expr_again(*) | + ast::expr_ret(*) | + ast::expr_log(*) | + ast::expr_fail(*) | + ast::expr_assert(*) | + ast::expr_while(*) | + ast::expr_loop(*) | + ast::expr_assign(*) | + ast::expr_move(*) | + ast::expr_swap(*) | + ast::expr_assign_op(*) => { + RvalueStmtExpr + } + + ast::expr_lit(_) | // Note: lit_str is carved out above + ast::expr_unary(*) | + ast::expr_addr_of(*) | + ast::expr_binary(*) | + ast::expr_vstore(_, ast::vstore_box) | + ast::expr_vstore(_, ast::vstore_uniq) => { + RvalueDatumExpr + } + + ast::expr_mac(*) => { + tcx.sess.span_bug( + expr.span, + ~"macro expression remains after expansion"); + } } } @@ -2553,12 +2698,21 @@ fn stmt_node_id(s: @ast::stmt) -> ast::node_id { } } -fn field_idx(id: ast::ident, fields: ~[field]) -> Option { +fn field_idx(id: ast::ident, fields: &[field]) -> Option { let mut i = 0u; for fields.each |f| { if f.ident == id { return Some(i); } i += 1u; } return None; } +fn field_idx_strict(tcx: ty::ctxt, id: ast::ident, fields: &[field]) -> uint { + let mut i = 0u; + for fields.each |f| { if f.ident == id { return i; } i += 1u; } + tcx.sess.bug(fmt!( + "No field named `%s` found in the list of fields `%?`", + tcx.sess.str_of(id), + fields.map(|f| tcx.sess.str_of(f.ident)))); +} + fn get_field(tcx: ctxt, rec_ty: t, id: ast::ident) -> field { match vec::find(get_fields(rec_ty), |f| f.ident == id) { Some(f) => f, @@ -2730,6 +2884,15 @@ fn ty_sort_str(cx: ctxt, t: t) -> ~str { } fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { + /*! + * + * Explains the source of a type err in a short, + * human readable way. This is meant to be placed in + * parentheses after some larger message. You should + * also invoke `note_and_explain_type_err()` afterwards + * to present additional details, particularly when + * it comes to lifetime-related errors. */ + fn terr_vstore_kind_to_str(k: terr_vstore_kind) -> ~str { match k { terr_vec => ~"[]", @@ -2795,20 +2958,14 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { fmt!("expected argument mode %s, but found %s", mode_to_str(values.expected), mode_to_str(values.found)) } - terr_regions_does_not_outlive(subregion, superregion) => { - fmt!("%s does not necessarily outlive %s", - explain_region(cx, superregion), - explain_region(cx, subregion)) + terr_regions_does_not_outlive(*) => { + fmt!("lifetime mismatch") } - terr_regions_not_same(region1, region2) => { - fmt!("%s is not the same as %s", - explain_region(cx, region1), - explain_region(cx, region2)) + terr_regions_not_same(*) => { + fmt!("lifetimes are not the same") } - terr_regions_no_overlap(region1, region2) => { - fmt!("%s does not intersect %s", - explain_region(cx, region1), - explain_region(cx, region2)) + terr_regions_no_overlap(*) => { + fmt!("lifetimes do not intersect") } terr_vstores_differ(k, values) => { fmt!("%s storage differs: expected %s but found %s", @@ -2835,6 +2992,27 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { } } +fn note_and_explain_type_err(cx: ctxt, err: &type_err) { + match *err { + terr_regions_does_not_outlive(subregion, superregion) => { + note_and_explain_region(cx, ~"", subregion, ~"..."); + note_and_explain_region(cx, ~"...does not necessarily outlive ", + superregion, ~""); + } + terr_regions_not_same(region1, region2) => { + note_and_explain_region(cx, ~"", region1, ~"..."); + note_and_explain_region(cx, ~"...is not the same lifetime as ", + region2, ~""); + } + terr_regions_no_overlap(region1, region2) => { + note_and_explain_region(cx, ~"", region1, ~"..."); + note_and_explain_region(cx, ~"...does not overlap ", + region2, ~""); + } + _ => {} + } +} + fn def_has_ty_params(def: ast::def) -> bool { match def { ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_, _) diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 870120547794c..3f4ec2616988c 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -225,12 +225,13 @@ fn require_same_types( } match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) { - result::Ok(()) => true, - result::Err(ref terr) => { - l_tcx.sess.span_err(span, msg() + ~": " + - ty::type_err_to_str(l_tcx, terr)); - false - } + result::Ok(()) => true, + result::Err(ref terr) => { + l_tcx.sess.span_err(span, msg() + ~": " + + ty::type_err_to_str(l_tcx, terr)); + ty::note_and_explain_type_err(l_tcx, terr); + false + } } } diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index ef3b22909831f..d74bf4f208ef9 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -667,6 +667,7 @@ impl @fn_ctxt { self.infcx().ty_to_str(e), self.infcx().ty_to_str(a), ty::type_err_to_str(self.ccx.tcx, err))); + ty::note_and_explain_type_err(self.ccx.tcx, err); } fn mk_subty(a_is_expected: bool, span: span, @@ -2049,29 +2050,36 @@ fn check_decl_initializer(fcx: @fn_ctxt, nid: ast::node_id, fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool { let mut bot = false; + let tcx = fcx.ccx.tcx; - let t = ty::mk_var(fcx.ccx.tcx, fcx.inh.locals.get(local.node.id)); + let t = ty::mk_var(tcx, fcx.inh.locals.get(local.node.id)); fcx.write_ty(local.node.id, t); + + let is_lvalue; match local.node.init { - Some(init) => { - bot = check_decl_initializer(fcx, local.node.id, init); - } - _ => {/* fall through */ } + Some(init) => { + bot = check_decl_initializer(fcx, local.node.id, init); + is_lvalue = ty::expr_is_lval(tcx, fcx.ccx.method_map, init.expr); + } + _ => { + is_lvalue = true; + } } let region = - ty::re_scope(fcx.ccx.tcx.region_map.get(local.node.id)); + ty::re_scope(tcx.region_map.get(local.node.id)); let pcx = { fcx: fcx, - map: pat_id_map(fcx.ccx.tcx.def_map, local.node.pat), + map: pat_id_map(tcx.def_map, local.node.pat), alt_region: region, block_region: region, - pat_region: region, - matching_lvalue: true, // FIXME(#3235) Make this more flexible - has_guard: false, - mut ever_bound_by_ref: false, }; alt::check_pat(pcx, local.node.pat, t); + let has_guard = false; + alt::check_legality_of_move_bindings(fcx, + is_lvalue, + has_guard, + [local.node.pat]); return bot; } diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs index 31202da4500ec..0282a9d78c4df 100644 --- a/src/rustc/middle/typeck/check/alt.rs +++ b/src/rustc/middle/typeck/check/alt.rs @@ -1,4 +1,6 @@ use syntax::print::pprust; +use syntax::ast_util::{walk_pat}; +use pat_util::{pat_is_variant}; fn check_alt(fcx: @fn_ctxt, expr: @ast::expr, @@ -9,7 +11,7 @@ fn check_alt(fcx: @fn_ctxt, let pattern_ty = fcx.infcx().next_ty_var(); bot = check_expr_with(fcx, discrim, pattern_ty); - let is_lvalue = ty::expr_is_lval(fcx.ccx.method_map, discrim); + let is_lvalue = ty::expr_is_lval(tcx, fcx.ccx.method_map, discrim); // Typecheck the patterns first, so that we get types for all the // bindings. @@ -18,17 +20,16 @@ fn check_alt(fcx: @fn_ctxt, fcx: fcx, map: pat_id_map(tcx.def_map, arm.pats[0]), alt_region: ty::re_scope(expr.id), - block_region: ty::re_scope(arm.body.node.id), - pat_region: ty::re_scope(expr.id), - // The following three fields determine whether 'move' is allowed. - matching_lvalue: is_lvalue, - has_guard: arm.guard.is_some(), - // Each arm is freshly allowed to decide whether it can 'move'. - mut ever_bound_by_ref: false, + block_region: ty::re_scope(arm.body.node.id) }; for arm.pats.each |p| { check_pat(pcx, p, pattern_ty);} + check_legality_of_move_bindings(fcx, + is_lvalue, + arm.guard.is_some(), + arm.pats); } + // Now typecheck the blocks. let mut result_ty = fcx.infcx().next_ty_var(); let mut arm_non_bot = false; @@ -47,20 +48,72 @@ fn check_alt(fcx: @fn_ctxt, return bot; } +fn check_legality_of_move_bindings(fcx: @fn_ctxt, + is_lvalue: bool, + has_guard: bool, + pats: &[@ast::pat]) +{ + let tcx = fcx.tcx(); + let def_map = tcx.def_map; + let mut by_ref = None; + let mut any_by_move = false; + for pats.each |pat| { + do pat_util::pat_bindings(def_map, pat) |bm, _id, span, _path| { + match bm { + ast::bind_by_ref(_) | ast::bind_by_implicit_ref => { + by_ref = Some(span); + } + ast::bind_by_move => { + any_by_move = true; + } + _ => { } + } + } + } + + if !any_by_move { return; } // pointless micro-optimization + for pats.each |pat| { + do walk_pat(pat) |p| { + if !pat_is_variant(def_map, p) { + match p.node { + ast::pat_ident(ast::bind_by_move, _, sub) => { + // check legality of moving out of the enum + if sub.is_some() { + tcx.sess.span_err( + p.span, + ~"cannot bind by-move with sub-bindings"); + } else if has_guard { + tcx.sess.span_err( + p.span, + ~"cannot bind by-move into a pattern guard"); + } else if by_ref.is_some() { + tcx.sess.span_err( + p.span, + ~"cannot bind by-move and by-ref \ + in the same pattern"); + tcx.sess.span_note( + by_ref.get(), + ~"by-ref binding occurs here"); + } else if is_lvalue { + tcx.sess.span_err( + p.span, + ~"cannot bind by-move when \ + matching an lvalue"); + } + } + _ => {} + } + } + } + } +} + + type pat_ctxt = { fcx: @fn_ctxt, map: pat_id_map, - alt_region: ty::region, - block_region: ty::region, - /* Equal to either alt_region or block_region. */ - pat_region: ty::region, - /* Moving out is only permitted when matching rvalues. */ - matching_lvalue: bool, - /* Moving out is not permitted with guards. */ - has_guard: bool, - /* If a pattern binding binds by-reference ever, then binding by-move in - * the same arm is disallowed (no "ref x @ some(move y)", etc etc). */ - mut ever_bound_by_ref: bool, + alt_region: ty::region, // Region for the alt as a whole + block_region: ty::region, // Region for the block of the arm }; fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path, @@ -175,7 +228,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { match bm { ast::bind_by_ref(mutbl) => { - pcx.ever_bound_by_ref = true; // if the binding is like // ref x | ref const x | ref mut x // then the type of x is &M T where M is the mutability @@ -193,26 +245,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { } ast::bind_by_move => { demand::eqtype(fcx, pat.span, expected, typ); - // check legality of moving out of the enum - if sub.is_some() { - tcx.sess.span_err(pat.span, - ~"cannot bind by-move with sub-bindings"); - } - if pcx.has_guard { - tcx.sess.span_err(pat.span, - ~"cannot bind by-move into a pattern guard"); - } - if pcx.ever_bound_by_ref { - tcx.sess.span_err(pat.span, - ~"cannot bind by-move and by-ref in the same pattern"); - } - if pcx.matching_lvalue { - tcx.sess.span_err(pat.span, - ~"cannot bind by-move when matching an lvalue"); - } } ast::bind_by_implicit_ref => { - pcx.ever_bound_by_ref = true; demand::eqtype(fcx, pat.span, expected, typ); } } diff --git a/src/rustc/middle/typeck/check/regionck.rs b/src/rustc/middle/typeck/check/regionck.rs index c980b6670a5b8..6b7a8b4e40278 100644 --- a/src/rustc/middle/typeck/check/regionck.rs +++ b/src/rustc/middle/typeck/check/regionck.rs @@ -286,12 +286,14 @@ fn constrain_free_variables( ~"captured variable does not outlive the enclosing closure"); note_and_explain_region( tcx, - ~"captured variable is valid for", - en_region); + ~"captured variable is valid for ", + en_region, + ~""); note_and_explain_region( tcx, - ~"closure is valid for", - region); + ~"closure is valid for ", + region, + ~""); } } } @@ -337,8 +339,9 @@ fn constrain_regions_in_type( fmt!("reference is not valid outside of its lifetime")); note_and_explain_region( tcx, - ~"the reference is only valid for", - region); + ~"the reference is only valid for ", + region, + ~""); rcx.errors_reported += 1u; } result::Ok(()) => { diff --git a/src/rustc/middle/typeck/infer/region_var_bindings.rs b/src/rustc/middle/typeck/infer/region_var_bindings.rs index ff0afdb4816e6..70dd5777e3deb 100644 --- a/src/rustc/middle/typeck/infer/region_var_bindings.rs +++ b/src/rustc/middle/typeck/infer/region_var_bindings.rs @@ -1104,21 +1104,23 @@ impl RegionVarBindings { note_and_explain_region( self.tcx, - ~"first, the lifetime cannot outlive", - upper_bound.region); + ~"first, the lifetime cannot outlive ", + upper_bound.region, + ~"..."); self.tcx.sess.span_note( upper_bound.span, - fmt!("due to the following expression")); + fmt!("...due to the following expression")); note_and_explain_region( self.tcx, - ~"but, the lifetime must be valid for", - lower_bound.region); + ~"but, the lifetime must be valid for ", + lower_bound.region, + ~"..."); self.tcx.sess.span_note( lower_bound.span, - fmt!("due to the following expression")); + fmt!("...due to the following expression")); return; } @@ -1154,21 +1156,23 @@ impl RegionVarBindings { note_and_explain_region( self.tcx, - ~"first, the lifetime must be contained by", - upper_bound_1.region); + ~"first, the lifetime must be contained by ", + upper_bound_1.region, + ~"..."); self.tcx.sess.span_note( upper_bound_1.span, - fmt!("due to the following expression")); + fmt!("...due to the following expression")); note_and_explain_region( self.tcx, - ~"but, the lifetime must also be contained by", - upper_bound_2.region); + ~"but, the lifetime must also be contained by ", + upper_bound_2.region, + ~"..."); self.tcx.sess.span_note( upper_bound_2.span, - fmt!("due to the following expression")); + fmt!("...due to the following expression")); return; } diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 5be0ea6d8d8db..70f617fe4a56a 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -35,6 +35,13 @@ use back_ = back; mod middle { mod trans { + mod inline; + mod monomorphize; + mod controlflow; + mod glue; + mod datum; + mod callee; + mod expr; mod common; mod consts; mod type_of; diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index 2b452f1d8b11a..ba7b9ffa478a4 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -21,16 +21,19 @@ use syntax::{ast, ast_util}; use syntax::ast_map; use driver::session::session; -fn note_and_explain_region(cx: ctxt, prefix: ~str, region: ty::region) { +fn note_and_explain_region(cx: ctxt, + prefix: ~str, + region: ty::region, + suffix: ~str) { match explain_region_and_span(cx, region) { (str, Some(span)) => { cx.sess.span_note( span, - fmt!("%s %s", prefix, str)); + fmt!("%s%s%s", prefix, str, suffix)); } (str, None) => { cx.sess.note( - fmt!("%s %s", prefix, str)); + fmt!("%s%s%s", prefix, str, suffix)); } } } @@ -55,7 +58,7 @@ fn explain_region_and_span(cx: ctxt, region: ty::region) Some(ast_map::node_expr(expr)) => { match expr.node { ast::expr_call(*) => explain_span(cx, ~"call", expr.span), - ast::expr_match(*) => explain_span(cx, ~"alt", expr.span), + ast::expr_match(*) => explain_span(cx, ~"match", expr.span), _ => explain_span(cx, ~"expression", expr.span) } } diff --git a/src/test/bench/graph500-bfs.rs b/src/test/bench/graph500-bfs.rs index 1dd116b2b4117..1402c9e860476 100644 --- a/src/test/bench/graph500-bfs.rs +++ b/src/test/bench/graph500-bfs.rs @@ -174,9 +174,9 @@ fn bfs2(graph: graph, key: node_id) -> bfs_result { match c { white => { let i = i as node_id; - + let neighbors = graph[i]; - + let mut color = white; do neighbors.each() |k| { @@ -217,7 +217,8 @@ fn pbfs(&&graph: arc::ARC, key: node_id) -> bfs_result { black(node_id) }; - let mut colors = do vec::from_fn((*arc::get(&graph)).len()) |i| { + let graph_vec = arc::get(&graph); // FIXME #3387 requires this temp + let mut colors = do vec::from_fn(graph_vec.len()) |i| { if i as node_id == key { gray(key) } @@ -243,7 +244,8 @@ fn pbfs(&&graph: arc::ARC, key: node_id) -> bfs_result { let color = arc::ARC(colors); - colors = do par::mapi_factory(*arc::get(&color)) { + let color_vec = arc::get(&color); // FIXME #3387 requires this temp + colors = do par::mapi_factory(*color_vec) { let colors = arc::clone(&color); let graph = arc::clone(&graph); fn~(i: uint, c: color) -> color { @@ -253,11 +255,11 @@ fn pbfs(&&graph: arc::ARC, key: node_id) -> bfs_result { match c { white => { let i = i as node_id; - + let neighbors = graph[i]; - + let mut color = white; - + do neighbors.each() |k| { if is_gray(colors[k]) { color = gray(k); diff --git a/src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs b/src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs new file mode 100644 index 0000000000000..71f4934effd37 --- /dev/null +++ b/src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs @@ -0,0 +1,9 @@ +struct X { x: (); drop { error!("destructor runs"); } } + +fn main() { + let x = Some((X { x: () }, X { x: () })); + match move x { + Some((move _y, ref _z)) => { }, //~ ERROR cannot bind by-move and by-ref in the same pattern + None => fail + } +} diff --git a/src/test/compile-fail/borrowck-confuse-region.rs b/src/test/compile-fail/borrowck-confuse-region.rs index 0f7323358d73c..a320cfae1b098 100644 --- a/src/test/compile-fail/borrowck-confuse-region.rs +++ b/src/test/compile-fail/borrowck-confuse-region.rs @@ -6,9 +6,11 @@ fn get() -> &int { + //~^ NOTE borrowed pointer must be valid for the anonymous lifetime #1 defined on + //~^^ NOTE ...but borrowed value is only valid for the block at let x = 3; return &x; - //~^ ERROR illegal borrow: borrowed pointer must be valid for the anonymous lifetime #1 defined on the block at 8:17, but the borrowed value is only valid for the block at 8:17 + //~^ ERROR illegal borrow } fn main() {} diff --git a/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs new file mode 100644 index 0000000000000..ef7c8da4b1921 --- /dev/null +++ b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs @@ -0,0 +1,15 @@ +// xfail-test #3387 + +enum foo = ~uint; + +impl foo: Add { + pure fn add(f: foo) -> foo { + foo(~(**self + **f)) + } +} + +fn main() { + let x = foo(~3); + let _y = x + move x; + //~^ ERROR moving out of immutable local variable prohibited due to outstanding loan +} diff --git a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs index 57e195a90754d..b2852784e18ed 100644 --- a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs +++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs @@ -7,7 +7,7 @@ fn foo(cond: fn() -> bool, box: fn() -> @int) { // Here we complain because the resulting region // of this borrow is the fn body as a whole. - y = borrow(x); //~ ERROR illegal borrow: managed value would have to be rooted + y = borrow(x); //~ ERROR illegal borrow: cannot root managed value long enough assert *x == *y; if cond() { break; } diff --git a/src/test/run-pass/autoderef-method-on-trait-monomorphized.rs b/src/test/run-pass/autoderef-method-on-trait-monomorphized.rs new file mode 100644 index 0000000000000..85868f44cfd50 --- /dev/null +++ b/src/test/run-pass/autoderef-method-on-trait-monomorphized.rs @@ -0,0 +1,16 @@ +trait double { + fn double() -> uint; +} + +impl uint: double { + fn double() -> uint { self * 2u } +} + +fn is_equal(x: @D, exp: uint) { + assert x.double() == exp; +} + +fn main() { + let x = @(3u as double); + is_equal(x, 6); +} diff --git a/src/test/run-pass/autoderef-method-on-trait.rs b/src/test/run-pass/autoderef-method-on-trait.rs new file mode 100644 index 0000000000000..68dde1e25a538 --- /dev/null +++ b/src/test/run-pass/autoderef-method-on-trait.rs @@ -0,0 +1,12 @@ +trait double { + fn double() -> uint; +} + +impl uint: double { + fn double() -> uint { self * 2u } +} + +fn main() { + let x = @(3u as double); + assert x.double() == 6u; +} diff --git a/src/test/run-pass/const-fields-and-indexing.rs b/src/test/run-pass/const-fields-and-indexing.rs index 8a02ea13186de..387ca032f6a74 100644 --- a/src/test/run-pass/const-fields-and-indexing.rs +++ b/src/test/run-pass/const-fields-and-indexing.rs @@ -6,6 +6,10 @@ const q : int = y[2]; const s : {a: int, b: int} = {a: 10, b: 20}; const t : int = s.b; +const k : {a: int, b: int, c: {d: int, e: int}} = {a: 10, b: 20, c: {d: 30, + e: 40}}; +const m : int = k.c.e; + fn main() { io::println(fmt!("%?", p)); io::println(fmt!("%?", q)); diff --git a/src/test/run-pass/pipe-presentation-examples.rs b/src/test/run-pass/pipe-presentation-examples.rs index 34e384e31218a..cf34c44d5000e 100644 --- a/src/test/run-pass/pipe-presentation-examples.rs +++ b/src/test/run-pass/pipe-presentation-examples.rs @@ -23,8 +23,8 @@ macro_rules! select_if ( } => { if $index == $count { match move pipes::try_recv($port) { - $(Some($message($($(move $x,)+)* next)) => { - let $next = unsafe { let x <- *ptr::addr_of(next); x }; + $(Some($message($($(move $x,)+)* move next)) => { + let $next = next; $e })+ _ => fail