From 92e3a8c17e9b7cb61b18791aea0544794a315bbb Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Tue, 6 Nov 2012 17:13:52 -0800 Subject: [PATCH] rustc: reuse const vals, translate fn paths as consts. Close #2530. --- src/libcore/condition.rs | 18 ++---------- src/rustc/middle/check_const.rs | 19 ++++++++++--- src/rustc/middle/trans/base.rs | 1 + src/rustc/middle/trans/common.rs | 15 ++++++++-- src/rustc/middle/trans/consts.rs | 47 +++++++++++++++---------------- src/test/run-pass/const-fn-val.rs | 11 ++++++++ 6 files changed, 65 insertions(+), 46 deletions(-) create mode 100644 src/test/run-pass/const-fn-val.rs diff --git a/src/libcore/condition.rs b/src/libcore/condition.rs index dd5f9752137..30b25403a4e 100644 --- a/src/libcore/condition.rs +++ b/src/libcore/condition.rs @@ -96,18 +96,16 @@ struct Guard { mod test { fn sadness_key(_x: @Handler) { } + const sadness_condition : Condition = + Condition { key: sadness_key }; + fn trouble(i: int) { - // Condition should work as a const, just limitations in consts. - let sadness_condition : Condition = - Condition { key: sadness_key }; debug!("trouble: raising conition"); let j = sadness_condition.raise(&i); debug!("trouble: handler recovered with %d", j); } fn nested_trap_test_inner() { - let sadness_condition : Condition = - Condition { key: sadness_key }; let mut inner_trapped = false; @@ -126,9 +124,6 @@ fn nested_trap_test_inner() { #[test] fn nested_trap_test_outer() { - let sadness_condition : Condition = - Condition { key: sadness_key }; - let mut outer_trapped = false; do sadness_condition.trap(|_j| { @@ -144,8 +139,6 @@ fn nested_trap_test_outer() { } fn nested_reraise_trap_test_inner() { - let sadness_condition : Condition = - Condition { key: sadness_key }; let mut inner_trapped = false; @@ -166,9 +159,6 @@ fn nested_reraise_trap_test_inner() { #[test] fn nested_reraise_trap_test_outer() { - let sadness_condition : Condition = - Condition { key: sadness_key }; - let mut outer_trapped = false; do sadness_condition.trap(|_j| { @@ -184,8 +174,6 @@ fn nested_reraise_trap_test_outer() { #[test] fn test_default() { - let sadness_condition : Condition = - Condition { key: sadness_key }; let mut trapped = false; diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs index 482ab3041f4..6eb592942e1 100644 --- a/src/rustc/middle/check_const.rs +++ b/src/rustc/middle/check_const.rs @@ -82,19 +82,30 @@ fn check_expr(sess: Session, def_map: resolve::DefMap, ~"` in a constant expression"); } } - expr_path(_) => { + expr_path(pth) => { + // NB: In the future you might wish to relax this slightly + // to handle on-demand instantiation of functions via + // foo:: in a const. Currently that is only done on + // a path in trans::callee that only works in block contexts. + if pth.types.len() != 0 { + sess.span_err( + e.span, ~"paths in constants may only refer to \ + items without type parameters"); + } match def_map.find(e.id) { - Some(def_const(def_id)) => { + Some(def_const(def_id)) | + Some(def_fn(def_id, _)) => { if !ast_util::is_local(def_id) { sess.span_err( e.span, ~"paths in constants may only refer to \ - crate-local constants"); + crate-local constants or functions"); } } _ => { sess.span_err( e.span, - ~"paths in constants may only refer to constants"); + ~"paths in constants may only refer to \ + constants or functions"); } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 4668f36ecf8..a2d74dc92df 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -2688,6 +2688,7 @@ fn trans_crate(sess: session::Session, vtables: map::HashMap(), const_cstr_cache: HashMap(), const_globals: HashMap(), + const_values: HashMap(), module_data: HashMap(), lltypes: ty::new_ty_hash(), names: new_namegen(sess.parse_sess.interner), diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index b02d12e0b73..36d6bb9258b 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -140,10 +140,19 @@ fn BuilderRef_res(B: BuilderRef) -> BuilderRef_res { vtables: HashMap, // Cache of constant strings, const_cstr_cache: HashMap<~str, ValueRef>, - // Reverse-direction for const ptrs cast from globals, - // since the ptr -> init association is lost any - // time a GlobalValue is cast. + + // Reverse-direction for const ptrs cast from globals. + // Key is an int, cast from 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: HashMap, + + // Cache of emitted const values + const_values: HashMap, module_data: HashMap<~str, ValueRef>, lltypes: HashMap, names: namegen, diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs index 54eec8c6de0..23ec0b6a9c1 100644 --- a/src/rustc/middle/trans/consts.rs +++ b/src/rustc/middle/trans/consts.rs @@ -28,10 +28,6 @@ fn const_lit(cx: @crate_ctxt, e: @ast::expr, lit: ast::lit) } } -// 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_ptrcast(cx: @crate_ctxt, a: ValueRef, t: TypeRef) -> ValueRef { let b = llvm::LLVMConstPointerCast(a, T_ptr(t)); assert cx.const_globals.insert(b as int, a); @@ -341,24 +337,29 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { ~"bad const-slice expr") } } - ast::expr_path(_) => { + ast::expr_path(pth) => { + assert pth.types.len() == 0; match 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); - match 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") - } + Some(ast::def_fn(def_id, _)) => { + assert ast_util::is_local(def_id); + let f = base::get_item_val(cx, def_id.node); + C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) } - _ => cx.sess.span_bug(e.span, ~"expected to find a const def") + Some(ast::def_const(def_id)) => { + assert ast_util::is_local(def_id); + if ! cx.const_values.contains_key(def_id.node) { + match cx.tcx.items.get(def_id.node) { + ast_map::node_item(@{ + node: ast::item_const(_, subexpr), _ + }, _) => { + trans_const(cx, subexpr, def_id.node); + } + _ => cx.sess.span_bug(e.span, ~"expected item") + } + } + cx.const_values.get(def_id.node) + } + _ => cx.sess.span_bug(e.span, ~"expected a const or fn def") } } ast::expr_paren(e) => { return const_expr(cx, e); } @@ -369,11 +370,9 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { 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); + let v = const_expr(ccx, e); + ccx.const_values.insert(id, v); llvm::LLVMSetInitializer(g, v); llvm::LLVMSetGlobalConstant(g, True); } diff --git a/src/test/run-pass/const-fn-val.rs b/src/test/run-pass/const-fn-val.rs new file mode 100644 index 00000000000..c37b5b68c06 --- /dev/null +++ b/src/test/run-pass/const-fn-val.rs @@ -0,0 +1,11 @@ +fn foo() -> int { + return 0xca7f000d; +} + +struct Bar { f: &fn() -> int } + +const b : Bar = Bar { f: foo }; + +fn main() { + assert (b.f)() == 0xca7f000d; +} \ No newline at end of file