Skip to content

Commit b4e30bd

Browse files
committed
allow constant evaluation of function calls
1 parent 9ed32bb commit b4e30bd

File tree

14 files changed

+155
-52
lines changed

14 files changed

+155
-52
lines changed

src/librustc/middle/check_const.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
473473
ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
474474
if !self.qualif.intersects(ConstQualif::NOT_CONST) {
475475
match const_eval::eval_const_expr_partial(
476-
self.tcx, ex, ExprTypeChecked) {
476+
self.tcx, ex, ExprTypeChecked, None) {
477477
Ok(_) => {}
478478
Err(msg) => {
479479
self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id,

src/librustc/middle/check_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
272272
fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
273273
front_util::walk_pat(pat, |p| {
274274
if let hir::PatLit(ref expr) = p.node {
275-
match eval_const_expr_partial(cx.tcx, &**expr, ExprTypeChecked) {
275+
match eval_const_expr_partial(cx.tcx, &**expr, ExprTypeChecked, None) {
276276
Ok(ConstVal::Float(f)) if f.is_nan() => {
277277
span_warn!(cx.tcx.sess, p.span, E0003,
278278
"unmatchable NaN in pattern, \

src/librustc/middle/const_eval.rs

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ use front::map::blocks::FnLikeNode;
1919
use metadata::csearch;
2020
use metadata::inline::InlinedItem;
2121
use middle::{astencode, def, infer, subst, traits};
22-
use middle::def_id::{DefId};
22+
use middle::def_id::DefId;
2323
use middle::pat_util::def_to_path;
2424
use middle::ty::{self, Ty};
2525
use middle::astconv_util::ast_ty_to_prim_ty;
2626
use util::num::ToPrimitive;
27+
use util::nodemap::NodeMap;
2728

28-
use syntax::ast;
29+
use syntax::{ast, abi};
2930
use rustc_front::hir::Expr;
3031
use rustc_front::hir;
3132
use rustc_front::visit::FnKind;
@@ -253,6 +254,7 @@ pub enum ConstVal {
253254
Bool(bool),
254255
Struct(ast::NodeId),
255256
Tuple(ast::NodeId),
257+
Function(DefId),
256258
}
257259

258260
/// Note that equality for `ConstVal` means that the it is the same
@@ -271,6 +273,7 @@ impl PartialEq for ConstVal {
271273
(&Bool(a), &Bool(b)) => a == b,
272274
(&Struct(a), &Struct(b)) => a == b,
273275
(&Tuple(a), &Tuple(b)) => a == b,
276+
(&Function(a), &Function(b)) => a == b,
274277
_ => false,
275278
}
276279
}
@@ -288,6 +291,7 @@ impl ConstVal {
288291
Bool(_) => "boolean",
289292
Struct(_) => "struct",
290293
Tuple(_) => "tuple",
294+
Function(_) => "function definition",
291295
}
292296
}
293297
}
@@ -350,12 +354,13 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<hir::Pat>
350354
}
351355

352356
pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> ConstVal {
353-
match eval_const_expr_partial(tcx, e, ExprTypeChecked) {
357+
match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) {
354358
Ok(r) => r,
355359
Err(s) => tcx.sess.span_fatal(s.span, &s.description())
356360
}
357361
}
358362

363+
pub type FnArgMap<'a> = Option<&'a NodeMap<ConstVal>>;
359364

360365
#[derive(Clone)]
361366
pub struct ConstEvalErr {
@@ -739,7 +744,8 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
739744
/// computing the length of an array. (See also the FIXME above EvalHint.)
740745
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
741746
e: &Expr,
742-
ty_hint: EvalHint<'tcx>) -> EvalResult {
747+
ty_hint: EvalHint<'tcx>,
748+
fn_args: FnArgMap) -> EvalResult {
743749
fn fromb(b: bool) -> ConstVal { Int(b as i64) }
744750

745751
// Try to compute the type of the expression based on the EvalHint.
@@ -776,7 +782,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
776782

777783
let result = match e.node {
778784
hir::ExprUnary(hir::UnNeg, ref inner) => {
779-
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) {
785+
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint, fn_args)) {
780786
Float(f) => Float(-f),
781787
Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
782788
Uint(i) => {
@@ -786,7 +792,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
786792
}
787793
}
788794
hir::ExprUnary(hir::UnNot, ref inner) => {
789-
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) {
795+
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint, fn_args)) {
790796
Int(i) => Int(!i),
791797
Uint(i) => const_uint_not(i, expr_uint_type),
792798
Bool(b) => Bool(!b),
@@ -804,8 +810,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
804810
}
805811
_ => ty_hint
806812
};
807-
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint)),
808-
try!(eval_const_expr_partial(tcx, &**b, b_ty))) {
813+
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args)),
814+
try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args))) {
809815
(Float(a), Float(b)) => {
810816
match op.node {
811817
hir::BiAdd => Float(a + b),
@@ -912,7 +918,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
912918
}
913919
};
914920

915-
let val = try!(eval_const_expr_partial(tcx, &**base, base_hint));
921+
let val = try!(eval_const_expr_partial(tcx, &**base, base_hint, fn_args));
916922
match cast_const(tcx, val, ety) {
917923
Ok(val) => val,
918924
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
@@ -990,6 +996,16 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
990996
Some(def::DefStruct(_)) => {
991997
return Ok(ConstVal::Struct(e.id))
992998
}
999+
Some(def::DefLocal(_, id)) => {
1000+
debug!("DefLocal({:?}): {:?}", id, fn_args);
1001+
if let Some(val) = fn_args.and_then(|args| args.get(&id)) {
1002+
return Ok(val.clone());
1003+
} else {
1004+
(None, None)
1005+
}
1006+
},
1007+
Some(def::DefFn(id, _)) => return Ok(Function(id)),
1008+
// FIXME: implement const methods?
9931009
_ => (None, None)
9941010
};
9951011
let const_expr = match const_expr {
@@ -1007,14 +1023,68 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
10071023
} else {
10081024
ty_hint
10091025
};
1010-
try!(eval_const_expr_partial(tcx, const_expr, item_hint))
1026+
try!(eval_const_expr_partial(tcx, const_expr, item_hint, fn_args))
10111027
}
1028+
hir::ExprCall(ref callee, ref args) => {
1029+
let sub_ty_hint = if let ExprTypeChecked = ty_hint {
1030+
ExprTypeChecked
1031+
} else {
1032+
UncheckedExprNoHint // we cannot reason about UncheckedExprHint here
1033+
};
1034+
let (
1035+
decl,
1036+
unsafety,
1037+
abi,
1038+
block,
1039+
) = match try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)) {
1040+
Function(did) => if did.is_local() {
1041+
match tcx.map.find(did.index.as_u32()) {
1042+
Some(ast_map::NodeItem(it)) => match it.node {
1043+
hir::ItemFn(
1044+
ref decl,
1045+
unsafety,
1046+
_, // no need to check for constness... either check_const
1047+
// already forbids this or we const eval over whatever
1048+
// we want
1049+
abi,
1050+
_, // ducktype generics? types are funky in const_eval
1051+
ref block,
1052+
) => (decl, unsafety, abi, block),
1053+
_ => signal!(e, NonConstPath),
1054+
},
1055+
_ => signal!(e, NonConstPath),
1056+
}
1057+
} else {
1058+
signal!(e, NonConstPath)
1059+
},
1060+
_ => signal!(e, NonConstPath),
1061+
};
1062+
assert_eq!(decl.inputs.len(), args.len());
1063+
assert_eq!(unsafety, hir::Unsafety::Normal);
1064+
assert_eq!(abi, abi::Abi::Rust);
1065+
1066+
let mut call_args = NodeMap();
1067+
for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) {
1068+
let arg_val = try!(eval_const_expr_partial(
1069+
tcx,
1070+
arg_expr,
1071+
sub_ty_hint,
1072+
fn_args
1073+
));
1074+
debug!("const call arg: {:?}", arg);
1075+
let old = call_args.insert(arg.pat.id, arg_val);
1076+
assert!(old.is_none());
1077+
}
1078+
let result = block.expr.as_ref().unwrap();
1079+
debug!("const call({:?})", call_args);
1080+
try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args)))
1081+
},
10121082
hir::ExprLit(ref lit) => {
10131083
lit_to_const(&**lit, ety)
10141084
}
10151085
hir::ExprBlock(ref block) => {
10161086
match block.expr {
1017-
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint)),
1087+
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint, fn_args)),
10181088
None => Int(0)
10191089
}
10201090
}
@@ -1026,11 +1096,11 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
10261096
} else {
10271097
UncheckedExprNoHint
10281098
};
1029-
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) {
1099+
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
10301100
if let Tuple(tup_id) = c {
10311101
if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
10321102
if index.node < fields.len() {
1033-
return eval_const_expr_partial(tcx, &fields[index.node], base_hint)
1103+
return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args)
10341104
} else {
10351105
signal!(e, TupleIndexOutOfBounds);
10361106
}
@@ -1051,14 +1121,14 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
10511121
} else {
10521122
UncheckedExprNoHint
10531123
};
1054-
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) {
1124+
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
10551125
if let Struct(struct_id) = c {
10561126
if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
10571127
// Check that the given field exists and evaluate it
10581128
// if the idents are compared run-pass/issue-19244 fails
10591129
if let Some(f) = fields.iter().find(|f| f.name.node
10601130
== field_name.node) {
1061-
return eval_const_expr_partial(tcx, &*f.expr, base_hint)
1131+
return eval_const_expr_partial(tcx, &*f.expr, base_hint, fn_args)
10621132
} else {
10631133
signal!(e, MissingStructField);
10641134
}
@@ -1237,14 +1307,14 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
12371307
pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
12381308
a: &Expr,
12391309
b: &Expr) -> Option<Ordering> {
1240-
let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked) {
1310+
let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) {
12411311
Ok(a) => a,
12421312
Err(e) => {
12431313
tcx.sess.span_err(a.span, &e.description());
12441314
return None;
12451315
}
12461316
};
1247-
let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked) {
1317+
let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) {
12481318
Ok(b) => b,
12491319
Err(e) => {
12501320
tcx.sess.span_err(b.span, &e.description());

src/librustc/middle/ty/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ impl<'tcx> ty::ctxt<'tcx> {
335335
/// Returns the repeat count for a repeating vector expression.
336336
pub fn eval_repeat_count(&self, count_expr: &hir::Expr) -> usize {
337337
let hint = UncheckedExprHint(self.types.usize);
338-
match const_eval::eval_const_expr_partial(self, count_expr, hint) {
338+
match const_eval::eval_const_expr_partial(self, count_expr, hint, None) {
339339
Ok(val) => {
340340
let found = match val {
341341
ConstVal::Uint(count) => return count as usize,

src/librustc_lint/types.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl LateLintPass for TypeLimits {
120120
if let ast::LitInt(shift, _) = lit.node { shift >= bits }
121121
else { false }
122122
} else {
123-
match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked) {
123+
match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) {
124124
Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
125125
Ok(ConstVal::Uint(shift)) => { shift >= bits },
126126
_ => { false }
@@ -674,4 +674,3 @@ impl LateLintPass for ImproperCTypes {
674674
}
675675
}
676676
}
677-

src/librustc_mir/tcx/pattern.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> {
166166
let opt_value =
167167
const_eval::eval_const_expr_partial(
168168
cx.tcx, const_expr,
169-
const_eval::EvalHint::ExprTypeChecked);
169+
const_eval::EvalHint::ExprTypeChecked,
170+
None);
170171
let literal = if let Ok(value) = opt_value {
171172
Literal::Value { value: value }
172173
} else {

src/librustc_trans/trans/consts.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
664664
let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
665665
adt::const_get_field(cx, &*brepr, bv, vinfo.discr, idx.node)
666666
},
667-
668667
hir::ExprIndex(ref base, ref index) => {
669668
let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst));
670-
let iv = match eval_const_expr_partial(cx.tcx(), &index, ExprTypeChecked) {
669+
let iv = match eval_const_expr_partial(cx.tcx(), &index, ExprTypeChecked, None) {
671670
Ok(ConstVal::Int(i)) => i as u64,
672671
Ok(ConstVal::Uint(u)) => u,
673672
_ => cx.sess().span_bug(index.span,

src/librustc_typeck/astconv.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
16741674
}
16751675
hir::TyFixedLengthVec(ref ty, ref e) => {
16761676
let hint = UncheckedExprHint(tcx.types.usize);
1677-
match const_eval::eval_const_expr_partial(tcx, &e, hint) {
1677+
match const_eval::eval_const_expr_partial(tcx, &e, hint, None) {
16781678
Ok(r) => {
16791679
match r {
16801680
ConstVal::Int(i) =>

src/librustc_typeck/collect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>,
11481148
debug!("disr expr, checking {}", pprust::expr_to_string(e));
11491149

11501150
let hint = UncheckedExprHint(repr_ty);
1151-
match const_eval::eval_const_expr_partial(tcx, e, hint) {
1151+
match const_eval::eval_const_expr_partial(tcx, e, hint, None) {
11521152
Ok(ConstVal::Int(val)) => Some(val as ty::Disr),
11531153
Ok(ConstVal::Uint(val)) => Some(val as ty::Disr),
11541154
Ok(_) => {

src/test/compile-fail/const-eval-span.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
struct S(i32);
1515

1616
const CONSTANT: S = S(0);
17-
//~^ ERROR: constant evaluation error: unsupported constant expr
17+
//~^ ERROR: constant evaluation error: non-constant path in constant expression [E0080]
1818

1919
enum E {
2020
V = CONSTANT,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2015 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+
// test that certain things are disallowed in const fn signatures
12+
13+
#![feature(const_fn)]
14+
15+
// no destructuring
16+
const fn i((a, b): (u32, u32)) -> u32 { a + b } //~ ERROR: E0022
17+
18+
fn main() {}

src/test/compile-fail/const-fn-stability-calls-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ extern crate const_fn_lib;
1717
use const_fn_lib::foo;
1818

1919
fn main() {
20-
let x: [usize; foo()] = []; //~ ERROR unsupported constant expr
20+
let x: [usize; foo()] = []; //~ ERROR non-constant path in constant expr
2121
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2012-2014 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+
#![feature(const_fn)]
12+
13+
const fn add(x: usize, y: usize) -> usize {
14+
x + y
15+
}
16+
17+
const ARR: [i32; add(1, 2)] = [5, 6, 7];
18+
19+
pub fn main() {}

0 commit comments

Comments
 (0)