1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use lib::llvm::{llvm, ConstFCmp, ConstICmp, SetLinkage, PrivateLinkage, ValueRef, Bool, True};
14 use lib::llvm::{IntEQ, IntNE, IntUGT, IntUGE, IntULT, IntULE, IntSGT, IntSGE, IntSLT, IntSLE,
15 RealOEQ, RealOGT, RealOGE, RealOLT, RealOLE, RealONE};
17 use metadata::csearch;
18 use middle::const_eval;
19 use middle::trans::adt;
20 use middle::trans::base;
21 use middle::trans::base::push_ctxt;
22 use middle::trans::closure;
23 use middle::trans::common::*;
24 use middle::trans::consts;
25 use middle::trans::expr;
26 use middle::trans::inline;
27 use middle::trans::machine;
28 use middle::trans::type_of;
30 use util::ppaux::{Repr, ty_to_str};
32 use middle::trans::type_::Type;
34 use std::c_str::ToCStr;
35 use std::libc::c_uint;
37 use syntax::{ast, ast_util};
39 pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: ast::Lit)
41 let _icx = push_ctxt("trans_lit");
43 ast::LitChar(i) => C_integral(Type::char(), i as u64, false),
44 ast::LitInt(i, t) => C_integral(Type::int_from_ty(cx, t), i as u64, true),
45 ast::LitUint(u, t) => C_integral(Type::uint_from_ty(cx, t), u, false),
46 ast::LitIntUnsuffixed(i) => {
47 let lit_int_ty = ty::node_id_to_type(cx.tcx, e.id);
48 match ty::get(lit_int_ty).sty {
50 C_integral(Type::int_from_ty(cx, t), i as u64, true)
53 C_integral(Type::uint_from_ty(cx, t), i as u64, false)
55 _ => cx.sess.span_bug(lit.span,
56 format!("integer literal has type {} (expected int or uint)",
57 ty_to_str(cx.tcx, lit_int_ty)))
60 ast::LitFloat(ref fs, t) => {
61 C_floating(fs.get(), Type::float_from_ty(t))
63 ast::LitFloatUnsuffixed(ref fs) => {
64 let lit_float_ty = ty::node_id_to_type(cx.tcx, e.id);
65 match ty::get(lit_float_ty).sty {
67 C_floating(fs.get(), Type::float_from_ty(t))
70 cx.sess.span_bug(lit.span,
71 "floating point literal doesn't have the right type");
75 ast::LitBool(b) => C_bool(b),
76 ast::LitNil => C_nil(),
77 ast::LitStr(ref s, _) => C_str_slice(cx, (*s).clone()),
78 ast::LitBinary(ref data) => {
79 C_binary_slice(cx, data.borrow().as_slice())
84 pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef {
86 let b = llvm::LLVMConstPointerCast(a, t.ptr_to().to_ref());
87 let mut const_globals = cx.const_globals.borrow_mut();
88 assert!(const_globals.get().insert(b as int, a));
93 fn const_vec(cx: @CrateContext, e: &ast::Expr,
94 es: &[@ast::Expr], is_local: bool) -> (ValueRef, Type, bool) {
95 let vec_ty = ty::expr_ty(cx.tcx, e);
96 let unit_ty = ty::sequence_element_type(cx.tcx, vec_ty);
97 let llunitty = type_of::type_of(cx, unit_ty);
98 let (vs, inlineable) = vec::unzip(es.iter().map(|e| const_expr(cx, *e, is_local)));
99 // If the vector contains enums, an LLVM array won't work.
100 let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
103 C_array(llunitty, vs)
105 (v, llunitty, inlineable.iter().fold(true, |a, &b| a && b))
108 fn const_addr_of(cx: &CrateContext, cv: ValueRef) -> ValueRef {
110 let gv = "const".with_c_str(|name| {
111 llvm::LLVMAddGlobal(cx.llmod, val_ty(cv).to_ref(), name)
113 llvm::LLVMSetInitializer(gv, cv);
114 llvm::LLVMSetGlobalConstant(gv, True);
115 SetLinkage(gv, PrivateLinkage);
120 fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef {
121 let const_globals = cx.const_globals.borrow();
122 let v = match const_globals.get().find(&(v as int)) {
127 assert_eq!(llvm::LLVMIsGlobalConstant(v), True);
128 llvm::LLVMGetInitializer(v)
132 fn const_deref_newtype(cx: &CrateContext, v: ValueRef, t: ty::t)
134 let repr = adt::represent_type(cx, t);
135 adt::const_get_field(cx, repr, v, 0, 0)
138 fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool)
139 -> (ValueRef, ty::t) {
140 match ty::deref(t, explicit) {
142 assert!(mt.mutbl != ast::MutMutable);
143 let dv = match ty::get(t).sty {
144 ty::ty_ptr(..) | ty::ty_rptr(..) => {
145 const_deref_ptr(cx, v)
147 ty::ty_enum(..) | ty::ty_struct(..) => {
148 const_deref_newtype(cx, v, t)
151 cx.sess.bug(format!("unexpected dereferenceable type {}",
152 ty_to_str(cx.tcx, t)))
158 cx.sess.bug(format!("can't dereference const of type {}",
159 ty_to_str(cx.tcx, t)))
164 pub fn get_const_val(cx: @CrateContext,
165 mut def_id: ast::DefId) -> (ValueRef, bool) {
167 let const_values = cx.const_values.borrow();
168 const_values.get().contains_key(&def_id.node)
170 if !ast_util::is_local(def_id) || !contains_key {
171 if !ast_util::is_local(def_id) {
172 def_id = inline::maybe_instantiate_inline(cx, def_id);
175 match cx.tcx.map.expect_item(def_id.node).node {
176 ast::ItemStatic(_, ast::MutImmutable, _) => {
177 trans_const(cx, ast::MutImmutable, def_id.node);
183 let const_values = cx.const_values.borrow();
184 let non_inlineable_statics = cx.non_inlineable_statics.borrow();
185 (const_values.get().get_copy(&def_id.node),
186 !non_inlineable_statics.get().contains(&def_id.node))
189 pub fn const_expr(cx: @CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool) {
190 let (llconst, inlineable) = const_expr_unadjusted(cx, e, is_local);
191 let mut llconst = llconst;
192 let mut inlineable = inlineable;
193 let ety = ty::expr_ty(cx.tcx, e);
194 let ety_adjusted = ty::expr_ty_adjusted(cx.tcx, e);
196 let adjustments = cx.tcx.adjustments.borrow();
197 adjustments.get().find_copy(&e.id)
203 ty::AutoAddEnv(ty::ReStatic, ast::BorrowedSigil) => {
204 let def = ty::resolve_expr(cx.tcx, e);
205 let wrapper = closure::get_wrapper_for_bare_fn(cx,
210 llconst = C_struct([wrapper, C_null(Type::i8p())], false)
212 ty::AutoAddEnv(ref r, ref s) => {
215 format!("unexpected static function: region \
220 ty::AutoObject(..) => {
223 "unimplemented const coercion to trait \
226 ty::AutoDerefRef(ref adj) => {
228 let mut maybe_ptr = None;
229 for _ in range(0, adj.autoderefs) {
230 let (dv, dt) = const_deref(cx, llconst, ty, false);
231 maybe_ptr = Some(llconst);
238 Some(ref autoref) => {
239 // Don't copy data to do a deref+ref.
240 let llptr = match maybe_ptr {
244 const_addr_of(cx, llconst)
249 ty::AutoPtr(ty::ReStatic, m) => {
250 assert!(m != ast::MutMutable);
253 ty::AutoBorrowVec(ty::ReStatic, m) => {
254 assert!(m != ast::MutMutable);
255 assert_eq!(abi::slice_elt_base, 0);
256 assert_eq!(abi::slice_elt_len, 1);
257 match ty::get(ty).sty {
259 ty::vstore_fixed(len)) => {
269 cx.sess.span_bug(e.span,
270 format!("unimplemented \
283 let llty = type_of::sizing_type_of(cx, ety_adjusted);
284 let csize = machine::llsize_of_alloc(cx, val_ty(llconst));
285 let tsize = machine::llsize_of_alloc(cx, llty);
288 // FIXME these values could use some context
289 llvm::LLVMDumpValue(llconst);
290 llvm::LLVMDumpValue(C_undef(llty));
292 cx.sess.bug(format!("const {} of type {} has size {} instead of {}",
293 e.repr(cx.tcx), ty_to_str(cx.tcx, ety),
296 (llconst, inlineable)
299 // the bool returned is whether this expression can be inlined into other crates
300 // if it's assigned to a static.
301 fn const_expr_unadjusted(cx: @CrateContext, e: &ast::Expr,
302 is_local: bool) -> (ValueRef, bool) {
303 let map_list = |exprs: &[@ast::Expr]| {
304 exprs.iter().map(|&e| const_expr(cx, e, is_local))
305 .fold((~[], true), |(l, all_inlineable), (val, inlineable)| {
306 (vec::append_one(l, val), all_inlineable && inlineable)
310 let _icx = push_ctxt("const_expr");
311 return match e.node {
312 ast::ExprLit(lit) => {
313 (consts::const_lit(cx, e, (*lit).clone()), true)
315 ast::ExprBinary(b, e1, e2) => {
316 let (te1, _) = const_expr(cx, e1, is_local);
317 let (te2, _) = const_expr(cx, e2, is_local);
319 let te2 = base::cast_shift_const_rhs(b, te1, te2);
321 /* Neither type is bottom, and we expect them to be unified
322 * already, so the following is safe. */
323 let ty = ty::expr_ty(cx.tcx, e1);
324 let is_float = ty::type_is_fp(ty);
325 let signed = ty::type_is_signed(ty);
328 if is_float { llvm::LLVMConstFAdd(te1, te2) }
329 else { llvm::LLVMConstAdd(te1, te2) }
332 if is_float { llvm::LLVMConstFSub(te1, te2) }
333 else { llvm::LLVMConstSub(te1, te2) }
336 if is_float { llvm::LLVMConstFMul(te1, te2) }
337 else { llvm::LLVMConstMul(te1, te2) }
340 if is_float { llvm::LLVMConstFDiv(te1, te2) }
341 else if signed { llvm::LLVMConstSDiv(te1, te2) }
342 else { llvm::LLVMConstUDiv(te1, te2) }
345 if is_float { llvm::LLVMConstFRem(te1, te2) }
346 else if signed { llvm::LLVMConstSRem(te1, te2) }
347 else { llvm::LLVMConstURem(te1, te2) }
349 ast::BiAnd => llvm::LLVMConstAnd(te1, te2),
350 ast::BiOr => llvm::LLVMConstOr(te1, te2),
351 ast::BiBitXor => llvm::LLVMConstXor(te1, te2),
352 ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2),
353 ast::BiBitOr => llvm::LLVMConstOr(te1, te2),
354 ast::BiShl => llvm::LLVMConstShl(te1, te2),
356 if signed { llvm::LLVMConstAShr(te1, te2) }
357 else { llvm::LLVMConstLShr(te1, te2) }
360 if is_float { ConstFCmp(RealOEQ, te1, te2) }
361 else { ConstICmp(IntEQ, te1, te2) }
364 if is_float { ConstFCmp(RealOLT, te1, te2) }
366 if signed { ConstICmp(IntSLT, te1, te2) }
367 else { ConstICmp(IntULT, te1, te2) }
371 if is_float { ConstFCmp(RealOLE, te1, te2) }
373 if signed { ConstICmp(IntSLE, te1, te2) }
374 else { ConstICmp(IntULE, te1, te2) }
378 if is_float { ConstFCmp(RealONE, te1, te2) }
379 else { ConstICmp(IntNE, te1, te2) }
382 if is_float { ConstFCmp(RealOGE, te1, te2) }
384 if signed { ConstICmp(IntSGE, te1, te2) }
385 else { ConstICmp(IntUGE, te1, te2) }
389 if is_float { ConstFCmp(RealOGT, te1, te2) }
391 if signed { ConstICmp(IntSGT, te1, te2) }
392 else { ConstICmp(IntUGT, te1, te2) }
397 ast::ExprUnary(u, e) => {
398 let (te, _) = const_expr(cx, e, is_local);
399 let ty = ty::expr_ty(cx.tcx, e);
400 let is_float = ty::type_is_fp(ty);
402 ast::UnBox | ast::UnUniq | ast::UnDeref => {
403 let (dv, _dt) = const_deref(cx, te, ty, true);
407 match ty::get(ty).sty {
409 // Somewhat questionable, but I believe this is
411 let te = llvm::LLVMConstTrunc(te, Type::i1().to_ref());
412 let te = llvm::LLVMConstNot(te);
413 llvm::LLVMConstZExt(te, Type::bool().to_ref())
415 _ => llvm::LLVMConstNot(te),
419 if is_float { llvm::LLVMConstFNeg(te) }
420 else { llvm::LLVMConstNeg(te) }
424 ast::ExprField(base, field, _) => {
425 let bt = ty::expr_ty_adjusted(cx.tcx, base);
426 let brepr = adt::represent_type(cx, bt);
427 let (bv, inlineable) = const_expr(cx, base, is_local);
428 expr::with_field_tys(cx.tcx, bt, None, |discr, field_tys| {
429 let ix = ty::field_idx_strict(cx.tcx, field.name, field_tys);
430 (adt::const_get_field(cx, brepr, bv, discr, ix), inlineable)
434 ast::ExprIndex(base, index) => {
435 let bt = ty::expr_ty_adjusted(cx.tcx, base);
436 let (bv, inlineable) = const_expr(cx, base, is_local);
437 let iv = match const_eval::eval_const_expr(cx.tcx, index) {
438 const_eval::const_int(i) => i as u64,
439 const_eval::const_uint(u) => u,
440 _ => cx.sess.span_bug(index.span,
441 "index is not an integer-constant expression")
443 let (arr, len) = match ty::get(bt).sty {
444 ty::ty_vec(_, vstore) | ty::ty_str(vstore) =>
446 ty::vstore_fixed(u) =>
449 ty::vstore_slice(_) => {
450 let e1 = const_get_elt(cx, bv, [0]);
451 (const_deref_ptr(cx, e1), const_get_elt(cx, bv, [1]))
453 _ => cx.sess.span_bug(base.span,
454 "index-expr base must be fixed-size or slice")
456 _ => cx.sess.span_bug(base.span,
457 "index-expr base must be a vector or string type")
460 let len = llvm::LLVMConstIntGetZExtValue(len) as u64;
461 let len = match ty::get(bt).sty {
462 ty::ty_str(..) => {assert!(len > 0); len - 1},
466 // FIXME #3170: report this earlier on in the const-eval
467 // pass. Reporting here is a bit late.
468 cx.sess.span_err(e.span,
469 "const index-expr is out of bounds");
471 (const_get_elt(cx, arr, [iv as c_uint]), inlineable)
473 ast::ExprCast(base, _) => {
474 let ety = ty::expr_ty(cx.tcx, e);
475 let llty = type_of::type_of(cx, ety);
476 let basety = ty::expr_ty(cx.tcx, base);
477 let (v, inlineable) = const_expr(cx, base, is_local);
478 return (match (expr::cast_type_kind(basety),
479 expr::cast_type_kind(ety)) {
481 (expr::cast_integral, expr::cast_integral) => {
482 let s = ty::type_is_signed(basety) as Bool;
483 llvm::LLVMConstIntCast(v, llty.to_ref(), s)
485 (expr::cast_integral, expr::cast_float) => {
486 if ty::type_is_signed(basety) {
487 llvm::LLVMConstSIToFP(v, llty.to_ref())
489 llvm::LLVMConstUIToFP(v, llty.to_ref())
492 (expr::cast_float, expr::cast_float) => {
493 llvm::LLVMConstFPCast(v, llty.to_ref())
495 (expr::cast_float, expr::cast_integral) => {
496 if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
497 else { llvm::LLVMConstFPToUI(v, llty.to_ref()) }
499 (expr::cast_enum, expr::cast_integral) |
500 (expr::cast_enum, expr::cast_float) => {
501 let repr = adt::represent_type(cx, basety);
502 let discr = adt::const_get_discrim(cx, repr, v);
503 let iv = C_integral(cx.int_type, discr, false);
504 let ety_cast = expr::cast_type_kind(ety);
506 expr::cast_integral => {
507 let s = ty::type_is_signed(ety) as Bool;
508 llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
510 expr::cast_float => llvm::LLVMConstUIToFP(iv, llty.to_ref()),
511 _ => cx.sess.bug("enum cast destination is not \
515 (expr::cast_pointer, expr::cast_pointer) => {
516 llvm::LLVMConstPointerCast(v, llty.to_ref())
518 (expr::cast_integral, expr::cast_pointer) => {
519 llvm::LLVMConstIntToPtr(v, llty.to_ref())
522 cx.sess.impossible_case(e.span,
523 "bad combination of types for cast")
527 ast::ExprAddrOf(ast::MutImmutable, sub) => {
528 let (e, _) = const_expr(cx, sub, is_local);
529 (const_addr_of(cx, e), false)
531 ast::ExprTup(ref es) => {
532 let ety = ty::expr_ty(cx.tcx, e);
533 let repr = adt::represent_type(cx, ety);
534 let (vals, inlineable) = map_list(es.as_slice());
535 (adt::trans_const(cx, repr, 0, vals), inlineable)
537 ast::ExprStruct(_, ref fs, ref base_opt) => {
538 let ety = ty::expr_ty(cx.tcx, e);
539 let repr = adt::represent_type(cx, ety);
542 let base_val = match *base_opt {
543 Some(base) => Some(const_expr(cx, base, is_local)),
547 expr::with_field_tys(tcx, ety, Some(e.id), |discr, field_tys| {
548 let cs = field_tys.iter().enumerate()
549 .map(|(ix, &field_ty)| {
550 match fs.iter().find(|f| field_ty.ident.name == f.ident.node.name) {
551 Some(f) => const_expr(cx, (*f).expr, is_local),
554 Some((bv, inlineable)) => {
555 (adt::const_get_field(cx, repr, bv, discr, ix),
558 None => cx.tcx.sess.span_bug(e.span, "missing struct field")
563 let (cs, inlineable) = vec::unzip(cs.move_iter());
564 (adt::trans_const(cx, repr, discr, cs),
565 inlineable.iter().fold(true, |a, &b| a && b))
568 ast::ExprVec(ref es, ast::MutImmutable) => {
569 let (v, _, inlineable) = const_vec(cx,
575 ast::ExprVstore(sub, ast::ExprVstoreSlice) => {
577 ast::ExprLit(ref lit) => {
579 ast::LitStr(..) => { const_expr(cx, sub, is_local) }
580 _ => { cx.sess.span_bug(e.span, "bad const-slice lit") }
583 ast::ExprVec(ref es, ast::MutImmutable) => {
584 let (cv, llunitty, _) = const_vec(cx,
588 let llty = val_ty(cv);
589 let gv = "const".with_c_str(|name| {
590 llvm::LLVMAddGlobal(cx.llmod, llty.to_ref(), name)
592 llvm::LLVMSetInitializer(gv, cv);
593 llvm::LLVMSetGlobalConstant(gv, True);
594 SetLinkage(gv, PrivateLinkage);
595 let p = const_ptrcast(cx, gv, llunitty);
596 (C_struct([p, C_uint(cx, es.len())], false), false)
598 _ => cx.sess.span_bug(e.span, "bad const-slice expr")
601 ast::ExprRepeat(elem, count, _) => {
602 let vec_ty = ty::expr_ty(cx.tcx, e);
603 let unit_ty = ty::sequence_element_type(cx.tcx, vec_ty);
604 let llunitty = type_of::type_of(cx, unit_ty);
605 let n = match const_eval::eval_const_expr(cx.tcx, count) {
606 const_eval::const_int(i) => i as uint,
607 const_eval::const_uint(i) => i as uint,
608 _ => cx.sess.span_bug(count.span, "count must be integral const expression.")
610 let vs = vec::from_elem(n, const_expr(cx, elem, is_local).val0());
611 let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
614 C_array(llunitty, vs)
618 ast::ExprPath(ref pth) => {
619 // Assert that there are no type parameters in this path.
620 assert!(pth.segments.iter().all(|seg| seg.types.is_empty()));
624 let def_map = tcx.def_map.borrow();
625 def_map.get().find_copy(&e.id)
628 Some(ast::DefFn(def_id, _purity)) => {
629 if !ast_util::is_local(def_id) {
630 let ty = csearch::get_type(cx.tcx, def_id).ty;
631 (base::trans_external_path(cx, def_id, ty), true)
633 assert!(ast_util::is_local(def_id));
634 (base::get_item_val(cx, def_id.node), true)
637 Some(ast::DefStatic(def_id, false)) => {
638 get_const_val(cx, def_id)
640 Some(ast::DefVariant(enum_did, variant_did, _)) => {
641 let ety = ty::expr_ty(cx.tcx, e);
642 let repr = adt::represent_type(cx, ety);
643 let vinfo = ty::enum_variant_with_id(cx.tcx,
646 (adt::trans_const(cx, repr, vinfo.disr_val, []), true)
648 Some(ast::DefStruct(_)) => {
649 let ety = ty::expr_ty(cx.tcx, e);
650 let llty = type_of::type_of(cx, ety);
654 cx.sess.span_bug(e.span, "expected a const, fn, struct, or variant def")
658 ast::ExprCall(callee, ref args) => {
661 let def_map = tcx.def_map.borrow();
662 def_map.get().find_copy(&callee.id)
665 Some(ast::DefStruct(_)) => {
666 let ety = ty::expr_ty(cx.tcx, e);
667 let repr = adt::represent_type(cx, ety);
668 let (arg_vals, inlineable) = map_list(args.as_slice());
669 (adt::trans_const(cx, repr, 0, arg_vals), inlineable)
671 Some(ast::DefVariant(enum_did, variant_did, _)) => {
672 let ety = ty::expr_ty(cx.tcx, e);
673 let repr = adt::represent_type(cx, ety);
674 let vinfo = ty::enum_variant_with_id(cx.tcx,
677 let (arg_vals, inlineable) = map_list(args.as_slice());
678 (adt::trans_const(cx, repr, vinfo.disr_val, arg_vals),
681 _ => cx.sess.span_bug(e.span, "expected a struct or variant def")
684 ast::ExprParen(e) => { const_expr(cx, e, is_local) }
685 _ => cx.sess.span_bug(e.span,
686 "bad constant expression type in consts::const_expr")
691 pub fn trans_const(ccx: @CrateContext, m: ast::Mutability, id: ast::NodeId) {
693 let _icx = push_ctxt("trans_const");
694 let g = base::get_item_val(ccx, id);
695 // At this point, get_item_val has already translated the
696 // constant's initializer to determine its LLVM type.
697 let const_values = ccx.const_values.borrow();
698 let v = const_values.get().get_copy(&id);
699 llvm::LLVMSetInitializer(g, v);
700 if m != ast::MutMutable {
701 llvm::LLVMSetGlobalConstant(g, True);