auto merge of #5304 : jld/rust/const-adjustments, r=graydon

These changes make const translation use adjustments (autodereference, autoreference, bare-fn-to-closure), like normal code does, replacing some ad-hoc logic that wasn't always right.

As a convenient side-effect, explicit dereference (both of pointers and of newtypes) is also supported in const expressions.

There is also a “bonus fix” for a bug in the pretty-printer exposed by one of the added tests.
This commit is contained in:
bors 2013-03-11 21:12:43 -07:00
commit 9b9ffd5b41
8 changed files with 194 additions and 54 deletions

View file

@ -91,8 +91,8 @@ pub fn check_expr(sess: Session,
v: visit::vt<bool>) {
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;

View file

@ -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)) => {

View file

@ -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),

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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]);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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));
}