Skip to content

Commit 7fc2363

Browse files
committed
[breaking-change] eager const evaluator
1 parent 6683fa4 commit 7fc2363

File tree

4 files changed

+173
-119
lines changed

4 files changed

+173
-119
lines changed

src/librustc/middle/const_eval.rs

Lines changed: 114 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use std::borrow::{Cow, IntoCow};
3939
use std::num::wrapping::OverflowingOps;
4040
use std::cmp::Ordering;
4141
use std::collections::hash_map::Entry::Vacant;
42+
use std::collections::HashMap;
4243
use std::hash;
4344
use std::mem::transmute;
4445
use std::{i8, i16, i32, i64, u8, u16, u32, u64};
@@ -253,11 +254,11 @@ pub enum ConstVal {
253254
Str(InternedString),
254255
ByteStr(Rc<Vec<u8>>),
255256
Bool(bool),
256-
Struct(ast::NodeId),
257-
Tuple(ast::NodeId),
257+
Struct(ast::NodeId, HashMap<ast::Name, ConstVal>),
258+
Tuple(Vec<ConstVal>),
258259
Function(DefId),
259-
Array(ast::NodeId, u64),
260-
Repeat(ast::NodeId, u64),
260+
Array(Vec<ConstVal>),
261+
Repeat(Box<ConstVal>, usize),
261262
}
262263

263264
impl hash::Hash for ConstVal {
@@ -269,11 +270,16 @@ impl hash::Hash for ConstVal {
269270
Str(ref a) => a.hash(state),
270271
ByteStr(ref a) => a.hash(state),
271272
Bool(a) => a.hash(state),
272-
Struct(a) => a.hash(state),
273-
Tuple(a) => a.hash(state),
273+
Struct(id, ref a) => {
274+
id.hash(state);
275+
for entry in a {
276+
entry.hash(state);
277+
}
278+
},
279+
Tuple(ref a) => a.hash(state),
274280
Function(a) => a.hash(state),
275-
Array(a, n) => { a.hash(state); n.hash(state) },
276-
Repeat(a, n) => { a.hash(state); n.hash(state) },
281+
Array(ref a) => a.hash(state),
282+
Repeat(ref a, n) => { a.hash(state); n.hash(state) },
277283
}
278284
}
279285
}
@@ -291,11 +297,11 @@ impl PartialEq for ConstVal {
291297
(&Str(ref a), &Str(ref b)) => a == b,
292298
(&ByteStr(ref a), &ByteStr(ref b)) => a == b,
293299
(&Bool(a), &Bool(b)) => a == b,
294-
(&Struct(a), &Struct(b)) => a == b,
295-
(&Tuple(a), &Tuple(b)) => a == b,
300+
(&Struct(id, ref a), &Struct(idb, ref b)) => (id == idb) && (a == b),
301+
(&Tuple(ref a), &Tuple(ref b)) => a == b,
296302
(&Function(a), &Function(b)) => a == b,
297-
(&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn),
298-
(&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn),
303+
(&Array(ref a), &Array(ref b)) => a == b,
304+
(&Repeat(ref a, an), &Repeat(ref b, bn)) => (a == b) && (an == bn),
299305
_ => false,
300306
}
301307
}
@@ -313,7 +319,7 @@ impl ConstVal {
313319
Str(_) => "string literal",
314320
ByteStr(_) => "byte string literal",
315321
Bool(_) => "boolean",
316-
Struct(_) => "struct",
322+
Struct(..) => "struct",
317323
Tuple(_) => "tuple",
318324
Function(_) => "function definition",
319325
Array(..) => "array",
@@ -404,6 +410,8 @@ pub enum ErrKind {
404410
InvalidOpForUintInt(hir::BinOp_),
405411
NegateOn(ConstVal),
406412
NotOn(ConstVal),
413+
BadStructBase(ConstVal),
414+
ExpectedStructDef(def::Def),
407415

408416
NegateWithOverflow(i64),
409417
AddiWithOverflow(i64, i64),
@@ -448,6 +456,10 @@ impl ConstEvalErr {
448456
InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(),
449457
NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(),
450458
NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(),
459+
BadStructBase(ref const_val) =>
460+
format!("bad struct base: `{}`", const_val.description()).into_cow(),
461+
ExpectedStructDef(def) =>
462+
format!("expected struct definition, got: `{:?}`", def).into_cow(),
451463

452464
NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(),
453465
AddiWithOverflow(..) => "attempted to add with overflow".into_cow(),
@@ -838,18 +850,23 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
838850
}
839851
}
840852
hir::ExprBinary(op, ref a, ref b) => {
841-
let b_ty = match op.node {
842-
hir::BiShl | hir::BiShr => {
843-
if let ExprTypeChecked = ty_hint {
844-
ExprTypeChecked
845-
} else {
846-
UncheckedExprHint(tcx.types.usize)
847-
}
853+
let a = try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args));
854+
let b_ty = if let ExprTypeChecked = ty_hint {
855+
ExprTypeChecked
856+
} else {
857+
match op.node {
858+
hir::BiShl | hir::BiShr => UncheckedExprHint(tcx.types.usize),
859+
_ => match a {
860+
Int(_) => UncheckedExprHint(tcx.types.isize),
861+
Uint(_) => UncheckedExprHint(tcx.types.usize),
862+
// don't care about the other types, they don't do much arithmetics
863+
_ => ty_hint,
864+
},
848865
}
849-
_ => ty_hint
850866
};
851-
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args)),
852-
try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args))) {
867+
868+
let b = try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args));
869+
match (a, b) {
853870
(Float(a), Float(b)) => {
854871
match op.node {
855872
hir::BiAdd => Float(a + b),
@@ -1032,7 +1049,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
10321049
(lookup_variant_by_id(tcx, enum_def, variant_def), None)
10331050
}
10341051
Some(def::DefStruct(_)) => {
1035-
return Ok(ConstVal::Struct(e.id))
1052+
// FIXME: necessary to check if struct is a unit struct?
1053+
return Ok(ConstVal::Struct(e.id, HashMap::new()))
10361054
}
10371055
Some(def::DefLocal(_, id)) => {
10381056
debug!("DefLocal({:?}): {:?}", id, fn_args);
@@ -1134,9 +1152,40 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
11341152
None => unreachable!(),
11351153
}
11361154
}
1137-
hir::ExprTup(_) => Tuple(e.id),
1138-
hir::ExprStruct(..) => Struct(e.id),
1155+
hir::ExprTup(ref fields) => {
1156+
let field_hint = if let ExprTypeChecked = ty_hint {
1157+
ExprTypeChecked
1158+
} else {
1159+
UncheckedExprNoHint
1160+
};
1161+
let mut fs = Vec::with_capacity(fields.len());
1162+
for field in fields {
1163+
fs.push(try!(eval_const_expr_partial(tcx, &**field, field_hint, fn_args)))
1164+
}
1165+
Tuple(fs)
1166+
},
1167+
hir::ExprStruct(_, ref fields, ref base_opt) => {
1168+
let mut base = if let Some(ref base) = *base_opt {
1169+
match try!(eval_const_expr_partial(tcx, &**base, ty_hint, fn_args)) {
1170+
Struct(_, base) => base,
1171+
other => signal!(e, BadStructBase(other)),
1172+
}
1173+
} else {
1174+
HashMap::new()
1175+
};
1176+
let field_hint = if let ExprTypeChecked = ty_hint {
1177+
ExprTypeChecked
1178+
} else {
1179+
UncheckedExprNoHint
1180+
};
1181+
for field in fields {
1182+
let val = try!(eval_const_expr_partial(tcx, &*field.expr, field_hint, fn_args));
1183+
base.insert(field.name.node, val);
1184+
}
1185+
Struct(e.id, base)
1186+
},
11391187
hir::ExprIndex(ref arr, ref idx) => {
1188+
//if tcx.sess.features.borrow().const_index
11401189
let arr_hint = if let ExprTypeChecked = ty_hint {
11411190
ExprTypeChecked
11421191
} else {
@@ -1149,26 +1198,16 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
11491198
UncheckedExprHint(tcx.types.usize)
11501199
};
11511200
let idx = match try!(eval_const_expr_partial(tcx, idx, idx_hint, fn_args)) {
1152-
Int(i) if i >= 0 => i as u64,
1201+
Int(i) if i >= 0 => i as usize,
11531202
Int(_) => signal!(idx, IndexNegative),
1154-
Uint(i) => i,
1203+
Uint(i) => i as usize,
11551204
_ => signal!(idx, IndexNotInt),
11561205
};
11571206
match arr {
1158-
Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
1159-
Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node {
1160-
try!(eval_const_expr_partial(tcx, &*v[idx as usize], ty_hint, fn_args))
1161-
} else {
1162-
unreachable!()
1163-
},
1164-
1207+
Array(ref v) if idx >= v.len() => signal!(e, IndexOutOfBounds),
1208+
Array(v) => v[idx as usize].clone(),
11651209
Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
1166-
Repeat(elem, _) => try!(eval_const_expr_partial(
1167-
tcx,
1168-
&*tcx.map.expect_expr(elem),
1169-
ty_hint,
1170-
fn_args,
1171-
)),
1210+
Repeat(elem, _) => *elem,
11721211

11731212
ByteStr(ref data) if idx as usize >= data.len()
11741213
=> signal!(e, IndexOutOfBounds),
@@ -1180,19 +1219,35 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
11801219
_ => signal!(e, IndexedNonVec),
11811220
}
11821221
}
1183-
hir::ExprVec(ref v) => Array(e.id, v.len() as u64),
1184-
hir::ExprRepeat(_, ref n) => {
1222+
hir::ExprVec(ref v) => {
1223+
let hint = if let ExprTypeChecked = ty_hint {
1224+
ExprTypeChecked
1225+
} else {
1226+
UncheckedExprNoHint
1227+
};
1228+
let mut fs = Vec::with_capacity(v.len());
1229+
for val in v {
1230+
fs.push(try!(eval_const_expr_partial(tcx, &**val, hint, fn_args)))
1231+
}
1232+
Array(fs)
1233+
}
1234+
hir::ExprRepeat(ref val, ref n) => {
11851235
let len_hint = if let ExprTypeChecked = ty_hint {
11861236
ExprTypeChecked
11871237
} else {
11881238
UncheckedExprHint(tcx.types.usize)
11891239
};
1240+
let val_hint = if let ExprTypeChecked = ty_hint {
1241+
ExprTypeChecked
1242+
} else {
1243+
UncheckedExprNoHint
1244+
};
11901245
Repeat(
1191-
e.id,
1246+
box try!(eval_const_expr_partial(tcx, &**val, val_hint, fn_args)),
11921247
match try!(eval_const_expr_partial(tcx, &**n, len_hint, fn_args)) {
1193-
Int(i) if i >= 0 => i as u64,
1248+
Int(i) if i >= 0 => i as usize,
11941249
Int(_) => signal!(e, RepeatCountNotNatural),
1195-
Uint(i) => i,
1250+
Uint(i) => i as usize,
11961251
_ => signal!(e, RepeatCountNotInt),
11971252
},
11981253
)
@@ -1203,22 +1258,15 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
12031258
} else {
12041259
UncheckedExprNoHint
12051260
};
1206-
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
1207-
if let Tuple(tup_id) = c {
1208-
if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
1209-
if index.node < fields.len() {
1210-
return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args)
1211-
} else {
1212-
signal!(e, TupleIndexOutOfBounds);
1213-
}
1214-
} else {
1215-
unreachable!()
1216-
}
1261+
let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args));
1262+
if let Tuple(fields) = c {
1263+
if index.node < fields.len() {
1264+
fields[index.node].clone()
12171265
} else {
1218-
signal!(base, ExpectedConstTuple);
1266+
signal!(e, TupleIndexOutOfBounds);
12191267
}
12201268
} else {
1221-
signal!(base, NonConstPath)
1269+
signal!(base, ExpectedConstTuple);
12221270
}
12231271
}
12241272
hir::ExprField(ref base, field_name) => {
@@ -1228,25 +1276,16 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
12281276
} else {
12291277
UncheckedExprNoHint
12301278
};
1231-
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
1232-
if let Struct(struct_id) = c {
1233-
if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
1234-
// Check that the given field exists and evaluate it
1235-
// if the idents are compared run-pass/issue-19244 fails
1236-
if let Some(f) = fields.iter().find(|f| f.name.node
1237-
== field_name.node) {
1238-
return eval_const_expr_partial(tcx, &*f.expr, base_hint, fn_args)
1239-
} else {
1240-
signal!(e, MissingStructField);
1241-
}
1242-
} else {
1243-
unreachable!()
1244-
}
1279+
let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args));
1280+
if let Struct(_, mut fields) = c {
1281+
if let Some(f) = fields.remove(&field_name.node) {
1282+
f
12451283
} else {
1246-
signal!(base, ExpectedConstStruct);
1284+
// FIXME: is this unreachable?
1285+
signal!(e, MissingStructField);
12471286
}
12481287
} else {
1249-
signal!(base, NonConstPath);
1288+
signal!(base, ExpectedConstStruct);
12501289
}
12511290
}
12521291
_ => signal!(e, MiscCatchAll)

