From 5571ca9b21f9b6aa9f8877700729564f829a6bef Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Thu, 7 Mar 2013 12:34:39 -0800 Subject: [PATCH 1/8] Break out const_addr_of. --- src/librustc/middle/trans/consts.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 8971d9f56699e..ad6b412d3d80d 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -94,6 +94,17 @@ pub fn const_vec(cx: @CrateContext, e: @ast::expr, es: &[@ast::expr]) } } +fn const_addr_of(cx: @CrateContext, cv: ValueRef) -> ValueRef { + unsafe { + let gv = do str::as_c_str("const") |name| { + llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name) + }; + llvm::LLVMSetInitializer(gv, cv); + llvm::LLVMSetGlobalConstant(gv, True); + gv + } +} + pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef { unsafe { let v = match cx.const_globals.find(&(v as int)) { @@ -355,13 +366,7 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { } } ast::expr_addr_of(ast::m_imm, sub) => { - let cv = const_expr(cx, sub); - let gv = do str::as_c_str("const") |name| { - llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name) - }; - llvm::LLVMSetInitializer(gv, cv); - llvm::LLVMSetGlobalConstant(gv, True); - gv + const_addr_of(cx, const_expr(cx, sub)) } ast::expr_tup(es) => { let ety = ty::expr_ty(cx.tcx, e); From 5ca1d353de16883a29732427b902501b7674fbe5 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Thu, 7 Mar 2013 12:34:58 -0800 Subject: [PATCH 2/8] Implement most of adjustment handling for consts. Changes the ad-hoc closure adjustment into using adjustment info instead of being separately driven from types, and likewise for autoderef. Also takes care of autoref (the cases we should be seeing in consts, at least, since we can't be doing method calls which would need the ref-to-vec mode), which didn't quite work right previously. However, "dereference" of a newtype isn't handled yet.... --- src/librustc/middle/trans/consts.rs | 93 ++++++++++++++++------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index ad6b412d3d80d..c570188b8d25e 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -10,6 +10,7 @@ use core::prelude::*; +use back::abi; use lib::llvm::{llvm, ValueRef, TypeRef, Bool, True, False}; use metadata::csearch; use middle::const_eval; @@ -117,22 +118,6 @@ pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef { } } -pub fn const_autoderef(cx: @CrateContext, ty: ty::t, v: ValueRef) - -> (ty::t, ValueRef) { - let mut t1 = ty; - let mut v1 = v; - loop { - // Only rptrs can be autoderef'ed in a const context. - match ty::get(t1).sty { - ty::ty_rptr(_, mt) => { - t1 = mt.ty; - v1 = const_deref(cx, v1); - } - _ => return (t1,v1) - } - } -} - pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef { let mut def_id = def_id; if !ast_util::is_local(def_id) || @@ -153,15 +138,59 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef { } pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { - let ety = ty::expr_ty_adjusted(cx.tcx, e); - let llty = type_of::sizing_type_of(cx, ety); - let llconst = const_expr_unchecked(cx, e); + let mut llconst = const_expr_unadjusted(cx, e); + let ety = ty::expr_ty(cx.tcx, e); + match cx.tcx.adjustments.find(&e.id) { + None => { } + Some(@ty::AutoAddEnv(ty::re_static, ast::BorrowedSigil)) => { + llconst = C_struct(~[llconst, C_null(T_opaque_box_ptr(cx))]) + } + Some(@ty::AutoAddEnv(ref r, ref s)) => { + cx.sess.span_bug(e.span, fmt!("unexpected const function: \ + region %? sigil %?", *r, *s)) + } + Some(@ty::AutoDerefRef(ref adj)) => { + for adj.autoderefs.times { + llconst = const_deref(cx, llconst) + } + + match adj.autoref { + None => { } + Some(ref autoref) => { + fail_unless!(autoref.region == ty::re_static); + fail_unless!(autoref.mutbl != ast::m_mutbl); + match autoref.kind { + ty::AutoPtr => { + llconst = const_addr_of(cx, llconst); + } + ty::AutoBorrowVec => { + let base = const_addr_of(cx, llconst); + let size = machine::llsize_of(cx, + val_ty(llconst)); + fail_unless!(abi::slice_elt_base == 0); + fail_unless!(abi::slice_elt_len == 1); + llconst = C_struct(~[base, size]); + } + _ => { + cx.sess.span_bug(e.span, + fmt!("unimplemented const \ + autoref %?", autoref)) + } + } + } + } + } + } + + let ety_adjusted = ty::expr_ty_adjusted(cx.tcx, e); + let llty = type_of::sizing_type_of(cx, ety_adjusted); let csize = machine::llsize_of_alloc(cx, val_ty(llconst)); let tsize = machine::llsize_of_alloc(cx, llty); if csize != tsize { unsafe { + // XXX these values could use some context llvm::LLVMDumpValue(llconst); - llvm::LLVMDumpValue(C_null(llty)); + llvm::LLVMDumpValue(C_undef(llty)); } cx.sess.bug(fmt!("const %s of type %s has size %u instead of %u", expr_repr(cx.tcx, e), ty_to_str(cx.tcx, ety), @@ -170,7 +199,7 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { llconst } -fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { +fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { unsafe { let _icx = cx.insn_ctxt("const_expr"); return match /*bad*/copy e.node { @@ -254,10 +283,9 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { } } ast::expr_field(base, field, _) => { - let bt = ty::expr_ty(cx.tcx, base); + let bt = ty::expr_ty_adjusted(cx.tcx, base); let brepr = adt::represent_type(cx, bt); let bv = const_expr(cx, base); - let (bt, bv) = const_autoderef(cx, bt, bv); do expr::with_field_tys(cx.tcx, bt, None) |discr, field_tys| { let ix = ty::field_idx_strict(cx.tcx, field, field_tys); adt::const_get_field(cx, brepr, bv, discr, ix) @@ -265,9 +293,8 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { } ast::expr_index(base, index) => { - let bt = ty::expr_ty(cx.tcx, base); + let bt = ty::expr_ty_adjusted(cx.tcx, base); let bv = const_expr(cx, base); - let (bt, bv) = const_autoderef(cx, bt, bv); 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, @@ -425,26 +452,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { fail_unless!(pth.types.len() == 0); match cx.tcx.def_map.find(&e.id) { Some(ast::def_fn(def_id, _purity)) => { - let f = if !ast_util::is_local(def_id) { + if !ast_util::is_local(def_id) { let ty = csearch::get_type(cx.tcx, def_id).ty; base::trans_external_path(cx, def_id, ty) } else { fail_unless!(ast_util::is_local(def_id)); base::get_item_val(cx, def_id.node) - }; - let ety = ty::expr_ty_adjusted(cx.tcx, e); - match ty::get(ety).sty { - ty::ty_bare_fn(*) | ty::ty_ptr(*) => { - llvm::LLVMConstPointerCast(f, T_ptr(T_i8())) - } - ty::ty_closure(*) => { - C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) - } - _ => { - cx.sess.span_bug(e.span, fmt!( - "unexpected const fn type: %s", - ty_to_str(cx.tcx, ety))) - } } } Some(ast::def_const(def_id)) => { From e42afd3129e502f219479916f5705c784330597f Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 8 Mar 2013 10:22:48 -0800 Subject: [PATCH 3/8] Let const_deref "dereference" newtypes. --- src/librustc/middle/trans/consts.rs | 57 ++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index c570188b8d25e..b390be59f9000 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -106,15 +106,46 @@ fn const_addr_of(cx: @CrateContext, cv: ValueRef) -> ValueRef { } } -pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef { +fn const_deref_ptr(cx: @CrateContext, v: ValueRef) -> ValueRef { + let v = match cx.const_globals.find(&(v as int)) { + Some(v) => v, + None => v + }; unsafe { - let v = match cx.const_globals.find(&(v as int)) { - Some(v) => v, - None => v - }; fail_unless!(llvm::LLVMIsGlobalConstant(v) == True); - let v = llvm::LLVMGetInitializer(v); - v + llvm::LLVMGetInitializer(v) + } +} + +fn const_deref_newtype(cx: @CrateContext, v: ValueRef, t: ty::t) + -> ValueRef { + let repr = adt::represent_type(cx, t); + adt::const_get_field(cx, repr, v, 0, 0) +} + +fn const_deref(cx: @CrateContext, v: ValueRef, t: ty::t, explicit: bool) + -> (ValueRef, ty::t) { + match ty::deref(cx.tcx, t, explicit) { + Some(ref mt) => { + fail_unless!(mt.mutbl != ast::m_mutbl); + let dv = match ty::get(t).sty { + ty::ty_ptr(*) | ty::ty_rptr(*) => { + const_deref_ptr(cx, v) + } + ty::ty_enum(*) | ty::ty_struct(*) => { + const_deref_newtype(cx, v, t) + } + _ => { + cx.sess.bug(fmt!("Unexpected dereferenceable type %s", + ty_to_str(cx.tcx, t))) + } + }; + (dv, mt.ty) + } + None => { + cx.sess.bug(fmt!("Can't dereference const of type %s", + ty_to_str(cx.tcx, t))) + } } } @@ -150,8 +181,11 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { region %? sigil %?", *r, *s)) } Some(@ty::AutoDerefRef(ref adj)) => { + let mut ty = ety; for adj.autoderefs.times { - llconst = const_deref(cx, llconst) + let (dv, dt) = const_deref(cx, llconst, ty, false); + llconst = dv; + ty = dt; } match adj.autoref { @@ -263,7 +297,10 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { return match u { ast::box(_) | ast::uniq(_) | - ast::deref => const_deref(cx, te), + ast::deref => { + let (dv, _dt) = const_deref(cx, te, ty, true); + dv + } ast::not => { match ty::get(ty).sty { ty::ty_bool => { @@ -313,7 +350,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { let llunitty = type_of::type_of(cx, unit_ty); let unit_sz = machine::llsize_of(cx, llunitty); - (const_deref(cx, const_get_elt(cx, bv, [0])), + (const_deref_ptr(cx, const_get_elt(cx, bv, [0])), llvm::LLVMConstUDiv(const_get_elt(cx, bv, [1]), unit_sz)) }, From 2e29f974230f8deeec103ab53a93e7f0f2dbf0fe Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 8 Mar 2013 10:57:24 -0800 Subject: [PATCH 4/8] Make a const translation test match TESTNAME=const --- src/test/run-pass/{enum-cast.rs => const-enum-cast.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/run-pass/{enum-cast.rs => const-enum-cast.rs} (100%) diff --git a/src/test/run-pass/enum-cast.rs b/src/test/run-pass/const-enum-cast.rs similarity index 100% rename from src/test/run-pass/enum-cast.rs rename to src/test/run-pass/const-enum-cast.rs From 5066bc9ff48131e4ee91fa9120c2d5894597216c Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 8 Mar 2013 18:43:40 -0800 Subject: [PATCH 5/8] Don't print addr_of(addr_of(e)) as `&&e`, which means something else. --- src/libsyntax/print/pprust.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 3744b7a8f6c03..94a2559100738 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1217,6 +1217,11 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { ast::expr_addr_of(m, expr) => { word(s.s, ~"&"); print_mutability(s, m); + // Avoid `& &e` => `&&e`. + match (m, &expr.node) { + (ast::m_imm, &ast::expr_addr_of(*)) => space(s.s), + _ => { } + } print_expr(s, expr); } ast::expr_lit(lit) => print_literal(s, lit), From c929ddde1650b4cde91efd435d7807dd313c2e9b Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 8 Mar 2013 10:57:28 -0800 Subject: [PATCH 6/8] Add some tests for const auto-deref. const-autoderef.rs is based on an example from jdm in IRC (but the quadruple-pointer part is entirely my fault). --- src/test/run-pass/const-autoderef-newtype.rs | 17 +++++++++++++++++ src/test/run-pass/const-autoderef.rs | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/test/run-pass/const-autoderef-newtype.rs create mode 100644 src/test/run-pass/const-autoderef.rs diff --git a/src/test/run-pass/const-autoderef-newtype.rs b/src/test/run-pass/const-autoderef-newtype.rs new file mode 100644 index 0000000000000..cb56ab3633565 --- /dev/null +++ b/src/test/run-pass/const-autoderef-newtype.rs @@ -0,0 +1,17 @@ +// Copyright 2013 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. + +struct S(&'static [int]); +const C0: S = S([3]); +const C1: int = C0[0]; + +pub fn main() { + fail_unless!(C1 == 3); +} diff --git a/src/test/run-pass/const-autoderef.rs b/src/test/run-pass/const-autoderef.rs new file mode 100644 index 0000000000000..9fb6c4aa0dbd9 --- /dev/null +++ b/src/test/run-pass/const-autoderef.rs @@ -0,0 +1,19 @@ +// Copyright 2013 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. + +const A: [u8 * 1] = ['h' as u8]; +const B: u8 = (&A)[0]; +const C: &'static &'static &'static &'static [u8 * 1] = & & & &A; +const D: u8 = (&C)[0]; + +pub fn main() { + fail_unless!(B == A[0]); + fail_unless!(D == A[0]); +} From 122e4a28b83ca0d3b3aef9b39eed414da54c4523 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 8 Mar 2013 11:29:47 -0800 Subject: [PATCH 7/8] Don't copy const data to do an autoderef+autoref. --- src/librustc/middle/trans/consts.rs | 12 +++++++++--- src/test/run-pass/const-region-ptrs-noncopy.rs | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/test/run-pass/const-region-ptrs-noncopy.rs diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index b390be59f9000..1d1fb13db0c6f 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -182,8 +182,10 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { } Some(@ty::AutoDerefRef(ref adj)) => { let mut ty = ety; + let mut maybe_ptr = None; for adj.autoderefs.times { let (dv, dt) = const_deref(cx, llconst, ty, false); + maybe_ptr = Some(llconst); llconst = dv; ty = dt; } @@ -193,17 +195,21 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { Some(ref autoref) => { fail_unless!(autoref.region == ty::re_static); fail_unless!(autoref.mutbl != ast::m_mutbl); + // Don't copy data to do a deref+ref. + let llptr = match maybe_ptr { + Some(ptr) => ptr, + None => const_addr_of(cx, llconst) + }; match autoref.kind { ty::AutoPtr => { - llconst = const_addr_of(cx, llconst); + llconst = llptr; } ty::AutoBorrowVec => { - let base = const_addr_of(cx, llconst); let size = machine::llsize_of(cx, val_ty(llconst)); fail_unless!(abi::slice_elt_base == 0); fail_unless!(abi::slice_elt_len == 1); - llconst = C_struct(~[base, size]); + llconst = C_struct(~[llptr, size]); } _ => { cx.sess.span_bug(e.span, diff --git a/src/test/run-pass/const-region-ptrs-noncopy.rs b/src/test/run-pass/const-region-ptrs-noncopy.rs new file mode 100644 index 0000000000000..078ae7661cff4 --- /dev/null +++ b/src/test/run-pass/const-region-ptrs-noncopy.rs @@ -0,0 +1,18 @@ +// Copyright 2013 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. + +type Big = [u64 * 8]; +struct Pair { a: int, b: &'self Big } +const x: &'static Big = &([13, 14, 10, 13, 11, 14, 14, 15]); +const y: &'static Pair<'static> = &Pair {a: 15, b: x}; + +pub fn main() { + fail_unless!(ptr::addr_of(x) == ptr::addr_of(y.b)); +} From 1df0a0ba0f04e15d74307088dd3c952dd61f2183 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 8 Mar 2013 12:41:40 -0800 Subject: [PATCH 8/8] Const dereference works now, so allow it. --- src/librustc/middle/check_const.rs | 4 ++-- src/test/run-pass/const-deref.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/const-deref.rs diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 9d7a041ca0990..7d228f7687147 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -91,8 +91,8 @@ pub fn check_expr(sess: Session, v: visit::vt) { if is_const { match e.node { - expr_unary(box(_), _) | expr_unary(uniq(_), _) | - expr_unary(deref, _) => { + expr_unary(deref, _) => { } + expr_unary(box(_), _) | expr_unary(uniq(_), _) => { sess.span_err(e.span, ~"disallowed operator in constant expression"); return; diff --git a/src/test/run-pass/const-deref.rs b/src/test/run-pass/const-deref.rs new file mode 100644 index 0000000000000..71ae273aaa3b2 --- /dev/null +++ b/src/test/run-pass/const-deref.rs @@ -0,0 +1,20 @@ +// Copyright 2013 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. + +const C: &'static int = &1000; +const D: int = *C; +struct S(&'static int); +const E: &'static S = &S(C); +const F: int = ***E; + +pub fn main() { + fail_unless!(D == 1000); + fail_unless!(F == 1000); +}