Skip to content

Commit 9b9ffd5

Browse files
committed
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.
2 parents 48cb9a8 + 1df0a0b commit 9b9ffd5

File tree

8 files changed

+193
-53
lines changed

8 files changed

+193
-53
lines changed

src/librustc/middle/check_const.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ pub fn check_expr(sess: Session,
9191
v: visit::vt<bool>) {
9292
if is_const {
9393
match e.node {
94-
expr_unary(box(_), _) | expr_unary(uniq(_), _) |
95-
expr_unary(deref, _) => {
94+
expr_unary(deref, _) => { }
95+
expr_unary(box(_), _) | expr_unary(uniq(_), _) => {
9696
sess.span_err(e.span,
9797
~"disallowed operator in constant expression");
9898
return;

src/librustc/middle/trans/consts.rs

Lines changed: 112 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use core::prelude::*;
1212

13+
use back::abi;
1314
use lib::llvm::{llvm, ValueRef, TypeRef, Bool, True, False};
1415
use metadata::csearch;
1516
use middle::const_eval;
@@ -94,30 +95,56 @@ pub fn const_vec(cx: @CrateContext, e: @ast::expr, es: &[@ast::expr])
9495
}
9596
}
9697

97-
pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef {
98+
fn const_addr_of(cx: @CrateContext, cv: ValueRef) -> ValueRef {
9899
unsafe {
99-
let v = match cx.const_globals.find(&(v as int)) {
100-
Some(v) => v,
101-
None => v
100+
let gv = do str::as_c_str("const") |name| {
101+
llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name)
102102
};
103+
llvm::LLVMSetInitializer(gv, cv);
104+
llvm::LLVMSetGlobalConstant(gv, True);
105+
gv
106+
}
107+
}
108+
109+
fn const_deref_ptr(cx: @CrateContext, v: ValueRef) -> ValueRef {
110+
let v = match cx.const_globals.find(&(v as int)) {
111+
Some(v) => v,
112+
None => v
113+
};
114+
unsafe {
103115
fail_unless!(llvm::LLVMIsGlobalConstant(v) == True);
104-
let v = llvm::LLVMGetInitializer(v);
105-
v
116+
llvm::LLVMGetInitializer(v)
106117
}
107118
}
108119

109-
pub fn const_autoderef(cx: @CrateContext, ty: ty::t, v: ValueRef)
110-
-> (ty::t, ValueRef) {
111-
let mut t1 = ty;
112-
let mut v1 = v;
113-
loop {
114-
// Only rptrs can be autoderef'ed in a const context.
115-
match ty::get(t1).sty {
116-
ty::ty_rptr(_, mt) => {
117-
t1 = mt.ty;
118-
v1 = const_deref(cx, v1);
119-
}
120-
_ => return (t1,v1)
120+
fn const_deref_newtype(cx: @CrateContext, v: ValueRef, t: ty::t)
121+
-> ValueRef {
122+
let repr = adt::represent_type(cx, t);
123+
adt::const_get_field(cx, repr, v, 0, 0)
124+
}
125+
126+
fn const_deref(cx: @CrateContext, v: ValueRef, t: ty::t, explicit: bool)
127+
-> (ValueRef, ty::t) {
128+
match ty::deref(cx.tcx, t, explicit) {
129+
Some(ref mt) => {
130+
fail_unless!(mt.mutbl != ast::m_mutbl);
131+
let dv = match ty::get(t).sty {
132+
ty::ty_ptr(*) | ty::ty_rptr(*) => {
133+
const_deref_ptr(cx, v)
134+
}
135+
ty::ty_enum(*) | ty::ty_struct(*) => {
136+
const_deref_newtype(cx, v, t)
137+
}
138+
_ => {
139+
cx.sess.bug(fmt!("Unexpected dereferenceable type %s",
140+
ty_to_str(cx.tcx, t)))
141+
}
142+
};
143+
(dv, mt.ty)
144+
}
145+
None => {
146+
cx.sess.bug(fmt!("Can't dereference const of type %s",
147+
ty_to_str(cx.tcx, t)))
121148
}
122149
}
123150
}
@@ -142,15 +169,68 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef {
142169
}
143170

144171
pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
145-
let ety = ty::expr_ty_adjusted(cx.tcx, e);
146-
let llty = type_of::sizing_type_of(cx, ety);
147-
let llconst = const_expr_unchecked(cx, e);
172+
let mut llconst = const_expr_unadjusted(cx, e);
173+
let ety = ty::expr_ty(cx.tcx, e);
174+
match cx.tcx.adjustments.find(&e.id) {
175+
None => { }
176+
Some(@ty::AutoAddEnv(ty::re_static, ast::BorrowedSigil)) => {
177+
llconst = C_struct(~[llconst, C_null(T_opaque_box_ptr(cx))])
178+
}
179+
Some(@ty::AutoAddEnv(ref r, ref s)) => {
180+
cx.sess.span_bug(e.span, fmt!("unexpected const function: \
181+
region %? sigil %?", *r, *s))
182+
}
183+
Some(@ty::AutoDerefRef(ref adj)) => {
184+
let mut ty = ety;
185+
let mut maybe_ptr = None;
186+
for adj.autoderefs.times {
187+
let (dv, dt) = const_deref(cx, llconst, ty, false);
188+
maybe_ptr = Some(llconst);
189+
llconst = dv;
190+
ty = dt;
191+
}
192+
193+
match adj.autoref {
194+
None => { }
195+
Some(ref autoref) => {
196+
fail_unless!(autoref.region == ty::re_static);
197+
fail_unless!(autoref.mutbl != ast::m_mutbl);
198+
// Don't copy data to do a deref+ref.
199+
let llptr = match maybe_ptr {
200+
Some(ptr) => ptr,
201+
None => const_addr_of(cx, llconst)
202+
};
203+
match autoref.kind {
204+
ty::AutoPtr => {
205+
llconst = llptr;
206+
}
207+
ty::AutoBorrowVec => {
208+
let size = machine::llsize_of(cx,
209+
val_ty(llconst));
210+
fail_unless!(abi::slice_elt_base == 0);
211+
fail_unless!(abi::slice_elt_len == 1);
212+
llconst = C_struct(~[llptr, size]);
213+
}
214+
_ => {
215+
cx.sess.span_bug(e.span,
216+
fmt!("unimplemented const \
217+
autoref %?", autoref))
218+
}
219+
}
220+
}
221+
}
222+
}
223+
}
224+
225+
let ety_adjusted = ty::expr_ty_adjusted(cx.tcx, e);
226+
let llty = type_of::sizing_type_of(cx, ety_adjusted);
148227
let csize = machine::llsize_of_alloc(cx, val_ty(llconst));
149228
let tsize = machine::llsize_of_alloc(cx, llty);
150229
if csize != tsize {
151230
unsafe {
231+
// XXX these values could use some context
152232
llvm::LLVMDumpValue(llconst);
153-
llvm::LLVMDumpValue(C_null(llty));
233+
llvm::LLVMDumpValue(C_undef(llty));
154234
}
155235
cx.sess.bug(fmt!("const %s of type %s has size %u instead of %u",
156236
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 {
159239
llconst
160240
}
161241

162-
fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
242+
fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef {
163243
unsafe {
164244
let _icx = cx.insn_ctxt("const_expr");
165245
return match /*bad*/copy e.node {
@@ -223,7 +303,10 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
223303
return match u {
224304
ast::box(_) |
225305
ast::uniq(_) |
226-
ast::deref => const_deref(cx, te),
306+
ast::deref => {
307+
let (dv, _dt) = const_deref(cx, te, ty, true);
308+
dv
309+
}
227310
ast::not => {
228311
match ty::get(ty).sty {
229312
ty::ty_bool => {
@@ -243,20 +326,18 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
243326
}
244327
}
245328
ast::expr_field(base, field, _) => {
246-
let bt = ty::expr_ty(cx.tcx, base);
329+
let bt = ty::expr_ty_adjusted(cx.tcx, base);
247330
let brepr = adt::represent_type(cx, bt);
248331
let bv = const_expr(cx, base);
249-
let (bt, bv) = const_autoderef(cx, bt, bv);
250332
do expr::with_field_tys(cx.tcx, bt, None) |discr, field_tys| {
251333
let ix = ty::field_idx_strict(cx.tcx, field, field_tys);
252334
adt::const_get_field(cx, brepr, bv, discr, ix)
253335
}
254336
}
255337

256338
ast::expr_index(base, index) => {
257-
let bt = ty::expr_ty(cx.tcx, base);
339+
let bt = ty::expr_ty_adjusted(cx.tcx, base);
258340
let bv = const_expr(cx, base);
259-
let (bt, bv) = const_autoderef(cx, bt, bv);
260341
let iv = match const_eval::eval_const_expr(cx.tcx, index) {
261342
const_eval::const_int(i) => i as u64,
262343
const_eval::const_uint(u) => u,
@@ -275,7 +356,7 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
275356
let llunitty = type_of::type_of(cx, unit_ty);
276357
let unit_sz = machine::llsize_of(cx, llunitty);
277358

278-
(const_deref(cx, const_get_elt(cx, bv, [0])),
359+
(const_deref_ptr(cx, const_get_elt(cx, bv, [0])),
279360
llvm::LLVMConstUDiv(const_get_elt(cx, bv, [1]),
280361
unit_sz))
281362
},
@@ -355,13 +436,7 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
355436
}
356437
}
357438
ast::expr_addr_of(ast::m_imm, sub) => {
358-
let cv = const_expr(cx, sub);
359-
let gv = do str::as_c_str("const") |name| {
360-
llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name)
361-
};
362-
llvm::LLVMSetInitializer(gv, cv);
363-
llvm::LLVMSetGlobalConstant(gv, True);
364-
gv
439+
const_addr_of(cx, const_expr(cx, sub))
365440
}
366441
ast::expr_tup(es) => {
367442
let ety = ty::expr_ty(cx.tcx, e);
@@ -420,26 +495,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
420495
fail_unless!(pth.types.len() == 0);
421496
match cx.tcx.def_map.find(&e.id) {
422497
Some(ast::def_fn(def_id, _purity)) => {
423-
let f = if !ast_util::is_local(def_id) {
498+
if !ast_util::is_local(def_id) {
424499
let ty = csearch::get_type(cx.tcx, def_id).ty;
425500
base::trans_external_path(cx, def_id, ty)
426501
} else {
427502
fail_unless!(ast_util::is_local(def_id));
428503
base::get_item_val(cx, def_id.node)
429-
};
430-
let ety = ty::expr_ty_adjusted(cx.tcx, e);
431-
match ty::get(ety).sty {
432-
ty::ty_bare_fn(*) | ty::ty_ptr(*) => {
433-
llvm::LLVMConstPointerCast(f, T_ptr(T_i8()))
434-
}
435-
ty::ty_closure(*) => {
436-
C_struct(~[f, C_null(T_opaque_box_ptr(cx))])
437-
}
438-
_ => {
439-
cx.sess.span_bug(e.span, fmt!(
440-
"unexpected const fn type: %s",
441-
ty_to_str(cx.tcx, ety)))
442-
}
443504
}
444505
}
445506
Some(ast::def_const(def_id)) => {

src/libsyntax/print/pprust.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,11 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) {
11921192
ast::expr_addr_of(m, expr) => {
11931193
word(s.s, ~"&");
11941194
print_mutability(s, m);
1195+
// Avoid `& &e` => `&&e`.
1196+
match (m, &expr.node) {
1197+
(ast::m_imm, &ast::expr_addr_of(*)) => space(s.s),
1198+
_ => { }
1199+
}
11951200
print_expr(s, expr);
11961201
}
11971202
ast::expr_lit(lit) => print_literal(s, lit),
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct S(&'static [int]);
12+
const C0: S = S([3]);
13+
const C1: int = C0[0];
14+
15+
pub fn main() {
16+
fail_unless!(C1 == 3);
17+
}

src/test/run-pass/const-autoderef.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
const A: [u8 * 1] = ['h' as u8];
12+
const B: u8 = (&A)[0];
13+
const C: &'static &'static &'static &'static [u8 * 1] = & & & &A;
14+
const D: u8 = (&C)[0];
15+
16+
pub fn main() {
17+
fail_unless!(B == A[0]);
18+
fail_unless!(D == A[0]);
19+
}

src/test/run-pass/const-deref.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
const C: &'static int = &1000;
12+
const D: int = *C;
13+
struct S(&'static int);
14+
const E: &'static S = &S(C);
15+
const F: int = ***E;
16+
17+
pub fn main() {
18+
fail_unless!(D == 1000);
19+
fail_unless!(F == 1000);
20+
}
File renamed without changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
type Big = [u64 * 8];
12+
struct Pair { a: int, b: &'self Big }
13+
const x: &'static Big = &([13, 14, 10, 13, 11, 14, 14, 15]);
14+
const y: &'static Pair<'static> = &Pair {a: 15, b: x};
15+
16+
pub fn main() {
17+
fail_unless!(ptr::addr_of(x) == ptr::addr_of(y.b));
18+
}

0 commit comments

Comments
 (0)