From 2a3084b527120db7c0645a191ef6147962dbe23d Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Tue, 31 Jul 2012 18:34:36 -0700 Subject: [PATCH] Start implementing structured constants. --- src/rustc/middle/check_const.rs | 2 + src/rustc/middle/trans/alt.rs | 6 +- src/rustc/middle/trans/base.rs | 171 +----------------------- src/rustc/middle/trans/consts.rs | 175 +++++++++++++++++++++++++ src/rustc/rustc.rc | 1 + src/test/run-pass/const-rec-and-tup.rs | 13 ++ 6 files changed, 196 insertions(+), 172 deletions(-) create mode 100644 src/rustc/middle/trans/consts.rs create mode 100644 src/test/run-pass/const-rec-and-tup.rs diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs index 6f9a47b98bb..3be041f4e9a 100644 --- a/src/rustc/middle/check_const.rs +++ b/src/rustc/middle/check_const.rs @@ -101,6 +101,8 @@ fn check_expr(sess: session, def_map: resolve3::DefMap, } } } + expr_tup(*) | + expr_rec(*) { } _ { sess.span_err(e.span, ~"constant contains unimplemented expression type"); diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index c079e78c782..ef957e1c75a 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -58,14 +58,14 @@ fn trans_opt(bcx: block, o: opt) -> opt_result { } _ { ret single_result( - rslt(bcx, trans_const_expr(ccx, l))); + rslt(bcx, consts::const_expr(ccx, l))); } } } var(disr_val, _) { ret single_result(rslt(bcx, C_int(ccx, disr_val))); } range(l1, l2) { - ret range_result(rslt(bcx, trans_const_expr(ccx, l1)), - rslt(bcx, trans_const_expr(ccx, l2))); + ret range_result(rslt(bcx, consts::const_expr(ccx, l1)), + rslt(bcx, consts::const_expr(ccx, l2))); } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 96e7c74f25d..5960b7657f4 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1420,35 +1420,6 @@ fn store_temp_expr(cx: block, action: copy_action, dst: ValueRef, ret move_val(cx, action, dst, src, t); } -fn trans_crate_lit(cx: @crate_ctxt, e: @ast::expr, lit: ast::lit) - -> ValueRef { - let _icx = cx.insn_ctxt(~"trans_crate_lit"); - alt lit.node { - ast::lit_int(i, t) { C_integral(T_int_ty(cx, t), i as u64, True) } - ast::lit_uint(u, t) { C_integral(T_uint_ty(cx, t), u, False) } - ast::lit_int_unsuffixed(i) { - let lit_int_ty = ty::node_id_to_type(cx.tcx, e.id); - alt ty::get(lit_int_ty).struct { - ty::ty_int(t) { - C_integral(T_int_ty(cx, t), i as u64, True) - } - ty::ty_uint(t) { - C_integral(T_uint_ty(cx, t), i as u64, False) - } - _ { cx.sess.span_bug(lit.span, - ~"integer literal doesn't have a type"); - } - } - } - ast::lit_float(fs, t) { C_floating(*fs, T_float_ty(cx, t)) } - ast::lit_bool(b) { C_bool(b) } - ast::lit_nil { C_nil() } - ast::lit_str(s) { - cx.sess.span_unimpl(lit.span, ~"unique string in this context"); - } - } -} - fn trans_lit(cx: block, e: @ast::expr, lit: ast::lit, dest: dest) -> block { let _icx = cx.insn_ctxt(~"trans_lit"); if dest == ignore { ret cx; } @@ -1456,12 +1427,11 @@ fn trans_lit(cx: block, e: @ast::expr, lit: ast::lit, dest: dest) -> block { ast::lit_str(s) { tvec::trans_estr(cx, s, ast::vstore_fixed(none), dest) } _ { - store_in_dest(cx, trans_crate_lit(cx.ccx(), e, lit), dest) + store_in_dest(cx, consts::const_lit(cx.ccx(), e, lit), dest) } } } - fn trans_boxed_expr(bcx: block, contents: @ast::expr, t: ty::t, heap: heap, dest: dest) -> block { @@ -1473,7 +1443,6 @@ fn trans_boxed_expr(bcx: block, contents: @ast::expr, ret store_in_dest(bcx, box, dest); } - fn trans_unary(bcx: block, op: ast::unop, e: @ast::expr, un_expr: @ast::expr, dest: dest) -> block { let _icx = bcx.insn_ctxt(~"trans_unary"); @@ -4716,142 +4685,6 @@ fn trans_enum_variant(ccx: @crate_ctxt, enum_id: ast::node_id, finish_fn(fcx, lltop); } - -// FIXME (#2530): this should do some structural hash-consing to avoid -// duplicate constants. I think. Maybe LLVM has a magical mode that does so -// later on? -fn trans_const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { - let _icx = cx.insn_ctxt(~"trans_const_expr"); - alt e.node { - ast::expr_lit(lit) { trans_crate_lit(cx, e, *lit) } - // If we have a vstore, just keep going; it has to be a string - ast::expr_vstore(e, _) { trans_const_expr(cx, e) } - ast::expr_binary(b, e1, e2) { - let te1 = trans_const_expr(cx, e1); - let te2 = trans_const_expr(cx, e2); - - let te2 = 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); - ret alt b { - ast::add { - if is_float { llvm::LLVMConstFAdd(te1, te2) } - else { llvm::LLVMConstAdd(te1, te2) } - } - ast::subtract { - if is_float { llvm::LLVMConstFSub(te1, te2) } - else { llvm::LLVMConstSub(te1, te2) } - } - ast::mul { - if is_float { llvm::LLVMConstFMul(te1, te2) } - else { llvm::LLVMConstMul(te1, te2) } - } - ast::div { - if is_float { llvm::LLVMConstFDiv(te1, te2) } - else if signed { llvm::LLVMConstSDiv(te1, te2) } - else { llvm::LLVMConstUDiv(te1, te2) } - } - ast::rem { - if is_float { llvm::LLVMConstFRem(te1, te2) } - else if signed { llvm::LLVMConstSRem(te1, te2) } - else { llvm::LLVMConstURem(te1, te2) } - } - ast::and | - ast::or { cx.sess.span_unimpl(e.span, ~"binop logic"); } - ast::bitxor { llvm::LLVMConstXor(te1, te2) } - ast::bitand { llvm::LLVMConstAnd(te1, te2) } - ast::bitor { llvm::LLVMConstOr(te1, te2) } - ast::shl { llvm::LLVMConstShl(te1, te2) } - ast::shr { - if signed { llvm::LLVMConstAShr(te1, te2) } - else { llvm::LLVMConstLShr(te1, te2) } - } - ast::eq | - ast::lt | - ast::le | - ast::ne | - ast::ge | - ast::gt { cx.sess.span_unimpl(e.span, ~"binop comparator"); } - } - } - ast::expr_unary(u, e) { - let te = trans_const_expr(cx, e); - let ty = ty::expr_ty(cx.tcx, e); - let is_float = ty::type_is_fp(ty); - ret alt u { - ast::box(_) | - ast::uniq(_) | - ast::deref { cx.sess.span_bug(e.span, - ~"bad unop type in trans_const_expr"); } - ast::not { llvm::LLVMConstNot(te) } - ast::neg { - if is_float { llvm::LLVMConstFNeg(te) } - else { llvm::LLVMConstNeg(te) } - } - } - } - ast::expr_cast(base, tp) { - let ety = ty::expr_ty(cx.tcx, e), llty = type_of(cx, ety); - let basety = ty::expr_ty(cx.tcx, base); - let v = trans_const_expr(cx, base); - alt check (cast_type_kind(basety), cast_type_kind(ety)) { - (cast_integral, cast_integral) { - let s = if ty::type_is_signed(basety) { True } else { False }; - llvm::LLVMConstIntCast(v, llty, s) - } - (cast_integral, cast_float) { - if ty::type_is_signed(basety) { llvm::LLVMConstSIToFP(v, llty) } - else { llvm::LLVMConstUIToFP(v, llty) } - } - (cast_float, cast_float) { llvm::LLVMConstFPCast(v, llty) } - (cast_float, cast_integral) { - if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) } - else { llvm::LLVMConstFPToUI(v, llty) } - } - } - } - ast::expr_path(path) { - alt cx.tcx.def_map.find(e.id) { - some(ast::def_const(def_id)) { - // Don't know how to handle external consts - assert ast_util::is_local(def_id); - alt cx.tcx.items.get(def_id.node) { - ast_map::node_item(@{ - node: ast::item_const(_, subexpr), _ - }, _) { - // FIXME (#2530): Instead of recursing here to regenerate - // the values for other constants, we should just look up - // the already-defined value. - trans_const_expr(cx, subexpr) - } - _ { - cx.sess.span_bug(e.span, ~"expected item"); - } - } - } - _ { cx.sess.span_bug(e.span, ~"expected to find a const def") } - } - } - _ { cx.sess.span_bug(e.span, - ~"bad constant expression type in trans_const_expr"); } - } -} - -fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) { - let _icx = ccx.insn_ctxt(~"trans_const"); - let v = trans_const_expr(ccx, e); - - // The scalars come back as 1st class LLVM vals - // which we have to stick into global constants. - let g = get_item_val(ccx, id); - llvm::LLVMSetInitializer(g, v); - llvm::LLVMSetGlobalConstant(g, True); -} - fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl, body: ast::blk, llctor_decl: ValueRef, psubsts: param_substs, ctor_id: ast::node_id, @@ -5008,7 +4841,7 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) { } } } - ast::item_const(_, expr) { trans_const(ccx, expr, item.id); } + ast::item_const(_, expr) { consts::trans_const(ccx, expr, item.id); } ast::item_foreign_mod(foreign_mod) { let abi = alt attr::foreign_abi(item.attrs) { either::right(abi_) { abi_ } diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs new file mode 100644 index 00000000000..c9e2582f5b2 --- /dev/null +++ b/src/rustc/middle/trans/consts.rs @@ -0,0 +1,175 @@ +import common::*; +import syntax::{ast, ast_util, codemap, ast_map}; +import base::get_insn_ctxt; + +fn const_lit(cx: @crate_ctxt, e: @ast::expr, lit: ast::lit) + -> ValueRef { + let _icx = cx.insn_ctxt(~"trans_lit"); + alt lit.node { + ast::lit_int(i, t) { C_integral(T_int_ty(cx, t), i as u64, True) } + ast::lit_uint(u, t) { C_integral(T_uint_ty(cx, t), u, False) } + ast::lit_int_unsuffixed(i) { + let lit_int_ty = ty::node_id_to_type(cx.tcx, e.id); + alt ty::get(lit_int_ty).struct { + ty::ty_int(t) { + C_integral(T_int_ty(cx, t), i as u64, True) + } + ty::ty_uint(t) { + C_integral(T_uint_ty(cx, t), i as u64, False) + } + _ { cx.sess.span_bug(lit.span, + ~"integer literal doesn't have a type"); + } + } + } + ast::lit_float(fs, t) { C_floating(*fs, T_float_ty(cx, t)) } + ast::lit_bool(b) { C_bool(b) } + ast::lit_nil { C_nil() } + ast::lit_str(s) { + cx.sess.span_unimpl(lit.span, ~"unique string in this context"); + } + } +} + +// FIXME (#2530): this should do some structural hash-consing to avoid +// duplicate constants. I think. Maybe LLVM has a magical mode that does so +// later on? +fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { + let _icx = cx.insn_ctxt(~"const_expr"); + alt e.node { + ast::expr_lit(lit) { consts::const_lit(cx, e, *lit) } + // If we have a vstore, just keep going; it has to be a string + ast::expr_vstore(e, _) { const_expr(cx, e) } + ast::expr_binary(b, e1, 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); + ret alt b { + ast::add { + if is_float { llvm::LLVMConstFAdd(te1, te2) } + else { llvm::LLVMConstAdd(te1, te2) } + } + ast::subtract { + if is_float { llvm::LLVMConstFSub(te1, te2) } + else { llvm::LLVMConstSub(te1, te2) } + } + ast::mul { + if is_float { llvm::LLVMConstFMul(te1, te2) } + else { llvm::LLVMConstMul(te1, te2) } + } + ast::div { + if is_float { llvm::LLVMConstFDiv(te1, te2) } + else if signed { llvm::LLVMConstSDiv(te1, te2) } + else { llvm::LLVMConstUDiv(te1, te2) } + } + ast::rem { + if is_float { llvm::LLVMConstFRem(te1, te2) } + else if signed { llvm::LLVMConstSRem(te1, te2) } + else { llvm::LLVMConstURem(te1, te2) } + } + ast::and | + ast::or { cx.sess.span_unimpl(e.span, ~"binop logic"); } + ast::bitxor { llvm::LLVMConstXor(te1, te2) } + ast::bitand { llvm::LLVMConstAnd(te1, te2) } + ast::bitor { llvm::LLVMConstOr(te1, te2) } + ast::shl { llvm::LLVMConstShl(te1, te2) } + ast::shr { + if signed { llvm::LLVMConstAShr(te1, te2) } + else { llvm::LLVMConstLShr(te1, te2) } + } + ast::eq | + ast::lt | + ast::le | + ast::ne | + ast::ge | + ast::gt { cx.sess.span_unimpl(e.span, ~"binop comparator"); } + } + } + ast::expr_unary(u, e) { + let te = const_expr(cx, e); + let ty = ty::expr_ty(cx.tcx, e); + let is_float = ty::type_is_fp(ty); + ret alt u { + ast::box(_) | + ast::uniq(_) | + ast::deref { cx.sess.span_bug(e.span, + ~"bad unop type in const_expr"); } + ast::not { llvm::LLVMConstNot(te) } + ast::neg { + if is_float { llvm::LLVMConstFNeg(te) } + else { llvm::LLVMConstNeg(te) } + } + } + } + ast::expr_cast(base, tp) { + let ety = ty::expr_ty(cx.tcx, e), llty = type_of::type_of(cx, ety); + let basety = ty::expr_ty(cx.tcx, base); + let v = const_expr(cx, base); + alt check (base::cast_type_kind(basety), base::cast_type_kind(ety)) { + (base::cast_integral, base::cast_integral) { + let s = if ty::type_is_signed(basety) { True } else { False }; + llvm::LLVMConstIntCast(v, llty, s) + } + (base::cast_integral, base::cast_float) { + if ty::type_is_signed(basety) { llvm::LLVMConstSIToFP(v, llty) } + else { llvm::LLVMConstUIToFP(v, llty) } + } + (base::cast_float, base::cast_float) { + llvm::LLVMConstFPCast(v, llty) + } + (base::cast_float, base::cast_integral) { + if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) } + else { llvm::LLVMConstFPToUI(v, llty) } + } + } + } + ast::expr_tup(es) { + C_struct(es.map(|e| const_expr(cx, e))) + } + ast::expr_rec(fs, none) { + C_struct(fs.map(|f| const_expr(cx, f.node.expr))) + } + ast::expr_path(path) { + alt cx.tcx.def_map.find(e.id) { + some(ast::def_const(def_id)) { + // Don't know how to handle external consts + assert ast_util::is_local(def_id); + alt cx.tcx.items.get(def_id.node) { + ast_map::node_item(@{ + node: ast::item_const(_, subexpr), _ + }, _) { + // FIXME (#2530): Instead of recursing here to regenerate + // the values for other constants, we should just look up + // the already-defined value. + const_expr(cx, subexpr) + } + _ { + cx.sess.span_bug(e.span, ~"expected item"); + } + } + } + _ { cx.sess.span_bug(e.span, ~"expected to find a const def") } + } + } + _ { cx.sess.span_bug(e.span, + ~"bad constant expression type in consts::const_expr"); } + } +} + +fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) { + let _icx = ccx.insn_ctxt(~"trans_const"); + let v = const_expr(ccx, e); + + // The scalars come back as 1st class LLVM vals + // which we have to stick into global constants. + let g = base::get_item_val(ccx, id); + llvm::LLVMSetInitializer(g, v); + llvm::LLVMSetGlobalConstant(g, True); +} diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 074346cd691..0c0b2764f3b 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -40,6 +40,7 @@ import back_ = back; mod middle { mod trans { mod common; + mod consts; mod type_of; mod build; mod base; diff --git a/src/test/run-pass/const-rec-and-tup.rs b/src/test/run-pass/const-rec-and-tup.rs new file mode 100644 index 00000000000..9f4b1785b7c --- /dev/null +++ b/src/test/run-pass/const-rec-and-tup.rs @@ -0,0 +1,13 @@ +const x : (int,int) = (0xfeedf00dd,0xca11ab1e); +const y : { x: (int, int), + y: { a: float, + b: float } } = { x: (0xf0f0f0f0_f0f0f0f0, + 0xabababab_abababab), + y: { a: 3.14159265358979323846, + b: 2.7182818284590452354 } }; + +fn main() { + let (p, _) = y.x; + assert p == 0xf0f0f0f0_f0f0f0f0; + io::println(fmt!{"0x%x", p as uint}); +}