src/librustc_trans/trans/consts.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr};
2727
use middle::const_eval::EvalHint::ExprTypeChecked;
2828
use middle::const_eval::eval_const_expr_partial;
2929
use middle::def_id::DefId;
30+
use middle::def::DefStruct;
3031
use trans::{adt, closure, debuginfo, expr, inline, machine};
3132
use trans::base::{self, push_ctxt};
3233
use trans::common::{self, type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt};
3334
use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty};
3435
use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint};
36+
use trans::common::C_floating_f64;
3537
use trans::common::{type_is_fat_ptr, Field, C_vector, C_array, C_null, ExprId, MethodCallKey};
3638
use trans::declare;
3739
use trans::monomorphize;
@@ -107,6 +109,56 @@ pub fn const_lit(cx: &CrateContext, e: &hir::Expr, lit: &ast::Lit)
107109
}
108110
}
109111

112+
pub fn const_val<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, val: &ConstVal, ty: Ty<'tcx>) -> ValueRef {
113+
let _icx = push_ctxt("trans_const");
114+
debug!("const_val: {:?}", val);
115+
let llty = type_of::type_of(cx, ty);
116+
match *val {
117+
ConstVal::Float(v) => C_floating_f64(v, llty),
118+
ConstVal::Bool(v) => C_bool(cx, v),
119+
ConstVal::Int(v) => C_integral(llty, v as u64, true),
120+
ConstVal::Uint(v) => C_integral(llty, v, false),
121+
ConstVal::Str(ref v) => C_str_slice(cx, v.clone()),
122+
ConstVal::ByteStr(ref v) => {
123+
addr_of(cx, C_bytes(cx, v), 1, "byte_str")
124+
}
125+
/*
126+
ConstVal::Int(i) => match ty.sty {
127+
ty::TyInt(ty) => C_integral(Type::int_from_ty(cx, ty), i as u64, true),
128+
_ => unimplemented!(),
129+
},
130+
ConstVal::Uint(u) => match ty.sty {
131+
ty::TyUint(ty) => C_integral(Type::uint_from_ty(cx, ty), u, false),
132+
_ => unimplemented!(),
133+
},
134+
ConstVal::Float(f) => match ty.sty {
135+
ty::TyFloat(ty) => C_floating_f64(f, Type::float_from_ty(cx, ty)),
136+
_ => unimplemented!(),
137+
},
138+
ConstVal::Bool(b) => C_bool(cx, b),
139+
ConstVal::Str(s) => C_str_slice(cx, s),
140+
ConstVal::ByteStr(ref data) => {
141+
addr_of(cx, C_bytes(cx, &data[..]), 1, "byte_str")
142+
},*/
143+
ConstVal::Struct(id, ref field_values) => {
144+
let repr = adt::represent_type(cx, ty);
145+
let mut trans_fields = Vec::with_capacity(field_values.len());
146+
let VariantInfo { discr, fields } = VariantInfo::of_node(cx.tcx(), ty, id);
147+
for Field(f_name, f_ty) in fields {
148+
let value = field_values.get(&f_name).expect("trans knows struct fields that const doesn't");
149+
let value = const_val(cx, value, f_ty);
150+
trans_fields.push(value);
151+
}
152+
if ty.is_simd() {
153+
C_vector(&trans_fields[..])
154+
} else {
155+
adt::trans_const(cx, &*repr, discr, &trans_fields[..])
156+
}
157+
},
158+
_ => unimplemented!(),
159+
}
160+
}
161+
110162
pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef {
111163
unsafe {
112164
llvm::LLVMConstPointerCast(val, ty.to_ref())

0 commit comments

Comments
 (0)