diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 878070e98e7..e73b626e6ef 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -881,6 +881,7 @@ extern { /* Operations on global variables */ + pub fn LLVMIsAGlobalVariable(GlobalVar: ValueRef) -> ValueRef; pub fn LLVMAddGlobal(M: ModuleRef, Ty: TypeRef, Name: *const c_char) -> ValueRef; pub fn LLVMAddGlobalInAddressSpace(M: ModuleRef, diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 6a296674f3f..5ece5e59299 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -278,14 +278,14 @@ impl<'a, 'tcx> Opt<'a, 'tcx> { match *self { ConstantValue(ConstantExpr(lit_expr), _) => { let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id); - let (llval, _) = consts::const_expr(ccx, &*lit_expr); + let (llval, _) = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs); let lit_datum = immediate_rvalue(llval, lit_ty); let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx)); SingleResult(Result::new(bcx, lit_datum.val)) } ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2), _) => { - let (l1, _) = consts::const_expr(ccx, &**l1); - let (l2, _) = consts::const_expr(ccx, &**l2); + let (l1, _) = consts::const_expr(ccx, &**l1, bcx.fcx.param_substs); + let (l2, _) = consts::const_expr(ccx, &**l2, bcx.fcx.param_substs); RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) } Variant(disr_val, ref repr, _, _) => { @@ -832,8 +832,8 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, let _icx = push_ctxt("compare_values"); if ty::type_is_scalar(rhs_t) { - let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, ast::BiEq, debug_loc); - return Result::new(rs.bcx, rs.val); + let cmp = compare_scalar_types(cx, lhs, rhs, rhs_t, ast::BiEq, debug_loc); + return Result::new(cx, cmp); } match rhs_t.sty { @@ -1163,29 +1163,16 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } RangeResult(Result { val: vbegin, .. }, Result { bcx, val: vend }) => { - let Result { bcx, val: llge } = - compare_scalar_types(bcx, - test_val, - vbegin, - t, - ast::BiGe, - debug_loc); - let Result { bcx, val: llle } = - compare_scalar_types(bcx, - test_val, - vend, - t, - ast::BiLe, - debug_loc); - Result::new(bcx, And(bcx, llge, llle, debug_loc)) + let llge = compare_scalar_types(bcx, test_val, vbegin, + t, ast::BiGe, debug_loc); + let llle = compare_scalar_types(bcx, test_val, vend, + t, ast::BiLe, debug_loc); + Result::new(bcx, And(bcx, llge, llle, DebugLoc::None)) } LowerBound(Result { bcx, val }) => { - compare_scalar_types(bcx, - test_val, - val, - t, - ast::BiGe, - debug_loc) + Result::new(bcx, compare_scalar_types(bcx, test_val, + val, t, ast::BiGe, + debug_loc)) } } }; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 54c50b7a62b..7f7b5cd8006 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -26,7 +26,6 @@ #![allow(non_camel_case_types)] pub use self::ValueOrigin::*; -pub use self::scalar_type::*; use super::CrateTranslation; use super::ModuleTranslation; @@ -40,7 +39,6 @@ use metadata::{csearch, encoder, loader}; use middle::astencode; use middle::cfg; use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; -use middle::subst; use middle::weak_lang_items; use middle::subst::{Subst, Substs}; use middle::ty::{self, Ty, ClosureTyper}; @@ -498,7 +496,7 @@ pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId, t: Ty<'tcx>, parent_id: ast::DefId, - substs: &subst::Substs<'tcx>) + substs: &Substs<'tcx>) -> ValueRef { let _icx = push_ctxt("trans_res_dtor"); let did = inline::maybe_instantiate_inline(ccx, did); @@ -507,9 +505,9 @@ pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, assert_eq!(did.krate, ast::LOCAL_CRATE); // Since we're in trans we don't care for any region parameters - let substs = subst::Substs::erased(substs.types.clone()); + let substs = ccx.tcx().mk_substs(Substs::erased(substs.types.clone())); - let (val, _, _) = monomorphize::monomorphic_fn(ccx, did, &substs, None); + let (val, _, _) = monomorphize::monomorphic_fn(ccx, did, substs, None); val } else if did.krate == ast::LOCAL_CRATE { @@ -532,137 +530,100 @@ pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -// Used only for creating scalar comparison glue. -#[derive(Copy)] -pub enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, } +pub fn bin_op_to_icmp_predicate(ccx: &CrateContext, op: ast::BinOp_, signed: bool) + -> llvm::IntPredicate { + match op { + ast::BiEq => llvm::IntEQ, + ast::BiNe => llvm::IntNE, + ast::BiLt => if signed { llvm::IntSLT } else { llvm::IntULT }, + ast::BiLe => if signed { llvm::IntSLE } else { llvm::IntULE }, + ast::BiGt => if signed { llvm::IntSGT } else { llvm::IntUGT }, + ast::BiGe => if signed { llvm::IntSGE } else { llvm::IntUGE }, + op => { + ccx.sess().bug(&format!("comparison_op_to_icmp_predicate: expected \ + comparison operator, found {:?}", op)[]); + } + } +} -pub fn compare_scalar_types<'blk, 'tcx>(cx: Block<'blk, 'tcx>, +pub fn bin_op_to_fcmp_predicate(ccx: &CrateContext, op: ast::BinOp_) + -> llvm::RealPredicate { + match op { + ast::BiEq => llvm::RealOEQ, + ast::BiNe => llvm::RealUNE, + ast::BiLt => llvm::RealOLT, + ast::BiLe => llvm::RealOLE, + ast::BiGt => llvm::RealOGT, + ast::BiGe => llvm::RealOGE, + op => { + ccx.sess().bug(&format!("comparison_op_to_fcmp_predicate: expected \ + comparison operator, found {:?}", op)[]); + } + } +} + +pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, lhs: ValueRef, rhs: ValueRef, t: Ty<'tcx>, op: ast::BinOp_, debug_loc: DebugLoc) - -> Result<'blk, 'tcx> { - let f = |a| Result::new(cx, compare_scalar_values(cx, lhs, rhs, a, op, debug_loc)); - + -> ValueRef { match t.sty { - ty::ty_tup(ref tys) if tys.is_empty() => f(nil_type), - ty::ty_bool | ty::ty_uint(_) | ty::ty_char => f(unsigned_int), - ty::ty_ptr(mt) if common::type_is_sized(cx.tcx(), mt.ty) => f(unsigned_int), - ty::ty_int(_) => f(signed_int), - ty::ty_float(_) => f(floating_point), - // Should never get here, because t is scalar. - _ => cx.sess().bug("non-scalar type passed to compare_scalar_types") - } -} - - -// A helper function to do the actual comparison of scalar values. -pub fn compare_scalar_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - nt: scalar_type, - op: ast::BinOp_, - debug_loc: DebugLoc) - -> ValueRef { - let _icx = push_ctxt("compare_scalar_values"); - fn die(cx: Block) -> ! { - cx.sess().bug("compare_scalar_values: must be a comparison operator"); - } - match nt { - nil_type => { - // We don't need to do actual comparisons for nil. - // () == () holds but () < () does not. - match op { - ast::BiEq | ast::BiLe | ast::BiGe => return C_bool(cx.ccx(), true), - ast::BiNe | ast::BiLt | ast::BiGt => return C_bool(cx.ccx(), false), - // refinements would be nice - _ => die(cx) + ty::ty_tup(ref tys) if tys.is_empty() => { + // We don't need to do actual comparisons for nil. + // () == () holds but () < () does not. + match op { + ast::BiEq | ast::BiLe | ast::BiGe => return C_bool(bcx.ccx(), true), + ast::BiNe | ast::BiLt | ast::BiGt => return C_bool(bcx.ccx(), false), + // refinements would be nice + _ => bcx.sess().bug("compare_scalar_types: must be a comparison operator") + } } - } - floating_point => { - let cmp = match op { - ast::BiEq => llvm::RealOEQ, - ast::BiNe => llvm::RealUNE, - ast::BiLt => llvm::RealOLT, - ast::BiLe => llvm::RealOLE, - ast::BiGt => llvm::RealOGT, - ast::BiGe => llvm::RealOGE, - _ => die(cx) - }; - return FCmp(cx, cmp, lhs, rhs, debug_loc); - } - signed_int => { - let cmp = match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntSLT, - ast::BiLe => llvm::IntSLE, - ast::BiGt => llvm::IntSGT, - ast::BiGe => llvm::IntSGE, - _ => die(cx) - }; - return ICmp(cx, cmp, lhs, rhs, debug_loc); - } - unsigned_int => { - let cmp = match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntULT, - ast::BiLe => llvm::IntULE, - ast::BiGt => llvm::IntUGT, - ast::BiGe => llvm::IntUGE, - _ => die(cx) - }; - return ICmp(cx, cmp, lhs, rhs, debug_loc); - } + ty::ty_bool | ty::ty_uint(_) | ty::ty_char => { + ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, false), lhs, rhs, debug_loc) + } + ty::ty_ptr(mt) if common::type_is_sized(bcx.tcx(), mt.ty) => { + ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, false), lhs, rhs, debug_loc) + } + ty::ty_int(_) => { + ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, true), lhs, rhs, debug_loc) + } + ty::ty_float(_) => { + FCmp(bcx, bin_op_to_fcmp_predicate(bcx.ccx(), op), lhs, rhs, debug_loc) + } + // Should never get here, because t is scalar. + _ => bcx.sess().bug("non-scalar type passed to compare_scalar_types") } } -pub fn compare_simd_types<'blk, 'tcx>( - cx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - t: Ty<'tcx>, - size: uint, - op: ast::BinOp_, - debug_loc: DebugLoc) - -> ValueRef { - let cmp = match t.sty { +pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + t: Ty<'tcx>, + op: ast::BinOp_, + debug_loc: DebugLoc) + -> ValueRef { + let signed = match t.sty { ty::ty_float(_) => { // The comparison operators for floating point vectors are challenging. // LLVM outputs a `< size x i1 >`, but if we perform a sign extension // then bitcast to a floating point vector, the result will be `-NaN` // for each truth value. Because of this they are unsupported. - cx.sess().bug("compare_simd_types: comparison operators \ - not supported for floating point SIMD types") + bcx.sess().bug("compare_simd_types: comparison operators \ + not supported for floating point SIMD types") }, - ty::ty_uint(_) => match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntULT, - ast::BiLe => llvm::IntULE, - ast::BiGt => llvm::IntUGT, - ast::BiGe => llvm::IntUGE, - _ => cx.sess().bug("compare_simd_types: must be a comparison operator"), - }, - ty::ty_int(_) => match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntSLT, - ast::BiLe => llvm::IntSLE, - ast::BiGt => llvm::IntSGT, - ast::BiGe => llvm::IntSGE, - _ => cx.sess().bug("compare_simd_types: must be a comparison operator"), - }, - _ => cx.sess().bug("compare_simd_types: invalid SIMD type"), + ty::ty_uint(_) => false, + ty::ty_int(_) => true, + _ => bcx.sess().bug("compare_simd_types: invalid SIMD type"), }; - let return_ty = Type::vector(&type_of(cx.ccx(), t), size as u64); + + let cmp = bin_op_to_icmp_predicate(bcx.ccx(), op, signed); // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension // to get the correctly sized type. This will compile to a single instruction // once the IR is converted to assembly if the SIMD instruction is supported // by the target architecture. - SExt(cx, ICmp(cx, cmp, lhs, rhs, debug_loc), return_ty) + SExt(bcx, ICmp(bcx, cmp, lhs, rhs, debug_loc), val_ty(lhs)) } // Iterates through the elements of a structural type. @@ -679,7 +640,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>, repr: &adt::Repr<'tcx>, av: ValueRef, variant: &ty::VariantInfo<'tcx>, - substs: &subst::Substs<'tcx>, + substs: &Substs<'tcx>, f: &mut F) -> Block<'blk, 'tcx> where F: FnMut(Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>, @@ -1034,21 +995,39 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef { if type_is_zero_size(cx.ccx(), t) { C_undef(type_of::type_of(cx.ccx(), t)) - } else if ty::type_is_bool(t) { - Trunc(cx, LoadRangeAssert(cx, ptr, 0, 2, llvm::False), Type::i1(cx.ccx())) } else if type_is_immediate(cx.ccx(), t) && type_of::type_of(cx.ccx(), t).is_aggregate() { // We want to pass small aggregates as immediate values, but using an aggregate LLVM type // for this leads to bad optimizations, so its arg type is an appropriately sized integer // and we have to convert it Load(cx, BitCast(cx, ptr, type_of::arg_type_of(cx.ccx(), t).ptr_to())) - } else if ty::type_is_region_ptr(t) || ty::type_is_unique(t) { - LoadNonNull(cx, ptr) - } else if ty::type_is_char(t) { - // a char is a Unicode codepoint, and so takes values from 0 - // to 0x10FFFF inclusive only. - LoadRangeAssert(cx, ptr, 0, 0x10FFFF + 1, llvm::False) } else { - Load(cx, ptr) + unsafe { + let global = llvm::LLVMIsAGlobalVariable(ptr); + if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { + let val = llvm::LLVMGetInitializer(global); + if !val.is_null() { + // This could go into its own function, for DRY. + // (something like "pre-store packing/post-load unpacking") + if ty::type_is_bool(t) { + return Trunc(cx, val, Type::i1(cx.ccx())); + } else { + return val; + } + } + } + } + if ty::type_is_bool(t) { + Trunc(cx, LoadRangeAssert(cx, ptr, 0, 2, llvm::False), Type::i1(cx.ccx())) + } else if ty::type_is_char(t) { + // a char is a Unicode codepoint, and so takes values from 0 + // to 0x10FFFF inclusive only. + LoadRangeAssert(cx, ptr, 0, 0x10FFFF + 1, llvm::False) + } else if (ty::type_is_region_ptr(t) || ty::type_is_unique(t)) + && !common::type_is_fat_ptr(cx.tcx(), t) { + LoadNonNull(cx, ptr) + } else { + Load(cx, ptr) + } } } @@ -1064,7 +1043,7 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t Store(cx, v, BitCast(cx, dst, type_of::arg_type_of(cx.ccx(), t).ptr_to())); } else { Store(cx, v, dst); - }; + } } pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &ast::Local) @@ -1162,7 +1141,7 @@ pub fn memcpy_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let llalign = type_of::align_of(ccx, t); call_memcpy(bcx, dst, src, llsz, llalign as u32); } else { - store_ty(bcx, Load(bcx, src), dst, t); + store_ty(bcx, load_ty(bcx, src, t), dst, t); } } @@ -1425,7 +1404,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, id: ast::NodeId, has_env: bool, output_type: ty::FnOutput<'tcx>, - param_substs: &'a Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, sp: Option, block_arena: &'a TypedArena>) -> FunctionContext<'a, 'tcx> { @@ -1793,7 +1772,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, decl: &ast::FnDecl, body: &ast::Block, llfndecl: ValueRef, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, fn_ast_id: ast::NodeId, _attributes: &[ast::Attribute], output_type: ty::FnOutput<'tcx>, @@ -1942,7 +1921,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, decl: &ast::FnDecl, body: &ast::Block, llfndecl: ValueRef, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, id: ast::NodeId, attrs: &[ast::Attribute]) { let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string()); @@ -1968,7 +1947,7 @@ pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, variant: &ast::Variant, _args: &[ast::VariantArg], disr: ty::Disr, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, llfndecl: ValueRef) { let _icx = push_ctxt("trans_enum_variant"); @@ -2049,7 +2028,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, pub fn trans_tuple_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, _fields: &[ast::StructField], ctor_id: ast::NodeId, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, llfndecl: ValueRef) { let _icx = push_ctxt("trans_tuple_struct"); @@ -2064,7 +2043,7 @@ pub fn trans_tuple_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ctor_id: ast::NodeId, disr: ty::Disr, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, llfndecl: ValueRef) { let ctor_ty = ty::node_id_to_type(ccx.tcx(), ctor_id); let ctor_ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &ctor_ty); @@ -2302,13 +2281,14 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { // translated everywhere it's needed. for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) { let llfn = get_item_val(ccx, item.id); + let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); if abi != Rust { foreign::trans_rust_fn_with_foreign_abi(ccx, &**decl, &**body, &item.attrs[], llfn, - &Substs::trans_empty(), + empty_substs, item.id, None); } else { @@ -2316,7 +2296,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { &**decl, &**body, llfn, - &Substs::trans_empty(), + empty_substs, item.id, &item.attrs[]); } @@ -2792,7 +2772,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { // We need the translated value here, because for enums the // LLVM type is not fully determined by the Rust type. - let (v, ty) = consts::const_expr(ccx, &**expr); + let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); + let (v, ty) = consts::const_expr(ccx, &**expr, empty_substs); ccx.static_values().borrow_mut().insert(id, v); unsafe { // boolean SSA values are i1, but they have to be stored in i8 slots, @@ -2820,12 +2801,6 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { } } - ast::ItemConst(_, ref expr) => { - let (v, _) = consts::const_expr(ccx, &**expr); - ccx.const_values().borrow_mut().insert(id, v); - v - } - ast::ItemFn(_, _, abi, _, _) => { let sym = sym(); let llfn = if abi == Rust { diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index e45918ce5a7..bda8b8938b7 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -226,7 +226,7 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) pub fn trans_fn_ref<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: ast::DefId, node: ExprOrMethodCall, - param_substs: &subst::Substs<'tcx>) + param_substs: &'tcx subst::Substs<'tcx>) -> Datum<'tcx, Rvalue> { let _icx = push_ctxt("trans_fn_ref"); @@ -326,7 +326,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( &function_name[]); // - let empty_substs = Substs::trans_empty(); + let empty_substs = tcx.mk_substs(Substs::trans_empty()); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); fcx = new_fn_ctxt(ccx, @@ -334,7 +334,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( ast::DUMMY_NODE_ID, false, sig.output, - &empty_substs, + empty_substs, None, &block_arena); let mut bcx = init_function(&fcx, false, sig.output); @@ -386,7 +386,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( ccx: &CrateContext<'a, 'tcx>, def_id: ast::DefId, node: ExprOrMethodCall, - param_substs: &subst::Substs<'tcx>, + param_substs: &'tcx subst::Substs<'tcx>, substs: subst::Substs<'tcx>) -> Datum<'tcx, Rvalue> { @@ -416,7 +416,9 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( // We need to modify the def_id and our substs in order to monomorphize // the function. let (is_default, def_id, substs) = match ty::provided_source(tcx, def_id) { - None => (false, def_id, substs), + None => { + (false, def_id, tcx.mk_substs(substs)) + } Some(source_id) => { // There are two relevant substitutions when compiling // default methods. First, there is the substitution for @@ -444,7 +446,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( .erase_regions(); // And compose them - let new_substs = first_subst.subst(tcx, &substs); + let new_substs = tcx.mk_substs(first_subst.subst(tcx, &substs)); debug!("trans_fn_with_vtables - default method: \ substs = {}, trait_subst = {}, \ @@ -463,7 +465,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( }; // If this is a closure, redirect to it. - match closure::get_or_create_declaration_if_closure(ccx, def_id, &substs) { + match closure::get_or_create_declaration_if_closure(ccx, def_id, substs) { None => {} Some(llfn) => return llfn, } @@ -505,7 +507,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( }; let (val, fn_ty, must_cast) = - monomorphize::monomorphic_fn(ccx, def_id, &substs, opt_ref_id); + monomorphize::monomorphic_fn(ccx, def_id, substs, opt_ref_id); if must_cast && node != ExprId(0) { // Monotype of the REFERENCE to the function (type params // are subst'd) diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index 8473ce1b797..f92df999e60 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -9,6 +9,7 @@ // except according to those terms. use back::link::mangle_internal_name_by_path_and_seq; +use llvm::ValueRef; use middle::mem_categorization::Typer; use trans::adt; use trans::base::*; @@ -137,7 +138,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc // duplicate declarations let function_type = erase_regions(ccx.tcx(), &function_type); let params = match function_type.sty { - ty::ty_closure(_, _, ref substs) => substs.types.clone(), + ty::ty_closure(_, _, substs) => &substs.types, _ => unreachable!() }; let mono_id = MonoId { @@ -171,41 +172,51 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc Some(Datum::new(llfn, function_type, Rvalue::new(ByValue))) } -pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - decl: &ast::FnDecl, - body: &ast::Block, - id: ast::NodeId, - dest: expr::Dest) - -> Block<'blk, 'tcx> +pub enum Dest<'a, 'tcx: 'a> { + SaveIn(Block<'a, 'tcx>, ValueRef), + Ignore(&'a CrateContext<'a, 'tcx>) +} + +pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, + decl: &ast::FnDecl, + body: &ast::Block, + id: ast::NodeId, + param_substs: &'tcx Substs<'tcx>) + -> Option> { + let ccx = match dest { + Dest::SaveIn(bcx, _) => bcx.ccx(), + Dest::Ignore(ccx) => ccx + }; + let tcx = ccx.tcx(); let _icx = push_ctxt("closure::trans_closure"); debug!("trans_closure()"); let closure_id = ast_util::local_def(id); let llfn = get_or_create_declaration_if_closure( - bcx.ccx(), + ccx, closure_id, - bcx.fcx.param_substs).unwrap(); + param_substs).unwrap(); // Get the type of this closure. Use the current `param_substs` as // the closure substitutions. This makes sense because the closure // takes the same set of type arguments as the enclosing fn, and // this function (`trans_closure`) is invoked at the point // of the closure expression. - let typer = NormalizingClosureTyper::new(bcx.tcx()); - let function_type = typer.closure_type(closure_id, bcx.fcx.param_substs); + let typer = NormalizingClosureTyper::new(tcx); + let function_type = typer.closure_type(closure_id, param_substs); let freevars: Vec = - ty::with_freevars(bcx.tcx(), id, |fv| fv.iter().map(|&fv| fv).collect()); + ty::with_freevars(tcx, id, |fv| fv.iter().map(|&fv| fv).collect()); - let sig = ty::erase_late_bound_regions(bcx.tcx(), &function_type.sig); + let sig = ty::erase_late_bound_regions(tcx, &function_type.sig); - trans_closure(bcx.ccx(), + trans_closure(ccx, decl, body, llfn.val, - bcx.fcx.param_substs, + param_substs, id, &[], sig.output, @@ -215,15 +226,15 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, // Don't hoist this to the top of the function. It's perfectly legitimate // to have a zero-size closure (in which case dest will be `Ignore`) and // we must still generate the closure body. - let dest_addr = match dest { - expr::SaveIn(p) => p, - expr::Ignore => { + let (mut bcx, dest_addr) = match dest { + Dest::SaveIn(bcx, p) => (bcx, p), + Dest::Ignore(_) => { debug!("trans_closure() ignoring result"); - return bcx + return None; } }; - let repr = adt::represent_type(bcx.ccx(), node_id_type(bcx, id)); + let repr = adt::represent_type(ccx, node_id_type(bcx, id)); // Create the closure. for (i, freevar) in freevars.iter().enumerate() { @@ -235,8 +246,7 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, i); let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(), closure_expr_id: id }; - let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap(); - match upvar_capture { + match tcx.upvar_capture(upvar_id).unwrap() { ty::UpvarCapture::ByValue => { bcx = datum.store_to(bcx, upvar_slot_dest); } @@ -247,6 +257,6 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } adt::trans_set_discr(bcx, &*repr, dest_addr, 0); - bcx + Some(bcx) } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 87b7f413fd1..d658003702d 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -410,7 +410,7 @@ pub struct FunctionContext<'a, 'tcx: 'a> { // If this function is being monomorphized, this contains the type // substitutions used. - pub param_substs: &'a Substs<'tcx>, + pub param_substs: &'tcx Substs<'tcx>, // The source span and nesting context where this function comes from, for // error reporting and symbol generation. @@ -858,25 +858,6 @@ pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef { C_named_struct(cx.tn().find_type("str_slice").unwrap(), &[cs, C_uint(cx, len)]) } -pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef { - unsafe { - let len = data.len(); - let lldata = C_bytes(cx, data); - - let gsym = token::gensym("binary"); - let name = format!("binary{}", gsym.usize()); - let name = CString::from_vec(name.into_bytes()); - let g = llvm::LLVMAddGlobal(cx.llmod(), val_ty(lldata).to_ref(), - name.as_ptr()); - llvm::LLVMSetInitializer(g, lldata); - llvm::LLVMSetGlobalConstant(g, True); - llvm::SetLinkage(g, llvm::InternalLinkage); - - let cs = consts::ptrcast(g, Type::i8p(cx)); - C_struct(cx, &[cs, C_uint(cx, len)], false) - } -} - pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { C_struct_in_context(cx.llcx(), elts, packed) } @@ -901,6 +882,12 @@ pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { } } +pub fn C_vector(elts: &[ValueRef]) -> ValueRef { + unsafe { + return llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint); + } +} + pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef { C_bytes_in_context(cx.llcx(), bytes) } diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 278323d186b..e4cbfc9a186 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -11,13 +11,13 @@ use back::abi; use llvm; -use llvm::{ConstFCmp, ConstICmp, SetLinkage, PrivateLinkage, ValueRef, Bool, True, False}; -use llvm::{IntEQ, IntNE, IntUGT, IntUGE, IntULT, IntULE, IntSGT, IntSGE, IntSLT, IntSLE, - RealOEQ, RealOGT, RealOGE, RealOLT, RealOLE, RealONE}; -use middle::{const_eval, def}; -use trans::{adt, consts, debuginfo, expr, inline, machine}; +use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr}; +use llvm::{PrivateLinkage, ValueRef, Bool, True}; +use middle::{check_const, const_eval, def}; +use trans::{adt, closure, debuginfo, expr, inline, machine}; use trans::base::{self, push_ctxt}; use trans::common::*; +use trans::monomorphize; use trans::type_::Type; use trans::type_of; use middle::subst::Substs; @@ -74,7 +74,16 @@ pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit) } ast::LitBool(b) => C_bool(cx, b), ast::LitStr(ref s, _) => C_str_slice(cx, (*s).clone()), - ast::LitBinary(ref data) => C_binary_slice(cx, &data[]), + ast::LitBinary(ref data) => { + let g = addr_of(cx, C_bytes(cx, &data[]), "binary", e.id); + let base = ptrcast(g, Type::i8p(cx)); + let prev_const = cx.const_unsized().borrow_mut() + .insert(base, g); + assert!(prev_const.is_none() || prev_const == Some(g)); + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + C_struct(cx, &[base, C_uint(cx, data.len())], false) + } } } @@ -84,36 +93,41 @@ pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { } } -fn const_vec(cx: &CrateContext, e: &ast::Expr, - es: &[P]) -> (ValueRef, Type) { - let vec_ty = ty::expr_ty(cx.tcx(), e); - let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); - let llunitty = type_of::type_of(cx, unit_ty); - let vs = es.iter().map(|e| const_expr(cx, &**e).0) - .collect::>(); - // If the vector contains enums, an LLVM array won't work. - let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) { - C_struct(cx, &vs[], false) - } else { - C_array(llunitty, &vs[]) - }; - (v, llunitty) -} - -pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef { +fn addr_of_mut(ccx: &CrateContext, + cv: ValueRef, + kind: &str, + id: ast::NodeId) + -> ValueRef { unsafe { - let gv = llvm::LLVMAddGlobal(cx.llmod(), val_ty(cv).to_ref(), - "const\0".as_ptr() as *const _); + let name = format!("{}{}\0", kind, id); + let gv = llvm::LLVMAddGlobal(ccx.llmod(), val_ty(cv).to_ref(), + name.as_ptr() as *const _); llvm::LLVMSetInitializer(gv, cv); - llvm::LLVMSetGlobalConstant(gv, - if mutbl == ast::MutImmutable {True} else {False}); SetLinkage(gv, PrivateLinkage); + SetUnnamedAddr(gv, true); gv } } +pub fn addr_of(ccx: &CrateContext, + cv: ValueRef, + kind: &str, + id: ast::NodeId) + -> ValueRef { + match ccx.const_globals().borrow().get(&cv) { + Some(&gv) => return gv, + None => {} + } + let gv = addr_of_mut(ccx, cv, kind, id); + unsafe { + llvm::LLVMSetGlobalConstant(gv, True); + } + ccx.const_globals().borrow_mut().insert(cv, gv); + gv +} + fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef { - let v = match cx.const_globals().borrow().get(&(v as int)) { + let v = match cx.const_unsized().borrow().get(&v) { Some(&v) => v, None => v }; @@ -122,155 +136,209 @@ fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef { } } -fn const_deref_newtype<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, v: ValueRef, t: Ty<'tcx>) - -> ValueRef { - let repr = adt::represent_type(cx, t); - adt::const_get_field(cx, &*repr, v, 0, 0) -} - -fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, v: ValueRef, - t: Ty<'tcx>, explicit: bool) +fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + v: ValueRef, + ty: Ty<'tcx>) -> (ValueRef, Ty<'tcx>) { - match ty::deref(t, explicit) { - Some(ref mt) => { - match t.sty { - ty::ty_ptr(mt) | ty::ty_rptr(_, mt) => { - if type_is_sized(cx.tcx(), mt.ty) { - (const_deref_ptr(cx, v), mt.ty) - } else { - // Derefing a fat pointer does not change the representation, - // just the type to ty_open. - (v, ty::mk_open(cx.tcx(), mt.ty)) - } - } - ty::ty_enum(..) | ty::ty_struct(..) => { - assert!(mt.mutbl != ast::MutMutable); - (const_deref_newtype(cx, v, t), mt.ty) - } - _ => { - cx.sess().bug(&format!("unexpected dereferenceable type {}", - ty_to_string(cx.tcx(), t))[]) - } + match ty::deref(ty, true) { + Some(mt) => { + if type_is_sized(cx.tcx(), mt.ty) { + (const_deref_ptr(cx, v), mt.ty) + } else { + // Derefing a fat pointer does not change the representation, + // just the type to ty_open. + (v, ty::mk_open(cx.tcx(), mt.ty)) } } None => { - cx.sess().bug(&format!("cannot dereference const of type {}", - ty_to_string(cx.tcx(), t))[]) + cx.sess().bug(&format!("unexpected dereferenceable type {}", + ty_to_string(cx.tcx(), ty))[]) } } } -pub fn get_const_val(cx: &CrateContext, - mut def_id: ast::DefId) -> ValueRef { - let contains_key = cx.const_values().borrow().contains_key(&def_id.node); - if !ast_util::is_local(def_id) || !contains_key { - if !ast_util::is_local(def_id) { - def_id = inline::maybe_instantiate_inline(cx, def_id); - } +pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: ast::DefId, + ref_expr: &ast::Expr) + -> &'tcx ast::Expr { + let def_id = inline::maybe_instantiate_inline(ccx, def_id); - if let ast::ItemConst(..) = cx.tcx().map.expect_item(def_id.node).node { - base::get_item_val(cx, def_id.node); - } + if def_id.krate != ast::LOCAL_CRATE { + ccx.sess().span_bug(ref_expr.span, + "cross crate constant could not be inlined"); } - cx.const_values().borrow()[def_id.node].clone() + let item = ccx.tcx().map.expect_item(def_id.node); + if let ast::ItemConst(_, ref expr) = item.node { + &**expr + } else { + ccx.sess().span_bug(ref_expr.span, + &format!("get_const_val given non-constant item {}", + item.repr(ccx.tcx()))[]); + } } -pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, e: &ast::Expr) +fn get_const_val(ccx: &CrateContext, + def_id: ast::DefId, + ref_expr: &ast::Expr) -> ValueRef { + let expr = get_const_expr(ccx, def_id, ref_expr); + let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); + get_const_expr_as_global(ccx, expr, check_const::PURE_CONST, empty_substs) +} + +pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + expr: &ast::Expr, + qualif: check_const::ConstQualif, + param_substs: &'tcx Substs<'tcx>) + -> ValueRef { + // Special-case constants to cache a common global for all uses. + match expr.node { + ast::ExprPath(_) => { + let def = ccx.tcx().def_map.borrow()[expr.id]; + match def { + def::DefConst(def_id) => { + if !ccx.tcx().adjustments.borrow().contains_key(&expr.id) { + return get_const_val(ccx, def_id, expr); + } + } + _ => {} + } + } + _ => {} + } + + let key = (expr.id, param_substs); + match ccx.const_values().borrow().get(&key) { + Some(&val) => return val, + None => {} + } + let val = if qualif.intersects(check_const::NON_STATIC_BORROWS) { + // Avoid autorefs as they would create global instead of stack + // references, even when only the latter are correct. + let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, + &ty::expr_ty(ccx.tcx(), expr)); + const_expr_unadjusted(ccx, expr, ty, param_substs) + } else { + const_expr(ccx, expr, param_substs).0 + }; + + // boolean SSA values are i1, but they have to be stored in i8 slots, + // otherwise some LLVM optimization passes don't work as expected + let val = unsafe { + if llvm::LLVMTypeOf(val) == Type::i1(ccx).to_ref() { + llvm::LLVMConstZExt(val, Type::i8(ccx).to_ref()) + } else { + val + } + }; + + let lvalue = addr_of(ccx, val, "const", expr.id); + ccx.const_values().borrow_mut().insert(key, lvalue); + lvalue +} + +pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + e: &ast::Expr, + param_substs: &'tcx Substs<'tcx>) -> (ValueRef, Ty<'tcx>) { - let llconst = const_expr_unadjusted(cx, e); + let ety = monomorphize::apply_param_substs(cx.tcx(), param_substs, + &ty::expr_ty(cx.tcx(), e)); + let llconst = const_expr_unadjusted(cx, e, ety, param_substs); let mut llconst = llconst; - let ety = ty::expr_ty(cx.tcx(), e); - let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e); + let mut ety_adjusted = monomorphize::apply_param_substs(cx.tcx(), param_substs, + &ty::expr_ty_adjusted(cx.tcx(), e)); let opt_adj = cx.tcx().adjustments.borrow().get(&e.id).cloned(); match opt_adj { - None => { } - Some(adj) => { - match adj { - ty::AdjustReifyFnPointer(_def_id) => { - // FIXME(#19925) once fn item types are - // zero-sized, we'll need to do something here + Some(ty::AdjustReifyFnPointer(_def_id)) => { + // FIXME(#19925) once fn item types are + // zero-sized, we'll need to do something here + } + Some(ty::AdjustDerefRef(adj)) => { + let mut ty = ety; + // Save the last autoderef in case we can avoid it. + if adj.autoderefs > 0 { + for _ in 0..adj.autoderefs-1 { + let (dv, dt) = const_deref(cx, llconst, ty); + llconst = dv; + ty = dt; } - ty::AdjustDerefRef(ref adj) => { - let mut ty = ety; - // Save the last autoderef in case we can avoid it. - if adj.autoderefs > 0 { - for _ in 0..adj.autoderefs-1 { - let (dv, dt) = const_deref(cx, llconst, ty, false); - llconst = dv; - ty = dt; - } - } + } - match adj.autoref { - None => { - let (dv, dt) = const_deref(cx, llconst, ty, false); - llconst = dv; + let second_autoref = match adj.autoref { + None => { + let (dv, dt) = const_deref(cx, llconst, ty); + llconst = dv; - // If we derefed a fat pointer then we will have an - // open type here. So we need to update the type with - // the one returned from const_deref. - ety_adjusted = dt; - } - Some(ref autoref) => { - match *autoref { - ty::AutoUnsafe(_, None) | - ty::AutoPtr(ty::ReStatic, _, None) => { - // Don't copy data to do a deref+ref - // (i.e., skip the last auto-deref). - if adj.autoderefs == 0 { - llconst = const_addr_of(cx, llconst, ast::MutImmutable); - } - } - ty::AutoPtr(ty::ReStatic, _, Some(box ty::AutoUnsize(..))) => { - if adj.autoderefs > 0 { - // Seeing as we are deref'ing here and take a reference - // again to make the pointer part of the far pointer below, - // we just skip the whole thing. We still need the type - // though. This works even if we don't need to deref - // because of byref semantics. Note that this is not just - // an optimisation, it is necessary for mutable vectors to - // work properly. - let (_, dt) = const_deref(cx, llconst, ty, false); - ty = dt; - } else { - llconst = const_addr_of(cx, llconst, ast::MutImmutable) - } - - match ty.sty { - ty::ty_vec(unit_ty, Some(len)) => { - let llunitty = type_of::type_of(cx, unit_ty); - let llptr = ptrcast(llconst, llunitty.ptr_to()); - let prev_const = cx.const_globals().borrow_mut() - .insert(llptr as int, llconst); - assert!(prev_const.is_none() || - prev_const == Some(llconst)); - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - llconst = C_struct(cx, &[ - llptr, - C_uint(cx, len) - ], false); - } - _ => cx.sess().span_bug(e.span, - &format!("unimplemented type in const unsize: {}", - ty_to_string(cx.tcx(), ty))[]) - } - } - _ => { - cx.sess() - .span_bug(e.span, - &format!("unimplemented const \ - autoref {:?}", - autoref)[]) + // If we derefed a fat pointer then we will have an + // open type here. So we need to update the type with + // the one returned from const_deref. + ety_adjusted = dt; + None + } + Some(ty::AutoUnsafe(_, opt_autoref)) | + Some(ty::AutoPtr(_, _, opt_autoref)) => { + if adj.autoderefs == 0 { + // Don't copy data to do a deref+ref + // (i.e., skip the last auto-deref). + llconst = addr_of(cx, llconst, "autoref", e.id); + } else { + // Seeing as we are deref'ing here and take a reference + // again to make the pointer part of the far pointer below, + // we just skip the whole thing. We still need the type + // though. This works even if we don't need to deref + // because of byref semantics. Note that this is not just + // an optimisation, it is necessary for mutable vectors to + // work properly. + ty = match ty::deref(ty, true) { + Some(mt) => { + if type_is_sized(cx.tcx(), mt.ty) { + mt.ty + } else { + // Derefing a fat pointer does not change the representation, + // just the type to ty_open. + ty::mk_open(cx.tcx(), mt.ty) } } + None => { + cx.sess().bug(&format!("unexpected dereferenceable type {}", + ty_to_string(cx.tcx(), ty))[]) + } } } + opt_autoref + } + Some(autoref) => { + cx.sess().span_bug(e.span, + &format!("unimplemented const first autoref {:?}", autoref)[]) + } + }; + match second_autoref { + None => {} + Some(box ty::AutoUnsafe(_, None)) | + Some(box ty::AutoPtr(_, _, None)) => { + llconst = addr_of(cx, llconst, "autoref", e.id); + } + Some(box ty::AutoUnsize(ref k)) => { + let unsized_ty = ty::unsize_ty(cx.tcx(), ty, k, e.span); + let info = expr::unsized_info(cx, k, e.id, ty, param_substs, + |t| ty::mk_imm_rptr(cx.tcx(), cx.tcx().mk_region(ty::ReStatic), t)); + + let base = ptrcast(llconst, type_of::type_of(cx, unsized_ty).ptr_to()); + let prev_const = cx.const_unsized().borrow_mut() + .insert(base, llconst); + assert!(prev_const.is_none() || prev_const == Some(llconst)); + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + llconst = C_struct(cx, &[base, info], false); + } + Some(autoref) => { + cx.sess().span_bug(e.span, + &format!("unimplemented const second autoref {:?}", autoref)[]) } } } - } + None => {} + }; let llty = type_of::sizing_type_of(cx, ety_adjusted); let csize = machine::llsize_of_alloc(cx, val_ty(llconst)); @@ -282,36 +350,42 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, e: &ast::Expr) llvm::LLVMDumpValue(C_undef(llty)); } cx.sess().bug(&format!("const {} of type {} has size {} instead of {}", - e.repr(cx.tcx()), ty_to_string(cx.tcx(), ety), + e.repr(cx.tcx()), ty_to_string(cx.tcx(), ety_adjusted), csize, tsize)[]); } (llconst, ety_adjusted) } -// the bool returned is whether this expression can be inlined into other crates -// if it's assigned to a static. -fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { +fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + e: &ast::Expr, + ety: Ty<'tcx>, + param_substs: &'tcx Substs<'tcx>) -> ValueRef { let map_list = |exprs: &[P]| { - exprs.iter().map(|e| const_expr(cx, &**e).0) + exprs.iter().map(|e| const_expr(cx, &**e, param_substs).0) .fold(Vec::new(), |mut l, val| { l.push(val); l }) }; unsafe { let _icx = push_ctxt("const_expr"); return match e.node { ast::ExprLit(ref lit) => { - consts::const_lit(cx, e, &**lit) + const_lit(cx, e, &**lit) } ast::ExprBinary(b, ref e1, ref e2) => { - let (te1, _) = const_expr(cx, &**e1); - let (te2, _) = const_expr(cx, &**e2); - - let te2 = base::cast_shift_const_rhs(b, te1, te2); - /* Neither type is bottom, and we expect them to be unified * already, so the following is safe. */ - let ty = ty::expr_ty(cx.tcx(), &**e1); - let is_float = ty::type_is_fp(ty); - let signed = ty::type_is_signed(ty); + let (te1, ty) = const_expr(cx, &**e1, param_substs); + let is_simd = ty::type_is_simd(cx.tcx(), ty); + let intype = if is_simd { + ty::simd_type(cx.tcx(), ty) + } else { + ty + }; + let is_float = ty::type_is_fp(intype); + let signed = ty::type_is_signed(intype); + + let (te2, _) = const_expr(cx, &**e2, param_substs); + let te2 = base::cast_shift_const_rhs(b, te1, te2); + return match b.node { ast::BiAdd => { if is_float { llvm::LLVMConstFAdd(te1, te2) } @@ -345,52 +419,30 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { if signed { llvm::LLVMConstAShr(te1, te2) } else { llvm::LLVMConstLShr(te1, te2) } } - ast::BiEq => { - if is_float { ConstFCmp(RealOEQ, te1, te2) } - else { ConstICmp(IntEQ, te1, te2) } - }, - ast::BiLt => { - if is_float { ConstFCmp(RealOLT, te1, te2) } - else { - if signed { ConstICmp(IntSLT, te1, te2) } - else { ConstICmp(IntULT, te1, te2) } + ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGt | ast::BiGe => { + if is_float { + let cmp = base::bin_op_to_fcmp_predicate(cx, b.node); + ConstFCmp(cmp, te1, te2) + } else { + let cmp = base::bin_op_to_icmp_predicate(cx, b.node, signed); + let bool_val = ConstICmp(cmp, te1, te2); + if is_simd { + // LLVM outputs an `< size x i1 >`, so we need to perform + // a sign extension to get the correctly sized type. + llvm::LLVMConstIntCast(bool_val, val_ty(te1).to_ref(), True) + } else { + bool_val + } } - }, - ast::BiLe => { - if is_float { ConstFCmp(RealOLE, te1, te2) } - else { - if signed { ConstICmp(IntSLE, te1, te2) } - else { ConstICmp(IntULE, te1, te2) } - } - }, - ast::BiNe => { - if is_float { ConstFCmp(RealONE, te1, te2) } - else { ConstICmp(IntNE, te1, te2) } - }, - ast::BiGe => { - if is_float { ConstFCmp(RealOGE, te1, te2) } - else { - if signed { ConstICmp(IntSGE, te1, te2) } - else { ConstICmp(IntUGE, te1, te2) } - } - }, - ast::BiGt => { - if is_float { ConstFCmp(RealOGT, te1, te2) } - else { - if signed { ConstICmp(IntSGT, te1, te2) } - else { ConstICmp(IntUGT, te1, te2) } - } - }, + } } }, ast::ExprUnary(u, ref e) => { - let (te, _) = const_expr(cx, &**e); - let ty = ty::expr_ty(cx.tcx(), &**e); + let (te, ty) = const_expr(cx, &**e, param_substs); let is_float = ty::type_is_fp(ty); return match u { ast::UnUniq | ast::UnDeref => { - let (dv, _dt) = const_deref(cx, te, ty, true); - dv + const_deref(cx, te, ty).0 } ast::UnNot => llvm::LLVMConstNot(te), ast::UnNeg => { @@ -400,7 +452,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { } } ast::ExprField(ref base, field) => { - let (bv, bt) = const_expr(cx, &**base); + let (bv, bt) = const_expr(cx, &**base, param_substs); let brepr = adt::represent_type(cx, bt); expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| { let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys); @@ -408,7 +460,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { }) } ast::ExprTupField(ref base, idx) => { - let (bv, bt) = const_expr(cx, &**base); + let (bv, bt) = const_expr(cx, &**base, param_substs); let brepr = adt::represent_type(cx, bt); expr::with_field_tys(cx.tcx(), bt, None, |discr, _| { adt::const_get_field(cx, &*brepr, bv, discr, idx.node) @@ -416,7 +468,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { } ast::ExprIndex(ref base, ref index) => { - let (bv, bt) = const_expr(cx, &**base); + let (bv, bt) = const_expr(cx, &**base, param_substs); let iv = match const_eval::eval_const_expr(cx.tcx(), &**index) { const_eval::const_int(i) => i as u64, const_eval::const_uint(u) => u, @@ -470,9 +522,11 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { const_get_elt(cx, arr, &[iv as c_uint]) } ast::ExprCast(ref base, _) => { - let ety = ty::expr_ty(cx.tcx(), e); let llty = type_of::type_of(cx, ety); - let (v, basety) = const_expr(cx, &**base); + let (v, basety) = const_expr(cx, &**base, param_substs); + if expr::cast_is_noop(basety, ety) { + return v; + } return match (expr::cast_type_kind(cx.tcx(), basety), expr::cast_type_kind(cx.tcx(), ety)) { @@ -523,7 +577,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { } } } - ast::ExprAddrOf(mutbl, ref sub) => { + ast::ExprAddrOf(ast::MutImmutable, ref sub) => { // If this is the address of some static, then we need to return // the actual address of the static itself (short circuit the rest // of const eval). @@ -531,41 +585,48 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { loop { match cur.node { ast::ExprParen(ref sub) => cur = sub, + ast::ExprBlock(ref blk) => { + if let Some(ref sub) = blk.expr { + cur = sub; + } else { + break; + } + } _ => break, } } let opt_def = cx.tcx().def_map.borrow().get(&cur.id).cloned(); if let Some(def::DefStatic(def_id, _)) = opt_def { - let ty = ty::expr_ty(cx.tcx(), e); - return get_static_val(cx, def_id, ty); + return get_static_val(cx, def_id, ety); } // If this isn't the address of a static, then keep going through // normal constant evaluation. - let (e, _) = const_expr(cx, &**sub); - const_addr_of(cx, e, mutbl) + let (v, _) = const_expr(cx, &**sub, param_substs); + addr_of(cx, v, "ref", e.id) + } + ast::ExprAddrOf(ast::MutMutable, ref sub) => { + let (v, _) = const_expr(cx, &**sub, param_substs); + addr_of_mut(cx, v, "ref_mut_slice", e.id) } ast::ExprTup(ref es) => { - let ety = ty::expr_ty(cx.tcx(), e); let repr = adt::represent_type(cx, ety); let vals = map_list(&es[]); adt::trans_const(cx, &*repr, 0, &vals[]) } ast::ExprStruct(_, ref fs, ref base_opt) => { - let ety = ty::expr_ty(cx.tcx(), e); let repr = adt::represent_type(cx, ety); - let tcx = cx.tcx(); let base_val = match *base_opt { - Some(ref base) => Some(const_expr(cx, &**base)), + Some(ref base) => Some(const_expr(cx, &**base, param_substs)), None => None }; - expr::with_field_tys(tcx, ety, Some(e.id), |discr, field_tys| { + expr::with_field_tys(cx.tcx(), ety, Some(e.id), |discr, field_tys| { let cs = field_tys.iter().enumerate() .map(|(ix, &field_ty)| { match fs.iter().find(|f| field_ty.name == f.ident.node.name) { - Some(ref f) => const_expr(cx, &*f.expr).0, + Some(ref f) => const_expr(cx, &*f.expr, param_substs).0, None => { match base_val { Some((bv, _)) => { @@ -580,23 +641,36 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { } } }).collect::>(); - adt::trans_const(cx, &*repr, discr, &cs[]) + if ty::type_is_simd(cx.tcx(), ety) { + C_vector(&cs[]) + } else { + adt::trans_const(cx, &*repr, discr, &cs[]) + } }) } ast::ExprVec(ref es) => { - const_vec(cx, e, es).0 + let unit_ty = ty::sequence_element_type(cx.tcx(), ety); + let llunitty = type_of::type_of(cx, unit_ty); + let vs = es.iter().map(|e| const_expr(cx, &**e, param_substs).0) + .collect::>(); + // If the vector contains enums, an LLVM array won't work. + if vs.iter().any(|vi| val_ty(*vi) != llunitty) { + C_struct(cx, &vs[], false) + } else { + C_array(llunitty, &vs[]) + } } ast::ExprRepeat(ref elem, ref count) => { - let vec_ty = ty::expr_ty(cx.tcx(), e); - let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); + let unit_ty = ty::sequence_element_type(cx.tcx(), ety); let llunitty = type_of::type_of(cx, unit_ty); let n = match const_eval::eval_const_expr(cx.tcx(), &**count) { const_eval::const_int(i) => i as uint, const_eval::const_uint(i) => i as uint, _ => cx.sess().span_bug(count.span, "count must be integral const expression.") }; - let vs: Vec<_> = repeat(const_expr(cx, &**elem).0).take(n).collect(); - if vs.iter().any(|vi| val_ty(*vi) != llunitty) { + let unit_val = const_expr(cx, &**elem, param_substs).0; + let vs: Vec<_> = repeat(unit_val).take(n).collect(); + if val_ty(unit_val) != llunitty { C_struct(cx, &vs[], false) } else { C_array(llunitty, &vs[]) @@ -606,10 +680,10 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { let def = cx.tcx().def_map.borrow()[e.id]; match def { def::DefFn(..) | def::DefStaticMethod(..) | def::DefMethod(..) => { - expr::trans_def_fn_unadjusted(cx, e, def, &Substs::trans_empty()).val + expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val } def::DefConst(def_id) => { - get_const_val(cx, def_id) + const_deref_ptr(cx, get_const_val(cx, def_id, e)) } def::DefVariant(enum_did, variant_did, _) => { let vinfo = ty::enum_variant_with_id(cx.tcx(), @@ -617,19 +691,17 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { variant_did); if vinfo.args.len() > 0 { // N-ary variant. - expr::trans_def_fn_unadjusted(cx, e, def, &Substs::trans_empty()).val + expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val } else { // Nullary variant. - let ety = ty::expr_ty(cx.tcx(), e); let repr = adt::represent_type(cx, ety); adt::trans_const(cx, &*repr, vinfo.disr_val, &[]) } } def::DefStruct(_) => { - let ety = ty::expr_ty(cx.tcx(), e); if let ty::ty_bare_fn(..) = ety.sty { // Tuple struct. - expr::trans_def_fn_unadjusted(cx, e, def, &Substs::trans_empty()).val + expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val } else { // Unit struct. C_null(type_of::type_of(cx, ety)) @@ -643,20 +715,21 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { } ast::ExprCall(ref callee, ref args) => { let opt_def = cx.tcx().def_map.borrow().get(&callee.id).cloned(); + let arg_vals = map_list(&args[]); match opt_def { Some(def::DefStruct(_)) => { - let ety = ty::expr_ty(cx.tcx(), e); - let repr = adt::represent_type(cx, ety); - let arg_vals = map_list(&args[]); - adt::trans_const(cx, &*repr, 0, &arg_vals[]) + if ty::type_is_simd(cx.tcx(), ety) { + C_vector(&arg_vals[]) + } else { + let repr = adt::represent_type(cx, ety); + adt::trans_const(cx, &*repr, 0, &arg_vals[]) + } } Some(def::DefVariant(enum_did, variant_did, _)) => { - let ety = ty::expr_ty(cx.tcx(), e); let repr = adt::represent_type(cx, ety); let vinfo = ty::enum_variant_with_id(cx.tcx(), enum_did, variant_did); - let arg_vals = map_list(&args[]); adt::trans_const(cx, &*repr, vinfo.disr_val, @@ -665,13 +738,19 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { _ => cx.sess().span_bug(e.span, "expected a struct or variant def") } } - ast::ExprParen(ref e) => const_expr(cx, &**e).0, + ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs).0, ast::ExprBlock(ref block) => { match block.expr { - Some(ref expr) => const_expr(cx, &**expr).0, + Some(ref expr) => const_expr(cx, &**expr, param_substs).0, None => C_nil(cx) } } + ast::ExprClosure(_, ref decl, ref body) => { + closure::trans_closure_expr(closure::Dest::Ignore(cx), + &**decl, &**body, e.id, + param_substs); + C_null(type_of::type_of(cx, ety)) + } _ => cx.sess().span_bug(e.span, "bad constant expression type in consts::const_expr") }; diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 27dba9c4809..96506291b5a 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -22,6 +22,7 @@ use trans::common::{ExternMap,tydesc_info,BuilderRef_res}; use trans::debuginfo; use trans::monomorphize::MonoId; use trans::type_::{Type, TypeNames}; +use middle::subst::Substs; use middle::ty::{self, Ty}; use session::config::NoDebugInfo; use session::Session; @@ -105,17 +106,20 @@ pub struct LocalCrateContext<'tcx> { const_cstr_cache: RefCell>, /// Reverse-direction for const ptrs cast from globals. - /// Key is an int, cast from a ValueRef holding a *T, + /// Key is a ValueRef holding a *T, /// Val is a ValueRef holding a *[T]. /// /// Needed because LLVM loses pointer->pointee association /// when we ptrcast, and we have to ptrcast during translation - /// of a [T] const because we form a slice, a [*T,int] pair, not - /// a pointer to an LLVM array type. - const_globals: RefCell>, + /// of a [T] const because we form a slice, a (*T,usize) pair, not + /// a pointer to an LLVM array type. Similar for trait objects. + const_unsized: RefCell>, + + /// Cache of emitted const globals (value -> global) + const_globals: RefCell>, /// Cache of emitted const values - const_values: RefCell>, + const_values: RefCell), ValueRef>>, /// Cache of emitted static values static_values: RefCell>, @@ -400,8 +404,9 @@ impl<'tcx> LocalCrateContext<'tcx> { monomorphizing: RefCell::new(DefIdMap()), vtables: RefCell::new(FnvHashMap()), const_cstr_cache: RefCell::new(FnvHashMap()), + const_unsized: RefCell::new(FnvHashMap()), const_globals: RefCell::new(FnvHashMap()), - const_values: RefCell::new(NodeMap()), + const_values: RefCell::new(FnvHashMap()), static_values: RefCell::new(NodeMap()), extern_const_values: RefCell::new(DefIdMap()), impl_method_cache: RefCell::new(FnvHashMap()), @@ -615,11 +620,16 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local.const_cstr_cache } - pub fn const_globals<'a>(&'a self) -> &'a RefCell> { + pub fn const_unsized<'a>(&'a self) -> &'a RefCell> { + &self.local.const_unsized + } + + pub fn const_globals<'a>(&'a self) -> &'a RefCell> { &self.local.const_globals } - pub fn const_values<'a>(&'a self) -> &'a RefCell> { + pub fn const_values<'a>(&'a self) -> &'a RefCell), + ValueRef>> { &self.local.const_values } diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs index 8004726d25e..ea5eef48257 100644 --- a/src/librustc_trans/trans/controlflow.rs +++ b/src/librustc_trans/trans/controlflow.rs @@ -372,7 +372,8 @@ pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let filename = C_str_slice(ccx, filename); let line = C_uint(ccx, loc.line); let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false); - let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const, ast::MutImmutable); + let expr_file_line = consts::addr_of(ccx, expr_file_line_const, + "panic_loc", call_info.id); let args = vec!(expr_file_line); let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem); let bcx = callee::trans_lang_call(bcx, @@ -400,7 +401,8 @@ pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let filename = C_str_slice(ccx, filename); let line = C_uint(ccx, loc.line); let file_line_const = C_struct(ccx, &[filename, line], false); - let file_line = consts::const_addr_of(ccx, file_line_const, ast::MutImmutable); + let file_line = consts::addr_of(ccx, file_line_const, + "panic_bounds_check_loc", call_info.id); let args = vec!(file_line, index, len); let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem); let bcx = callee::trans_lang_call(bcx, diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 44d8457c316..480679f43cb 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -54,6 +54,7 @@ use self::lazy_binop_ty::*; use back::abi; use llvm::{self, ValueRef}; +use middle::check_const; use middle::def; use middle::mem_categorization::Typer; use middle::subst::{self, Substs}; @@ -68,7 +69,6 @@ use trans::glue; use trans::machine; use trans::meth; use trans::monomorphize; -use trans::inline; use trans::tvec; use trans::type_of; use middle::ty::{struct_fields, tup_fields}; @@ -84,8 +84,9 @@ use trans::type_::Type; use syntax::{ast, ast_util, codemap}; use syntax::ptr::P; use syntax::parse::token; -use std::rc::Rc; use std::iter::repeat; +use std::mem; +use std::rc::Rc; // Destinations @@ -115,11 +116,56 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, -> Block<'blk, 'tcx> { let mut bcx = bcx; + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + if bcx.tcx().adjustments.borrow().contains_key(&expr.id) { // use trans, which may be less efficient but // which will perform the adjustments: let datum = unpack_datum!(bcx, trans(bcx, expr)); - return datum.store_to_dest(bcx, dest, expr.id) + return datum.store_to_dest(bcx, dest, expr.id); + } + + let qualif = bcx.tcx().const_qualif_map.borrow()[expr.id]; + if !qualif.intersects(check_const::NOT_CONST | check_const::NEEDS_DROP) { + if !qualif.intersects(check_const::PREFER_IN_PLACE) { + if let SaveIn(lldest) = dest { + let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, + bcx.fcx.param_substs); + // Cast pointer to destination, because constants + // have different types. + let lldest = PointerCast(bcx, lldest, val_ty(global)); + memcpy_ty(bcx, lldest, global, expr_ty_adjusted(bcx, expr)); + } + // Don't do anything in the Ignore case, consts don't need drop. + return bcx; + } else { + // The only way we're going to see a `const` at this point is if + // it prefers in-place instantiation, likely because it contains + // `[x; N]` somewhere within. + match expr.node { + ast::ExprPath(_) | ast::ExprQPath(_) => { + match bcx.def(expr.id) { + def::DefConst(did) => { + let expr = consts::get_const_expr(bcx.ccx(), did, expr); + // Temporarily get cleanup scopes out of the way, + // as they require sub-expressions to be contained + // inside the current AST scope. + // These should record no cleanups anyways, `const` + // can't have destructors. + let scopes = mem::replace(&mut *bcx.fcx.scopes.borrow_mut(), + vec![]); + bcx = trans_into(bcx, expr, dest); + let scopes = mem::replace(&mut *bcx.fcx.scopes.borrow_mut(), + scopes); + assert!(scopes.is_empty()); + return bcx; + } + _ => {} + } + } + _ => {} + } + } } debug!("trans_into() expr={}", expr.repr(bcx.tcx())); @@ -130,7 +176,6 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, false); bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc); - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); let kind = ty::expr_kind(bcx.tcx(), expr); bcx = match kind { ty::LvalueExpr | ty::RvalueDatumExpr => { @@ -157,14 +202,70 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let mut bcx = bcx; let fcx = bcx.fcx; + let qualif = bcx.tcx().const_qualif_map.borrow()[expr.id]; + let adjusted_global = !qualif.intersects(check_const::NON_STATIC_BORROWS); + let global = if !qualif.intersects(check_const::NOT_CONST | check_const::NEEDS_DROP) { + let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, + bcx.fcx.param_substs); + + if qualif.intersects(check_const::HAS_STATIC_BORROWS) { + // Is borrowed as 'static, must return lvalue. + + // Cast pointer to global, because constants have different types. + let const_ty = expr_ty_adjusted(bcx, expr); + let llty = type_of::type_of(bcx.ccx(), const_ty); + let global = PointerCast(bcx, global, llty.ptr_to()); + let datum = Datum::new(global, const_ty, Lvalue); + return DatumBlock::new(bcx, datum.to_expr_datum()); + } + + // Otherwise, keep around and perform adjustments, if needed. + let const_ty = if adjusted_global { + expr_ty_adjusted(bcx, expr) + } else { + expr_ty(bcx, expr) + }; + + // This could use a better heuristic. + Some(if type_is_immediate(bcx.ccx(), const_ty) { + // Cast pointer to global, because constants have different types. + let llty = type_of::type_of(bcx.ccx(), const_ty); + let global = PointerCast(bcx, global, llty.ptr_to()); + // Maybe just get the value directly, instead of loading it? + immediate_rvalue(load_ty(bcx, global, const_ty), const_ty) + } else { + let llty = type_of::type_of(bcx.ccx(), const_ty); + // HACK(eddyb) get around issues with lifetime intrinsics. + let scratch = alloca_no_lifetime(bcx, llty, "const"); + let lldest = if !ty::type_is_structural(const_ty) { + // Cast pointer to slot, because constants have different types. + PointerCast(bcx, scratch, val_ty(global)) + } else { + // In this case, memcpy_ty calls llvm.memcpy after casting both + // source and destination to i8*, so we don't need any casts. + scratch + }; + memcpy_ty(bcx, lldest, global, const_ty); + Datum::new(scratch, const_ty, Rvalue::new(ByRef)) + }) + } else { + None + }; let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), expr.id, expr.span, false); fcx.push_ast_cleanup_scope(cleanup_debug_loc); - let datum = unpack_datum!(bcx, trans_unadjusted(bcx, expr)); - let datum = unpack_datum!(bcx, apply_adjustments(bcx, expr, datum)); + let datum = match global { + Some(rvalue) => rvalue.to_expr_datum(), + None => unpack_datum!(bcx, trans_unadjusted(bcx, expr)) + }; + let datum = if adjusted_global { + datum // trans::consts already performed adjustments. + } else { + unpack_datum!(bcx, apply_adjustments(bcx, expr, datum)) + }; bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id); return DatumBlock::new(bcx, datum); } @@ -177,6 +278,54 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef { GEPi(bcx, fat_ptr, &[0, abi::FAT_PTR_ADDR]) } +// Retrieve the information we are losing (making dynamic) in an unsizing +// adjustment. +// When making a dtor, we need to do different things depending on the +// ownership of the object.. mk_ty is a function for turning `unadjusted_ty` +// into a type to be destructed. If we want to end up with a Box pointer, +// then mk_ty should make a Box pointer (T -> Box), if we want a +// borrowed reference then it should be T -> &T. +pub fn unsized_info<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>, + kind: &ty::UnsizeKind<'tcx>, + id: ast::NodeId, + unadjusted_ty: Ty<'tcx>, + param_substs: &'tcx subst::Substs<'tcx>, + mk_ty: F) -> ValueRef where + F: FnOnce(Ty<'tcx>) -> Ty<'tcx>, +{ + // FIXME(#19596) workaround: `|t| t` causes monomorphization recursion + fn identity(t: T) -> T { t } + + debug!("unsized_info(kind={:?}, id={}, unadjusted_ty={})", + kind, id, unadjusted_ty.repr(ccx.tcx())); + match kind { + &ty::UnsizeLength(len) => C_uint(ccx, len), + &ty::UnsizeStruct(box ref k, tp_index) => match unadjusted_ty.sty { + ty::ty_struct(_, ref substs) => { + let ty_substs = substs.types.get_slice(subst::TypeSpace); + // The dtor for a field treats it like a value, so mk_ty + // should just be the identity function. + unsized_info(ccx, k, id, ty_substs[tp_index], param_substs, identity) + } + _ => ccx.sess().bug(&format!("UnsizeStruct with bad sty: {}", + unadjusted_ty.repr(ccx.tcx()))[]) + }, + &ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => { + // Note that we preserve binding levels here: + let substs = principal.0.substs.with_self_ty(unadjusted_ty).erase_regions(); + let substs = ccx.tcx().mk_substs(substs); + let trait_ref = ty::Binder(Rc::new(ty::TraitRef { def_id: principal.def_id(), + substs: substs })); + let trait_ref = monomorphize::apply_param_substs(ccx.tcx(), + param_substs, + &trait_ref); + let box_ty = mk_ty(unadjusted_ty); + consts::ptrcast(meth::get_vtable(ccx, box_ty, trait_ref, param_substs), + Type::vtable_ptr(ccx)) + } + } +} + /// Helper for trans that apply adjustments from `expr` to `datum`, which should be the unadjusted /// translation of `expr`. fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, @@ -262,13 +411,17 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let datum = match autoref { &AutoPtr(_, _, ref a) | &AutoUnsafe(_, ref a) => { debug!(" AutoPtr"); - match a { - &Some(box ref a) => { - datum = unpack_datum!(bcx, apply_autoref(a, bcx, expr, datum)); - } - &None => {} + if let &Some(box ref a) = a { + datum = unpack_datum!(bcx, apply_autoref(a, bcx, expr, datum)); + } + if !type_is_sized(bcx.tcx(), datum.ty) { + // Arrange cleanup + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "ref_fat_ptr", expr.id)); + unpack_datum!(bcx, ref_fat_ptr(bcx, lval)) + } else { + unpack_datum!(bcx, auto_ref(bcx, datum, expr)) } - unpack_datum!(bcx, ref_ptr(bcx, expr, datum)) } &ty::AutoUnsize(ref k) => { debug!(" AutoUnsize"); @@ -288,139 +441,35 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, DatumBlock::new(bcx, datum) } - fn ref_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum<'tcx, Expr>) - -> DatumBlock<'blk, 'tcx, Expr> { - debug!("ref_ptr(expr={}, datum={})", - expr.repr(bcx.tcx()), - datum.to_string(bcx.ccx())); - - if !type_is_sized(bcx.tcx(), datum.ty) { - debug!("Taking address of unsized type {}", - bcx.ty_to_string(datum.ty)); - ref_fat_ptr(bcx, expr, datum) - } else { - debug!("Taking address of sized type {}", - bcx.ty_to_string(datum.ty)); - auto_ref(bcx, datum, expr) - } - } - - // Retrieve the information we are losing (making dynamic) in an unsizing - // adjustment. - // When making a dtor, we need to do different things depending on the - // ownership of the object.. mk_ty is a function for turning `unadjusted_ty` - // into a type to be destructed. If we want to end up with a Box pointer, - // then mk_ty should make a Box pointer (T -> Box), if we want a - // borrowed reference then it should be T -> &T. - fn unsized_info<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, - kind: &ty::UnsizeKind<'tcx>, - id: ast::NodeId, - unadjusted_ty: Ty<'tcx>, - mk_ty: F) -> ValueRef where - F: FnOnce(Ty<'tcx>) -> Ty<'tcx>, - { - // FIXME(#19596) workaround: `|t| t` causes monomorphization recursion - fn identity(t: T) -> T { t } - - debug!("unsized_info(kind={:?}, id={}, unadjusted_ty={})", - kind, id, unadjusted_ty.repr(bcx.tcx())); - match kind { - &ty::UnsizeLength(len) => C_uint(bcx.ccx(), len), - &ty::UnsizeStruct(box ref k, tp_index) => match unadjusted_ty.sty { - ty::ty_struct(_, ref substs) => { - let ty_substs = substs.types.get_slice(subst::TypeSpace); - // The dtor for a field treats it like a value, so mk_ty - // should just be the identity function. - unsized_info(bcx, k, id, ty_substs[tp_index], identity) - } - _ => bcx.sess().bug(&format!("UnsizeStruct with bad sty: {}", - bcx.ty_to_string(unadjusted_ty))[]) - }, - &ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => { - // Note that we preserve binding levels here: - let substs = principal.0.substs.with_self_ty(unadjusted_ty).erase_regions(); - let substs = bcx.tcx().mk_substs(substs); - let trait_ref = - ty::Binder(Rc::new(ty::TraitRef { def_id: principal.def_id(), - substs: substs })); - let trait_ref = bcx.monomorphize(&trait_ref); - let box_ty = mk_ty(unadjusted_ty); - PointerCast(bcx, - meth::get_vtable(bcx, box_ty, trait_ref), - Type::vtable_ptr(bcx.ccx())) - } - } - } - fn unsize_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr, datum: Datum<'tcx, Expr>, k: &ty::UnsizeKind<'tcx>) -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; let tcx = bcx.tcx(); let datum_ty = datum.ty; let unsized_ty = ty::unsize_ty(tcx, datum_ty, k, expr.span); debug!("unsized_ty={}", unsized_ty.repr(bcx.tcx())); let dest_ty = ty::mk_open(tcx, unsized_ty); debug!("dest_ty={}", unsized_ty.repr(bcx.tcx())); - // Closures for extracting and manipulating the data and payload parts of - // the fat pointer. - let info = |bcx, _val| unsized_info(bcx, - k, - expr.id, - datum_ty, - |t| ty::mk_rptr(tcx, - tcx.mk_region(ty::ReStatic), - ty::mt{ - ty: t, - mutbl: ast::MutImmutable - })); - match *k { - ty::UnsizeStruct(..) => - into_fat_ptr(bcx, expr, datum, dest_ty, |bcx, val| { - PointerCast(bcx, val, type_of::type_of(bcx.ccx(), unsized_ty).ptr_to()) - }, info), - ty::UnsizeLength(..) => - into_fat_ptr(bcx, expr, datum, dest_ty, |bcx, val| { - GEPi(bcx, val, &[0, 0]) - }, info), - ty::UnsizeVtable(..) => - into_fat_ptr(bcx, expr, datum, dest_ty, |_bcx, val| { - PointerCast(bcx, val, Type::i8p(bcx.ccx())) - }, info), - } - } - fn ref_fat_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum<'tcx, Expr>) - -> DatumBlock<'blk, 'tcx, Expr> { - let tcx = bcx.tcx(); - let dest_ty = ty::close_type(tcx, datum.ty); - let base = |bcx, val| Load(bcx, get_dataptr(bcx, val)); - let len = |bcx, val| Load(bcx, get_len(bcx, val)); - into_fat_ptr(bcx, expr, datum, dest_ty, base, len) - } - - fn into_fat_ptr<'blk, 'tcx, F, G>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum<'tcx, Expr>, - dest_ty: Ty<'tcx>, - base: F, - info: G) - -> DatumBlock<'blk, 'tcx, Expr> where - F: FnOnce(Block<'blk, 'tcx>, ValueRef) -> ValueRef, - G: FnOnce(Block<'blk, 'tcx>, ValueRef) -> ValueRef, - { - let mut bcx = bcx; + let info = unsized_info(bcx.ccx(), k, expr.id, datum_ty, bcx.fcx.param_substs, + |t| ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), t)); // Arrange cleanup let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id)); - let base = base(bcx, lval.val); - let info = info(bcx, lval.val); + // Compute the base pointer. This doesn't change the pointer value, + // but merely its type. + let base = match *k { + ty::UnsizeStruct(..) | ty::UnsizeVtable(..) => { + PointerCast(bcx, lval.val, type_of::type_of(bcx.ccx(), unsized_ty).ptr_to()) + } + ty::UnsizeLength(..) => { + GEPi(bcx, lval.val, &[0u, 0u]) + } + }; let scratch = rvalue_scratch_datum(bcx, dest_ty, "__fat_ptr"); Store(bcx, base, get_dataptr(bcx, scratch.val)); @@ -490,7 +539,8 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to()); bcx = datum.store_to(bcx, base); - let info = unsized_info(bcx, k, expr.id, unboxed_ty, |t| ty::mk_uniq(tcx, t)); + let info = unsized_info(bcx.ccx(), k, expr.id, unboxed_ty, bcx.fcx.param_substs, + |t| ty::mk_uniq(tcx, t)); Store(bcx, info, get_len(bcx, scratch.val)); DatumBlock::new(bcx, scratch.to_expr_datum()) @@ -847,53 +897,25 @@ fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // pointer to that. let const_ty = expr_ty(bcx, ref_expr); - fn get_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, did: ast::DefId, - const_ty: Ty<'tcx>) -> ValueRef { - // For external constants, we don't inline. - if did.krate == ast::LOCAL_CRATE { - // Case 1. + // For external constants, we don't inline. + let val = if did.krate == ast::LOCAL_CRATE { + // Case 1. - // The LLVM global has the type of its initializer, - // which may not be equal to the enum's type for - // non-C-like enums. - let val = base::get_item_val(bcx.ccx(), did.node); - let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); - PointerCast(bcx, val, pty) - } else { - // Case 2. - base::get_extern_const(bcx.ccx(), did, const_ty) - } - } - let val = get_val(bcx, did, const_ty); + // The LLVM global has the type of its initializer, + // which may not be equal to the enum's type for + // non-C-like enums. + let val = base::get_item_val(bcx.ccx(), did.node); + let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); + PointerCast(bcx, val, pty) + } else { + // Case 2. + base::get_extern_const(bcx.ccx(), did, const_ty) + }; DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr)) } - def::DefConst(did) => { - // First, inline any external constants into the local crate so we - // can be sure to get the LLVM value corresponding to it. - let did = inline::maybe_instantiate_inline(bcx.ccx(), did); - if did.krate != ast::LOCAL_CRATE { - bcx.tcx().sess.span_bug(ref_expr.span, - "cross crate constant could not \ - be inlined"); - } - let val = base::get_item_val(bcx.ccx(), did.node); - - // Next, we need to crate a ByRef rvalue datum to return. We can't - // use the normal .to_ref_datum() function because the type of - // `val` is not actually the same as `const_ty`. - // - // To get around this, we make a custom alloca slot with the - // appropriate type (const_ty), and then we cast it to a pointer of - // typeof(val), store the value, and then hand this slot over to - // the datum infrastructure. - let const_ty = expr_ty(bcx, ref_expr); - let llty = type_of::type_of(bcx.ccx(), const_ty); - let slot = alloca(bcx, llty, "const"); - let pty = Type::from_ref(unsafe { llvm::LLVMTypeOf(val) }).ptr_to(); - Store(bcx, val, PointerCast(bcx, slot, pty)); - - let datum = Datum::new(slot, const_ty, Rvalue::new(ByRef)); - DatumBlock::new(bcx, datum.to_expr_datum()) + def::DefConst(_) => { + bcx.sess().span_bug(ref_expr.span, + "constant expression should not reach expr::trans_def") } _ => { DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum()) @@ -1119,7 +1141,12 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, tvec::trans_fixed_vstore(bcx, expr, dest) } ast::ExprClosure(_, ref decl, ref body) => { - closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest) + let dest = match dest { + SaveIn(lldest) => closure::Dest::SaveIn(bcx, lldest), + Ignore => closure::Dest::Ignore(bcx.ccx()) + }; + closure::trans_closure_expr(dest, &**decl, &**body, expr.id, bcx.fcx.param_substs) + .unwrap_or(bcx) } ast::ExprCall(ref f, ref args) => { if bcx.tcx().is_method_call(expr.id) { @@ -1247,7 +1274,7 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_def_fn_unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ref_expr: &ast::Expr, def: def::Def, - param_substs: &subst::Substs<'tcx>) + param_substs: &'tcx subst::Substs<'tcx>) -> Datum<'tcx, Rvalue> { let _icx = push_ctxt("trans_def_datum_unadjusted"); @@ -1641,6 +1668,16 @@ fn trans_uniq_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, immediate_rvalue_bcx(bcx, val, box_ty).to_expr_datumblock() } +fn ref_fat_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lval: Datum<'tcx, Lvalue>) + -> DatumBlock<'blk, 'tcx, Expr> { + let dest_ty = ty::close_type(bcx.tcx(), lval.ty); + let scratch = rvalue_scratch_datum(bcx, dest_ty, "__fat_ptr"); + memcpy_ty(bcx, scratch.val, lval.val, scratch.ty); + + DatumBlock::new(bcx, scratch.to_expr_datum()) +} + fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr, subexpr: &ast::Expr) @@ -1651,18 +1688,7 @@ fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, match sub_datum.ty.sty { ty::ty_open(_) => { // Opened DST value, close to a fat pointer - debug!("Closing fat pointer {}", bcx.ty_to_string(sub_datum.ty)); - - let scratch = rvalue_scratch_datum(bcx, - ty::close_type(bcx.tcx(), sub_datum.ty), - "fat_addr_of"); - let base = Load(bcx, get_dataptr(bcx, sub_datum.val)); - Store(bcx, base, get_dataptr(bcx, scratch.val)); - - let len = Load(bcx, get_len(bcx, sub_datum.val)); - Store(bcx, len, get_len(bcx, scratch.val)); - - DatumBlock::new(bcx, scratch.to_expr_datum()) + ref_fat_ptr(bcx, sub_datum) } _ => { // Sized value, ref to a thin pointer @@ -1687,9 +1713,10 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let tcx = bcx.tcx(); let is_simd = ty::type_is_simd(tcx, lhs_t); - let intype = { - if is_simd { ty::simd_type(tcx, lhs_t) } - else { lhs_t } + let intype = if is_simd { + ty::simd_type(tcx, lhs_t) + } else { + lhs_t }; let is_float = ty::type_is_fp(intype); let is_signed = ty::type_is_signed(intype); @@ -1766,24 +1793,10 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { - if ty::type_is_scalar(rhs_t) { - unpack_result!(bcx, - base::compare_scalar_types(bcx, - lhs, - rhs, - rhs_t, - op.node, - binop_debug_loc)) - } else if is_simd { - base::compare_simd_types(bcx, - lhs, - rhs, - intype, - ty::simd_size(tcx, lhs_t), - op.node, - binop_debug_loc) + if is_simd { + base::compare_simd_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc) } else { - bcx.tcx().sess.span_bug(binop_expr.span, "comparison operator unsupported for type") + base::compare_scalar_types(bcx, lhs, rhs, intype, op.node, binop_debug_loc) } } _ => { @@ -1997,7 +2010,7 @@ pub fn cast_type_kind<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> cast_kind { } } -fn cast_is_noop<'tcx>(t_in: Ty<'tcx>, t_out: Ty<'tcx>) -> bool { +pub fn cast_is_noop<'tcx>(t_in: Ty<'tcx>, t_out: Ty<'tcx>) -> bool { match (ty::deref(t_in, true), ty::deref(t_out, true)) { (Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => { t_in == t_out diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 96bd028aa9d..8f0e4e647b5 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -557,7 +557,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, body: &ast::Block, attrs: &[ast::Attribute], llwrapfn: ValueRef, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, id: ast::NodeId, hash: Option<&str>) { let _icx = push_ctxt("foreign::build_foreign_fn"); @@ -577,7 +577,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, decl: &ast::FnDecl, body: &ast::Block, - param_substs: &Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>, attrs: &[ast::Attribute], id: ast::NodeId, hash: Option<&str>) diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 8cf9a51b3bf..af90e1ec5c5 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -558,12 +558,12 @@ fn make_generic_glue<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>, let glue_name = format!("glue {} {}", name, ty_to_short_str(ccx.tcx(), t)); let _s = StatRecorder::new(ccx, glue_name); - let empty_param_substs = Substs::trans_empty(); + let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); let (arena, fcx): (TypedArena<_>, FunctionContext); arena = TypedArena::new(); fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, ty::FnConverging(ty::mk_nil(ccx.tcx())), - &empty_param_substs, None, &arena); + empty_substs, None, &arena); let bcx = init_function(&fcx, false, ty::FnConverging(ty::mk_nil(ccx.tcx()))); diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs index ea6d9b88e11..56fda20e0e8 100644 --- a/src/librustc_trans/trans/inline.rs +++ b/src/librustc_trans/trans/inline.rs @@ -159,13 +159,14 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) let unparameterized = impl_tpt.generics.types.is_empty() && mth.pe_generics().ty_params.is_empty(); + let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); if unparameterized { let llfn = get_item_val(ccx, mth.id); trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), llfn, - &Substs::trans_empty(), + empty_substs, mth.id, &[]); // Use InternalLinkage so LLVM can optimize more diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 5161382a927..1d5d24a6403 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -11,7 +11,7 @@ use arena::TypedArena; use back::abi; use back::link; -use llvm::{self, ValueRef, get_param}; +use llvm::{ValueRef, get_param}; use metadata::csearch; use middle::subst::Substs; use middle::subst::VecPerParamSpace; @@ -23,6 +23,7 @@ use trans::callee::*; use trans::callee; use trans::cleanup; use trans::common::*; +use trans::consts; use trans::datum::*; use trans::debuginfo::DebugLoc; use trans::expr::{SaveIn, Ignore}; @@ -36,7 +37,6 @@ use middle::ty::{self, Ty}; use middle::ty::MethodCall; use util::ppaux::Repr; -use std::ffi::CString; use std::rc::Rc; use syntax::abi::{Rust, RustCall}; use syntax::parse::token; @@ -82,11 +82,12 @@ pub fn trans_impl(ccx: &CrateContext, let trans_everywhere = attr::requests_inline(&method.attrs[]); for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) { let llfn = get_item_val(ccx, method.id); + let empty_substs = tcx.mk_substs(Substs::trans_empty()); trans_fn(ccx, method.pe_fn_decl(), method.pe_body(), llfn, - &Substs::trans_empty(), + empty_substs, method.id, &[]); update_linkage(ccx, @@ -174,7 +175,7 @@ pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, method_id: ast::DefId, trait_id: ast::DefId, expr_id: ast::NodeId, - param_substs: &subst::Substs<'tcx>) + param_substs: &'tcx subst::Substs<'tcx>) -> Datum<'tcx, Rvalue> { let _icx = push_ctxt("meth::trans_static_method_callee"); @@ -599,7 +600,7 @@ pub fn trans_object_shim<'a, 'tcx>( let sig = ty::erase_late_bound_regions(ccx.tcx(), &fty.sig); - let empty_substs = Substs::trans_empty(); + let empty_substs = tcx.mk_substs(Substs::trans_empty()); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); fcx = new_fn_ctxt(ccx, @@ -607,7 +608,7 @@ pub fn trans_object_shim<'a, 'tcx>( ast::DUMMY_NODE_ID, false, sig.output, - &empty_substs, + empty_substs, None, &block_arena); let mut bcx = init_function(&fcx, false, sig.output); @@ -689,19 +690,19 @@ pub fn trans_object_shim<'a, 'tcx>( /// `trait_ref` would map `T:Trait`, but `box_ty` would be /// `Foo`. This `box_ty` is primarily used to encode the destructor. /// This will hopefully change now that DST is underway. -pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - box_ty: Ty<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>) - -> ValueRef +pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + box_ty: Ty<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + param_substs: &'tcx subst::Substs<'tcx>) + -> ValueRef { - debug!("get_vtable(box_ty={}, trait_ref={})", - box_ty.repr(bcx.tcx()), - trait_ref.repr(bcx.tcx())); - - let tcx = bcx.tcx(); - let ccx = bcx.ccx(); + let tcx = ccx.tcx(); let _icx = push_ctxt("meth::get_vtable"); + debug!("get_vtable(box_ty={}, trait_ref={})", + box_ty.repr(tcx), + trait_ref.repr(tcx)); + // Check the cache. let cache_key = (box_ty, trait_ref.clone()); match ccx.vtables().borrow().get(&cache_key) { @@ -711,9 +712,7 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Not in the cache. Build it. let methods = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| { - let vtable = fulfill_obligation(bcx.ccx(), - DUMMY_SP, - trait_ref.clone()); + let vtable = fulfill_obligation(ccx, DUMMY_SP, trait_ref.clone()); match vtable { traits::VtableBuiltin(_) => { Vec::new().into_iter() @@ -723,83 +722,61 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, impl_def_id: id, substs, nested: _ }) => { - emit_vtable_methods(bcx, id, substs).into_iter() + emit_vtable_methods(ccx, id, substs, param_substs).into_iter() } traits::VtableClosure(closure_def_id, substs) => { let llfn = trans_fn_ref_with_substs( - bcx.ccx(), + ccx, closure_def_id, ExprId(0), - bcx.fcx.param_substs, - substs.clone()).val; + param_substs, + substs).val; - (vec!(llfn)).into_iter() + vec![llfn].into_iter() } traits::VtableFnPointer(bare_fn_ty) => { - let llfn = vec![trans_fn_pointer_shim(bcx.ccx(), bare_fn_ty)]; - llfn.into_iter() + vec![trans_fn_pointer_shim(ccx, bare_fn_ty)].into_iter() } traits::VtableObject(ref data) => { // this would imply that the Self type being erased is // an object type; this cannot happen because we // cannot cast an unsized type into a trait object - bcx.sess().bug( + tcx.sess.bug( &format!("cannot get vtable for an object type: {}", - data.repr(bcx.tcx()))); + data.repr(tcx))); } traits::VtableParam(..) => { - bcx.sess().bug( + tcx.sess.bug( &format!("resolved vtable for {} to bad vtable {} in trans", - trait_ref.repr(bcx.tcx()), - vtable.repr(bcx.tcx()))[]); + trait_ref.repr(tcx), + vtable.repr(tcx))[]); } } }); let size_ty = sizing_type_of(ccx, trait_ref.self_ty()); let size = machine::llsize_of_alloc(ccx, size_ty); - let ll_size = C_uint(ccx, size); let align = align_of(ccx, trait_ref.self_ty()); - let ll_align = C_uint(ccx, align); - // Generate a destructor for the vtable. - let drop_glue = glue::get_drop_glue(ccx, box_ty); - let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods); + let components: Vec<_> = vec![ + // Generate a destructor for the vtable. + glue::get_drop_glue(ccx, box_ty), + C_uint(ccx, size), + C_uint(ccx, align) + ].into_iter().chain(methods).collect(); + + let vtable = consts::addr_of(ccx, C_struct(ccx, &components, false), + "vtable", trait_ref.def_id().node); ccx.vtables().borrow_mut().insert(cache_key, vtable); vtable } -/// Helper function to declare and initialize the vtable. -pub fn make_vtable>(ccx: &CrateContext, - drop_glue: ValueRef, - size: ValueRef, - align: ValueRef, - ptrs: I) - -> ValueRef { - let _icx = push_ctxt("meth::make_vtable"); - - let head = vec![drop_glue, size, align]; - let components: Vec<_> = head.into_iter().chain(ptrs).collect(); - - unsafe { - let tbl = C_struct(ccx, &components[], false); - let sym = token::gensym("vtable"); - let buf = CString::from_vec(format!("vtable{}", sym.usize()).into_bytes()); - let vt_gvar = llvm::LLVMAddGlobal(ccx.llmod(), val_ty(tbl).to_ref(), - buf.as_ptr()); - llvm::LLVMSetInitializer(vt_gvar, tbl); - llvm::LLVMSetGlobalConstant(vt_gvar, llvm::True); - llvm::SetLinkage(vt_gvar, llvm::InternalLinkage); - vt_gvar - } -} - -fn emit_vtable_methods<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - impl_id: ast::DefId, - substs: subst::Substs<'tcx>) - -> Vec { - let ccx = bcx.ccx(); +fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + impl_id: ast::DefId, + substs: subst::Substs<'tcx>, + param_substs: &'tcx subst::Substs<'tcx>) + -> Vec { let tcx = ccx.tcx(); let trt_id = match ty::impl_trait_ref(tcx, impl_id) { @@ -808,7 +785,7 @@ fn emit_vtable_methods<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, make a vtable for a type impl!") }; - ty::populate_implementations_for_trait_if_necessary(bcx.tcx(), trt_id); + ty::populate_implementations_for_trait_if_necessary(tcx, trt_id); let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id); trait_item_def_ids.iter().flat_map(|method_def_id| { @@ -835,7 +812,7 @@ fn emit_vtable_methods<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ccx, m_id, ExprId(0), - bcx.fcx.param_substs, + param_substs, substs.clone()).val; // currently, at least, by-value self is not object safe @@ -882,7 +859,7 @@ pub fn trans_trait_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx = datum.store_to(bcx, llboxdest); // Store the vtable into the second half of pair. - let vtable = get_vtable(bcx, datum_ty, trait_ref); + let vtable = get_vtable(bcx.ccx(), datum_ty, trait_ref, bcx.fcx.param_substs); let llvtabledest = GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA]); let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to()); Store(bcx, vtable, llvtabledest); diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 5796e72a610..30797344da8 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -36,7 +36,7 @@ use std::hash::{Hasher, Hash, SipHasher}; pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_id: ast::DefId, - psubsts: &subst::Substs<'tcx>, + psubsts: &'tcx subst::Substs<'tcx>, ref_id: Option) -> (ValueRef, Ty<'tcx>, bool) { debug!("monomorphic_fn(\ @@ -55,7 +55,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let hash_id = MonoId { def: fn_id, - params: psubsts.types.clone() + params: &psubsts.types }; let item_ty = ty::lookup_item_type(ccx.tcx(), fn_id).ty; @@ -289,7 +289,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, #[derive(PartialEq, Eq, Hash, Debug)] pub struct MonoId<'tcx> { pub def: ast::DefId, - pub params: subst::VecPerParamSpace> + pub params: &'tcx subst::VecPerParamSpace> } /// Monomorphizes a type from the AST by first applying the in-scope diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs index edd4da4711f..8f10865ae32 100644 --- a/src/librustc_trans/trans/tvec.rs +++ b/src/librustc_trans/trans/tvec.rs @@ -309,8 +309,6 @@ pub fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, |set_bcx, lleltptr, _| { elem.shallow_copy(set_bcx, lleltptr) }); - - elem.add_clean_if_rvalue(bcx, element.id); bcx } } diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs new file mode 100644 index 00000000000..01bed69fb1d --- /dev/null +++ b/src/test/compile-fail/issue-8460-const.rs @@ -0,0 +1,55 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::{int, i8, i16, i32, i64}; +use std::thread::Thread; + +fn main() { + assert!(Thread::scoped(move|| int::MIN / -1).join().is_err()); + //~^ ERROR attempted to divide with overflow in a constant expression + assert!(Thread::scoped(move|| i8::MIN / -1).join().is_err()); + //~^ ERROR attempted to divide with overflow in a constant expression + assert!(Thread::scoped(move|| i16::MIN / -1).join().is_err()); + //~^ ERROR attempted to divide with overflow in a constant expression + assert!(Thread::scoped(move|| i32::MIN / -1).join().is_err()); + //~^ ERROR attempted to divide with overflow in a constant expression + assert!(Thread::scoped(move|| i64::MIN / -1).join().is_err()); + //~^ ERROR attempted to divide with overflow in a constant expression + assert!(Thread::scoped(move|| 1is / 0).join().is_err()); + //~^ ERROR attempted to divide by zero in a constant expression + assert!(Thread::scoped(move|| 1i8 / 0).join().is_err()); + //~^ ERROR attempted to divide by zero in a constant expression + assert!(Thread::scoped(move|| 1i16 / 0).join().is_err()); + //~^ ERROR attempted to divide by zero in a constant expression + assert!(Thread::scoped(move|| 1i32 / 0).join().is_err()); + //~^ ERROR attempted to divide by zero in a constant expression + assert!(Thread::scoped(move|| 1i64 / 0).join().is_err()); + //~^ ERROR attempted to divide by zero in a constant expression + assert!(Thread::scoped(move|| int::MIN % -1).join().is_err()); + //~^ ERROR attempted remainder with overflow in a constant expression + assert!(Thread::scoped(move|| i8::MIN % -1).join().is_err()); + //~^ ERROR attempted remainder with overflow in a constant expression + assert!(Thread::scoped(move|| i16::MIN % -1).join().is_err()); + //~^ ERROR attempted remainder with overflow in a constant expression + assert!(Thread::scoped(move|| i32::MIN % -1).join().is_err()); + //~^ ERROR attempted remainder with overflow in a constant expression + assert!(Thread::scoped(move|| i64::MIN % -1).join().is_err()); + //~^ ERROR attempted remainder with overflow in a constant expression + assert!(Thread::scoped(move|| 1is % 0).join().is_err()); + //~^ ERROR attempted remainder with a divisor of zero in a constant expression + assert!(Thread::scoped(move|| 1i8 % 0).join().is_err()); + //~^ ERROR attempted remainder with a divisor of zero in a constant expression + assert!(Thread::scoped(move|| 1i16 % 0).join().is_err()); + //~^ ERROR attempted remainder with a divisor of zero in a constant expression + assert!(Thread::scoped(move|| 1i32 % 0).join().is_err()); + //~^ ERROR attempted remainder with a divisor of zero in a constant expression + assert!(Thread::scoped(move|| 1i64 % 0).join().is_err()); + //~^ ERROR attempted remainder with a divisor of zero in a constant expression +} diff --git a/src/test/run-pass/issue-17216.rs b/src/test/run-pass/issue-17216.rs index e48d7b0756c..ce5a0aa96e3 100644 --- a/src/test/run-pass/issue-17216.rs +++ b/src/test/run-pass/issue-17216.rs @@ -25,7 +25,9 @@ fn main() { let mut dropped = false; { let leak = Leak { dropped: &mut dropped }; - for ((), leaked) in Some(((),leak)).into_iter() {} + // FIXME(#21721) "hack" used to be () but that can cause + // certain LLVM versions to abort during optimizations. + for (_, leaked) in Some(("hack", leak)).into_iter() {} } assert!(dropped); diff --git a/src/test/run-pass/issue-8460.rs b/src/test/run-pass/issue-8460.rs index 3944895460f..4b9ed44c7cd 100644 --- a/src/test/run-pass/issue-8460.rs +++ b/src/test/run-pass/issue-8460.rs @@ -8,28 +8,32 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::{int, i8, i16, i32, i64}; +use std::num::Int; use std::thread::Thread; +// Avoid using constants, which would trigger compile-time errors. +fn min_val() -> T { Int::min_value() } +fn zero() -> T { Int::zero() } + fn main() { - assert!(Thread::scoped(move|| int::MIN / -1).join().is_err()); - assert!(Thread::scoped(move|| i8::MIN / -1).join().is_err()); - assert!(Thread::scoped(move|| i16::MIN / -1).join().is_err()); - assert!(Thread::scoped(move|| i32::MIN / -1).join().is_err()); - assert!(Thread::scoped(move|| i64::MIN / -1).join().is_err()); - assert!(Thread::scoped(move|| 1 / 0).join().is_err()); - assert!(Thread::scoped(move|| 1i8 / 0).join().is_err()); - assert!(Thread::scoped(move|| 1i16 / 0).join().is_err()); - assert!(Thread::scoped(move|| 1i32 / 0).join().is_err()); - assert!(Thread::scoped(move|| 1i64 / 0).join().is_err()); - assert!(Thread::scoped(move|| int::MIN % -1).join().is_err()); - assert!(Thread::scoped(move|| i8::MIN % -1).join().is_err()); - assert!(Thread::scoped(move|| i16::MIN % -1).join().is_err()); - assert!(Thread::scoped(move|| i32::MIN % -1).join().is_err()); - assert!(Thread::scoped(move|| i64::MIN % -1).join().is_err()); - assert!(Thread::scoped(move|| 1 % 0).join().is_err()); - assert!(Thread::scoped(move|| 1i8 % 0).join().is_err()); - assert!(Thread::scoped(move|| 1i16 % 0).join().is_err()); - assert!(Thread::scoped(move|| 1i32 % 0).join().is_err()); - assert!(Thread::scoped(move|| 1i64 % 0).join().is_err()); + assert!(Thread::scoped(move|| min_val::() / -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() / -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() / -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() / -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() / -1).join().is_err()); + assert!(Thread::scoped(move|| 1is / zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i8 / zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i16 / zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i32 / zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i64 / zero()).join().is_err()); + assert!(Thread::scoped(move|| min_val::() % -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() % -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() % -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() % -1).join().is_err()); + assert!(Thread::scoped(move|| min_val::() % -1).join().is_err()); + assert!(Thread::scoped(move|| 1is % zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i8 % zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i16 % zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i32 % zero()).join().is_err()); + assert!(Thread::scoped(move|| 1i64 % zero()).join().is_err()); }