diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 9d7a041ca09..7d228f76871 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/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 8971d9f5669..1d1fb13db0c 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; @@ -94,30 +95,56 @@ pub fn const_vec(cx: @CrateContext, e: @ast::expr, es: &[@ast::expr]) } } -pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef { +fn const_addr_of(cx: @CrateContext, cv: ValueRef) -> ValueRef { unsafe { - let v = match cx.const_globals.find(&(v as int)) { - Some(v) => v, - None => v + let gv = do str::as_c_str("const") |name| { + llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name) }; - fail_unless!(llvm::LLVMIsGlobalConstant(v) == True); - let v = llvm::LLVMGetInitializer(v); - v + llvm::LLVMSetInitializer(gv, cv); + llvm::LLVMSetGlobalConstant(gv, True); + gv } } -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) +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 { + fail_unless!(llvm::LLVMIsGlobalConstant(v) == True); + 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))) } } } @@ -142,15 +169,68 @@ 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)) => { + 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; + } + + match adj.autoref { + None => { } + 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 = llptr; + } + ty::AutoBorrowVec => { + 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(~[llptr, 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), @@ -159,7 +239,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 { @@ -223,7 +303,10 @@ fn const_expr_unchecked(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 => { @@ -243,10 +326,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) @@ -254,9 +336,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, @@ -275,7 +356,7 @@ fn const_expr_unchecked(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)) }, @@ -355,13 +436,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); @@ -420,26 +495,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)) => { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 49899fdeec4..62f593f15c1 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1192,6 +1192,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), 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 00000000000..cb56ab36335 --- /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 00000000000..9fb6c4aa0db --- /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]); +} diff --git a/src/test/run-pass/const-deref.rs b/src/test/run-pass/const-deref.rs new file mode 100644 index 00000000000..71ae273aaa3 --- /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); +} 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 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 00000000000..078ae7661cf --- /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)); +}