diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 21ece8f381ebf..276eaa36a0151 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -395,6 +395,7 @@ pub enum ErrKind { InvalidOpForUintInt(hir::BinOp_), NegateOn(ConstVal), NotOn(ConstVal), + CallOn(ConstVal), NegateWithOverflow(i64), AddiWithOverflow(i64, i64), @@ -411,6 +412,7 @@ pub enum ErrKind { ShiftRightWithOverflow, MissingStructField, NonConstPath, + UnimplementedConstVal(&'static str), UnresolvedPath, ExpectedConstTuple, ExpectedConstStruct, @@ -435,6 +437,7 @@ impl ConstEvalErr { InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(), NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(), NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(), + CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(), NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(), AddiWithOverflow(..) => "attempted to add with overflow".into_cow(), @@ -451,6 +454,8 @@ impl ConstEvalErr { ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(), MissingStructField => "nonexistent struct field".into_cow(), NonConstPath => "non-constant path in constant expression".into_cow(), + UnimplementedConstVal(what) => + format!("unimplemented constant expression: {}", what).into_cow(), UnresolvedPath => "unresolved path in constant expression".into_cow(), ExpectedConstTuple => "expected constant tuple".into_cow(), ExpectedConstStruct => "expected constant struct".into_cow(), @@ -1023,8 +1028,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, (None, None) } }, - Some(def::DefFn(id, _)) => return Ok(Function(id)), - // FIXME: implement const methods? + Some(def::DefMethod(id)) | Some(def::DefFn(id, _)) => return Ok(Function(id)), _ => (None, None) }; let const_expr = match const_expr { @@ -1050,31 +1054,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, } else { UncheckedExprNoHint // we cannot reason about UncheckedExprHint here }; - let ( - decl, - block, - constness, - ) = match try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)) { - Function(did) => if did.is_local() { - match tcx.map.find(did.index.as_u32()) { - Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemFn( - ref decl, - hir::Unsafety::Normal, - constness, - abi::Abi::Rust, - _, // ducktype generics? types are funky in const_eval - ref block, - ) => (decl, block, constness), - _ => signal!(e, NonConstPath), - }, - _ => signal!(e, NonConstPath), - } - } else { - signal!(e, NonConstPath) - }, - _ => signal!(e, NonConstPath), - }; + let callee_val = try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)); + let (decl, block, constness) = try!(get_fn_def(tcx, e, callee_val)); match (ty_hint, constness) { (ExprTypeChecked, _) => { // no need to check for constness... either check_const @@ -1349,3 +1330,46 @@ pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, }; compare_const_vals(&a, &b) } + + +// returns Err if callee is not `Function` +// `e` is only used for error reporting/spans +fn get_fn_def<'a>(tcx: &'a ty::ctxt, + e: &hir::Expr, + callee: ConstVal) + -> Result<(&'a hir::FnDecl, &'a hir::Block, hir::Constness), ConstEvalErr> { + let did = match callee { + Function(did) => did, + callee => signal!(e, CallOn(callee)), + }; + debug!("fn call: {:?}", tcx.map.get_if_local(did)); + match tcx.map.get_if_local(did) { + None => signal!(e, UnimplementedConstVal("calling non-local const fn")), // non-local + Some(ast_map::NodeItem(it)) => match it.node { + hir::ItemFn( + ref decl, + hir::Unsafety::Normal, + constness, + abi::Abi::Rust, + _, // ducktype generics? types are funky in const_eval + ref block, + ) => Ok((&**decl, &**block, constness)), + _ => signal!(e, NonConstPath), + }, + Some(ast_map::NodeImplItem(it)) => match it.node { + hir::ImplItemKind::Method( + hir::MethodSig { + ref decl, + unsafety: hir::Unsafety::Normal, + constness, + abi: abi::Abi::Rust, + .. // ducktype generics? types are funky in const_eval + }, + ref block, + ) => Ok((decl, block, constness)), + _ => signal!(e, NonConstPath), + }, + Some(ast_map::NodeTraitItem(..)) => signal!(e, NonConstPath), + Some(_) => unimplemented!(), + } +} diff --git a/src/test/compile-fail/const-eval-span.rs b/src/test/compile-fail/const-eval-span.rs index 3e75afcda6d71..44ab798f4911b 100644 --- a/src/test/compile-fail/const-eval-span.rs +++ b/src/test/compile-fail/const-eval-span.rs @@ -14,7 +14,7 @@ struct S(i32); const CONSTANT: S = S(0); -//~^ ERROR: constant evaluation error: non-constant path in constant expression [E0080] +//~^ ERROR: constant evaluation error: call on struct [E0080] enum E { V = CONSTANT, diff --git a/src/test/compile-fail/const-fn-stability-calls-2.rs b/src/test/compile-fail/const-fn-stability-calls-2.rs index 59e0db7b35508..592a312d80048 100644 --- a/src/test/compile-fail/const-fn-stability-calls-2.rs +++ b/src/test/compile-fail/const-fn-stability-calls-2.rs @@ -17,5 +17,6 @@ extern crate const_fn_lib; use const_fn_lib::foo; fn main() { - let x: [usize; foo()] = []; //~ ERROR non-constant path in constant expr + let x: [usize; foo()] = []; + //~^ ERROR unimplemented constant expression: calling non-local const fn [E0250] } diff --git a/src/test/run-pass/const-fn-method.rs b/src/test/run-pass/const-fn-method.rs index 42c7a47c59db1..7d8d941439cf8 100644 --- a/src/test/run-pass/const-fn-method.rs +++ b/src/test/run-pass/const-fn-method.rs @@ -22,4 +22,5 @@ const FOO: Foo = Foo::new(); pub fn main() { assert_eq!(FOO.value, 22); + let _: [&'static str; Foo::new().value as usize] = ["hey"; 22]; } diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs index 9bd8eb55cc388..38c73febc3108 100644 --- a/src/test/run-pass/const-fn.rs +++ b/src/test/run-pass/const-fn.rs @@ -29,4 +29,5 @@ fn main() { assert_eq!(DIFF, 22); + let _: [&'static str; sub(100, 99) as usize] = ["hi"]; }