implement calling of const fn
-methods in true constants
This commit is contained in:
parent
e5aa92a0df
commit
8e64e22ef7
5 changed files with 56 additions and 29 deletions
|
@ -395,6 +395,7 @@ pub enum ErrKind {
|
||||||
InvalidOpForUintInt(hir::BinOp_),
|
InvalidOpForUintInt(hir::BinOp_),
|
||||||
NegateOn(ConstVal),
|
NegateOn(ConstVal),
|
||||||
NotOn(ConstVal),
|
NotOn(ConstVal),
|
||||||
|
CallOn(ConstVal),
|
||||||
|
|
||||||
NegateWithOverflow(i64),
|
NegateWithOverflow(i64),
|
||||||
AddiWithOverflow(i64, i64),
|
AddiWithOverflow(i64, i64),
|
||||||
|
@ -411,6 +412,7 @@ pub enum ErrKind {
|
||||||
ShiftRightWithOverflow,
|
ShiftRightWithOverflow,
|
||||||
MissingStructField,
|
MissingStructField,
|
||||||
NonConstPath,
|
NonConstPath,
|
||||||
|
UnimplementedConstVal(&'static str),
|
||||||
UnresolvedPath,
|
UnresolvedPath,
|
||||||
ExpectedConstTuple,
|
ExpectedConstTuple,
|
||||||
ExpectedConstStruct,
|
ExpectedConstStruct,
|
||||||
|
@ -435,6 +437,7 @@ impl ConstEvalErr {
|
||||||
InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(),
|
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(),
|
NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(),
|
||||||
NotOn(ref const_val) => format!("not 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(),
|
NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(),
|
||||||
AddiWithOverflow(..) => "attempted to add 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(),
|
ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(),
|
||||||
MissingStructField => "nonexistent struct field".into_cow(),
|
MissingStructField => "nonexistent struct field".into_cow(),
|
||||||
NonConstPath => "non-constant path in constant expression".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(),
|
UnresolvedPath => "unresolved path in constant expression".into_cow(),
|
||||||
ExpectedConstTuple => "expected constant tuple".into_cow(),
|
ExpectedConstTuple => "expected constant tuple".into_cow(),
|
||||||
ExpectedConstStruct => "expected constant struct".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)
|
(None, None)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(def::DefFn(id, _)) => return Ok(Function(id)),
|
Some(def::DefMethod(id)) | Some(def::DefFn(id, _)) => return Ok(Function(id)),
|
||||||
// FIXME: implement const methods?
|
|
||||||
_ => (None, None)
|
_ => (None, None)
|
||||||
};
|
};
|
||||||
let const_expr = match const_expr {
|
let const_expr = match const_expr {
|
||||||
|
@ -1050,31 +1054,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||||
} else {
|
} else {
|
||||||
UncheckedExprNoHint // we cannot reason about UncheckedExprHint here
|
UncheckedExprNoHint // we cannot reason about UncheckedExprHint here
|
||||||
};
|
};
|
||||||
let (
|
let callee_val = try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args));
|
||||||
decl,
|
let (decl, block, constness) = try!(get_fn_def(tcx, e, callee_val));
|
||||||
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),
|
|
||||||
};
|
|
||||||
match (ty_hint, constness) {
|
match (ty_hint, constness) {
|
||||||
(ExprTypeChecked, _) => {
|
(ExprTypeChecked, _) => {
|
||||||
// no need to check for constness... either check_const
|
// 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)
|
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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
struct S(i32);
|
struct S(i32);
|
||||||
|
|
||||||
const CONSTANT: S = S(0);
|
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 {
|
enum E {
|
||||||
V = CONSTANT,
|
V = CONSTANT,
|
||||||
|
|
|
@ -17,5 +17,6 @@ extern crate const_fn_lib;
|
||||||
use const_fn_lib::foo;
|
use const_fn_lib::foo;
|
||||||
|
|
||||||
fn main() {
|
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]
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,5 @@ const FOO: Foo = Foo::new();
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
assert_eq!(FOO.value, 22);
|
assert_eq!(FOO.value, 22);
|
||||||
|
let _: [&'static str; Foo::new().value as usize] = ["hey"; 22];
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,4 +29,5 @@ fn main() {
|
||||||
|
|
||||||
assert_eq!(DIFF, 22);
|
assert_eq!(DIFF, 22);
|
||||||
|
|
||||||
|
let _: [&'static str; sub(100, 99) as usize] = ["hi"];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue