Separate the IR evaluator, add an interesting stack-breaking case.
This commit is contained in:
@@ -120,15 +120,6 @@ proptest::proptest! {
|
||||
#[test]
|
||||
fn static_backend(program in Program::arbitrary()) {
|
||||
use crate::eval::PrimOpError;
|
||||
|
||||
let basic_result = program.eval().map(|(_,x)| x);
|
||||
|
||||
// windows `printf` is going to terminate lines with "\r\n", so we need to adjust
|
||||
// our test result here.
|
||||
#[cfg(target_family="windows")]
|
||||
let basic_result = basic_result.map(|x| x.replace('\n', "\r\n"));
|
||||
|
||||
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
||||
use pretty::DocAllocator;
|
||||
let allocator = pretty::Arena::new();
|
||||
let result = allocator.text("-------------")
|
||||
@@ -138,6 +129,28 @@ proptest::proptest! {
|
||||
result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout()))
|
||||
.expect("rendering works");
|
||||
|
||||
|
||||
eprintln!("BEFORE EVAL");
|
||||
let ir_evaluator = crate::ir::Evaluator::default();
|
||||
let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o);
|
||||
eprintln!("AFTER EVAL");
|
||||
|
||||
// windows `printf` is going to terminate lines with "\r\n", so we need to adjust
|
||||
// our test result here.
|
||||
#[cfg(target_family="windows")]
|
||||
let basic_result = basic_result.map(|x| x.replace('\n', "\r\n"));
|
||||
|
||||
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
||||
//use pretty::DocAllocator;
|
||||
//let allocator = pretty::Arena::new();
|
||||
//let result = allocator.text("-------------")
|
||||
// .append(allocator.line())
|
||||
// .append(program.pretty(&allocator))
|
||||
// .append(allocator.line());
|
||||
//result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout()))
|
||||
// .expect("rendering works");
|
||||
|
||||
eprintln!("BEFORE OM EVAL");
|
||||
let compiled_result = Backend::<ObjectModule>::eval(program);
|
||||
proptest::prop_assert_eq!(basic_result, compiled_result);
|
||||
}
|
||||
@@ -159,7 +172,8 @@ proptest::proptest! {
|
||||
// .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto))
|
||||
// .expect("rendering works");
|
||||
|
||||
let basic_result = program.eval().map(|(_,x)| x);
|
||||
let ir_evaluator = crate::ir::Evaluator::default();
|
||||
let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o);
|
||||
|
||||
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
||||
let compiled_result = Backend::<JITModule>::eval(program);
|
||||
|
||||
@@ -500,7 +500,7 @@ impl<M: Module> Backend<M> {
|
||||
.into_iter()
|
||||
.unzip();
|
||||
|
||||
match *function {
|
||||
match function {
|
||||
ValueOrRef::Value(_, _, _) => {
|
||||
panic!("Can't use a value for a function")
|
||||
}
|
||||
@@ -568,10 +568,7 @@ impl<M: Module> Backend<M> {
|
||||
|
||||
Primitive::Negate => {
|
||||
assert_eq!(1, arguments.len());
|
||||
Ok((
|
||||
builder.ins().ineg(arguments[0]),
|
||||
argument_types[0],
|
||||
))
|
||||
Ok((builder.ins().ineg(arguments[0]), argument_types[0]))
|
||||
}
|
||||
|
||||
Primitive::Print => {
|
||||
|
||||
@@ -102,7 +102,8 @@ fn main() {
|
||||
};
|
||||
|
||||
if cli.interpreter == Interpreter::IR {
|
||||
match ir.eval() {
|
||||
let evaluator = ngr::ir::Evaluator::default();
|
||||
match evaluator.eval(ir) {
|
||||
Err(e) => tracing::error!(error = %e, "Evaluation error"),
|
||||
Ok(v) => print_result(v),
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ impl PrimitiveType {
|
||||
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)]
|
||||
}
|
||||
PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 => {
|
||||
&[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)]
|
||||
&[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,3 +21,4 @@ mod strings;
|
||||
mod top_level;
|
||||
|
||||
pub use ast::*;
|
||||
pub use eval::Evaluator;
|
||||
|
||||
@@ -359,11 +359,7 @@ fn generate_random_expression(
|
||||
Expression::Call(
|
||||
Location::manufactured(),
|
||||
out_type,
|
||||
Box::new(ValueOrRef::Primitive(
|
||||
Location::manufactured(),
|
||||
primtype,
|
||||
primop,
|
||||
)),
|
||||
ValueOrRef::Primitive(Location::manufactured(), primtype, primop),
|
||||
args,
|
||||
)
|
||||
}
|
||||
@@ -393,11 +389,7 @@ fn generate_random_expression(
|
||||
Expression::Call(
|
||||
Location::manufactured(),
|
||||
Type::void(),
|
||||
Box::new(ValueOrRef::Primitive(
|
||||
Location::manufactured(),
|
||||
Type::void(),
|
||||
Primitive::Print,
|
||||
)),
|
||||
ValueOrRef::Primitive(Location::manufactured(), Type::void(), Primitive::Print),
|
||||
vec![ValueOrRef::Ref(
|
||||
Location::manufactured(),
|
||||
var_type.clone(),
|
||||
|
||||
@@ -114,7 +114,7 @@ pub enum Expression<Type> {
|
||||
),
|
||||
FieldRef(Location, Type, Type, ValueOrRef<Type>, ArcIntern<String>),
|
||||
Block(Location, Type, Vec<Expression<Type>>),
|
||||
Call(Location, Type, Box<ValueOrRef<Type>>, Vec<ValueOrRef<Type>>),
|
||||
Call(Location, Type, ValueOrRef<Type>, Vec<ValueOrRef<Type>>),
|
||||
Bind(Location, Variable, Type, Box<Expression<Type>>),
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ impl<Type: Clone + TypeWithVoid> Expression<Type> {
|
||||
Expression::FieldRef(_, t, _, _, _) => t.clone(),
|
||||
Expression::Block(_, t, _) => t.clone(),
|
||||
Expression::Call(_, t, _, _) => t.clone(),
|
||||
Expression::Bind(_, _, _, _) => Type::void(),
|
||||
Expression::Bind(_, _, t, _) => t.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
138
src/ir/eval.rs
138
src/ir/eval.rs
@@ -3,60 +3,74 @@ use crate::eval::{EvalError, Value};
|
||||
use crate::ir::{Expression, Program, TopLevel, Variable};
|
||||
use crate::util::scoped_map::ScopedMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
|
||||
type IRValue<T> = Value<Expression<T>>;
|
||||
type IREvalError<T> = EvalError<Expression<T>>;
|
||||
|
||||
impl<T: Clone + Into<Type>> Program<T> {
|
||||
pub struct Evaluator<T> {
|
||||
env: ScopedMap<Variable, IRValue<T>>,
|
||||
stdout: String,
|
||||
}
|
||||
|
||||
impl<T> Default for Evaluator<T> {
|
||||
fn default() -> Self {
|
||||
Evaluator {
|
||||
env: ScopedMap::new(),
|
||||
stdout: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Evaluator<T>
|
||||
where
|
||||
T: Clone + Into<Type>,
|
||||
Expression<T>: Display,
|
||||
{
|
||||
/// Evaluate the program, returning either an error or the result of the final
|
||||
/// statement and the complete contents of the console output.
|
||||
///
|
||||
/// The print outs will be newline separated, with one print out per line.
|
||||
pub fn eval(&self) -> Result<(IRValue<T>, String), IREvalError<T>> {
|
||||
let mut env: ScopedMap<Variable, IRValue<T>> = ScopedMap::new();
|
||||
let mut stdout = String::new();
|
||||
pub fn eval(mut self, program: Program<T>) -> Result<(IRValue<T>, String), IREvalError<T>> {
|
||||
let mut last_value = Value::Void;
|
||||
|
||||
for stmt in self.items.iter() {
|
||||
for stmt in program.items.into_iter() {
|
||||
match stmt {
|
||||
TopLevel::Function(name, args, _, body) => {
|
||||
let closure = Value::Closure(
|
||||
Some(name.clone()),
|
||||
env.clone(),
|
||||
self.env.clone(),
|
||||
args.iter().map(|(x, _)| x.clone()).collect(),
|
||||
body.clone(),
|
||||
);
|
||||
|
||||
env.insert(name.clone(), closure.clone());
|
||||
self.env.insert(name.clone(), closure.clone());
|
||||
|
||||
last_value = closure;
|
||||
}
|
||||
|
||||
TopLevel::Statement(expr) => {
|
||||
last_value = expr.eval(&mut env, &mut stdout)?;
|
||||
last_value = self.eval_expr(expr)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((last_value, stdout))
|
||||
Ok((last_value, self.stdout))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Expression<T>
|
||||
where
|
||||
T: Clone + Into<Type>,
|
||||
{
|
||||
fn eval(
|
||||
&self,
|
||||
env: &mut ScopedMap<Variable, IRValue<T>>,
|
||||
stdout: &mut String,
|
||||
) -> Result<IRValue<T>, IREvalError<T>> {
|
||||
match self {
|
||||
Expression::Atomic(x) => x.eval(env),
|
||||
/// Get the current output of the evaluated program.
|
||||
pub fn stdout(self) -> String {
|
||||
self.stdout
|
||||
}
|
||||
|
||||
fn eval_expr(&mut self, expr: Expression<T>) -> Result<IRValue<T>, IREvalError<T>> {
|
||||
println!("evaluating {}", expr);
|
||||
match expr {
|
||||
Expression::Atomic(x) => self.eval_atomic(x),
|
||||
|
||||
Expression::Cast(_, t, valref) => {
|
||||
let value = valref.eval(env)?;
|
||||
let ty = t.clone().into();
|
||||
let value = self.eval_atomic(valref)?;
|
||||
let ty = t.into();
|
||||
|
||||
match ty {
|
||||
Type::Primitive(pt) => Ok(pt.safe_cast(&value)?),
|
||||
@@ -68,15 +82,15 @@ where
|
||||
Expression::Construct(_, _, name, fields) => {
|
||||
let mut result_fields = HashMap::with_capacity(fields.len());
|
||||
|
||||
for (name, subexpr) in fields.iter() {
|
||||
result_fields.insert(name.clone(), subexpr.eval(env)?);
|
||||
for (name, subexpr) in fields.into_iter() {
|
||||
result_fields.insert(name.clone(), self.eval_atomic(subexpr)?);
|
||||
}
|
||||
|
||||
Ok(Value::Structure(Some(name.clone()), result_fields))
|
||||
}
|
||||
|
||||
Expression::FieldRef(loc, _, _, valref, field) => match valref.eval(env)? {
|
||||
Value::Structure(oname, mut fields) => match fields.remove(field) {
|
||||
Expression::FieldRef(loc, _, _, valref, field) => match self.eval_atomic(valref)? {
|
||||
Value::Structure(oname, mut fields) => match fields.remove(&field) {
|
||||
None => Err(EvalError::NoFieldForValue(
|
||||
loc.clone(),
|
||||
Value::Structure(oname, fields),
|
||||
@@ -91,21 +105,21 @@ where
|
||||
Expression::Block(_, _, stmts) => {
|
||||
let mut result = Value::Void;
|
||||
|
||||
for stmt in stmts.iter() {
|
||||
result = stmt.eval(env, stdout)?;
|
||||
for stmt in stmts.into_iter() {
|
||||
result = self.eval_expr(stmt)?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
Expression::Bind(_, name, _, value) => {
|
||||
let value = value.eval(env, stdout)?;
|
||||
env.insert(name.clone(), value.clone());
|
||||
let value = self.eval_expr(*value)?;
|
||||
self.env.insert(name.clone(), value.clone());
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
Expression::Call(loc, _, fun, args) => {
|
||||
let function = fun.eval(env)?;
|
||||
let function = self.eval_atomic(fun)?;
|
||||
|
||||
match function {
|
||||
Value::Closure(name, mut closure_env, arguments, body) => {
|
||||
@@ -120,20 +134,29 @@ where
|
||||
|
||||
closure_env.new_scope();
|
||||
for (name, value) in arguments.into_iter().zip(args) {
|
||||
let value = value.eval(env)?;
|
||||
let value = self.eval_atomic(value)?;
|
||||
closure_env.insert(name, value);
|
||||
}
|
||||
let result = body.eval(&mut closure_env, stdout)?;
|
||||
|
||||
let temp_ref = &mut closure_env;
|
||||
|
||||
std::mem::swap(&mut self.env, temp_ref);
|
||||
let result = self.eval_expr(body);
|
||||
std::mem::swap(&mut self.env, temp_ref);
|
||||
closure_env.release_scope();
|
||||
Ok(result)
|
||||
result
|
||||
}
|
||||
|
||||
Value::Primitive(name) if name == "print" => {
|
||||
if let [ValueOrRef::Ref(loc, ty, name)] = &args[..] {
|
||||
let value = ValueOrRef::Ref(loc.clone(), ty.clone(), name.clone()).eval(env)?;
|
||||
let value = self.eval_atomic(ValueOrRef::Ref(
|
||||
loc.clone(),
|
||||
ty.clone(),
|
||||
name.clone(),
|
||||
))?;
|
||||
let addendum = format!("{} = {}\n", name, value);
|
||||
|
||||
stdout.push_str(&addendum);
|
||||
self.stdout.push_str(&addendum);
|
||||
Ok(Value::Void)
|
||||
} else {
|
||||
panic!("Non-reference/non-singleton argument to 'print'");
|
||||
@@ -142,8 +165,8 @@ where
|
||||
|
||||
Value::Primitive(name) => {
|
||||
let values = args
|
||||
.iter()
|
||||
.map(|x| x.eval(env))
|
||||
.into_iter()
|
||||
.map(|x| self.eval_atomic(x))
|
||||
.collect::<Result<_, _>>()?;
|
||||
println!("primitive {}: args {:?}", name, values);
|
||||
Value::calculate(name.as_str(), values).map_err(Into::into)
|
||||
@@ -154,25 +177,24 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> ValueOrRef<T> {
|
||||
fn eval(&self, env: &ScopedMap<Variable, IRValue<T>>) -> Result<IRValue<T>, IREvalError<T>> {
|
||||
match self {
|
||||
fn eval_atomic(&self, value: ValueOrRef<T>) -> Result<IRValue<T>, IREvalError<T>> {
|
||||
match value {
|
||||
ValueOrRef::Value(_, _, v) => match v {
|
||||
super::Value::I8(_, v) => Ok(Value::I8(*v)),
|
||||
super::Value::I16(_, v) => Ok(Value::I16(*v)),
|
||||
super::Value::I32(_, v) => Ok(Value::I32(*v)),
|
||||
super::Value::I64(_, v) => Ok(Value::I64(*v)),
|
||||
super::Value::U8(_, v) => Ok(Value::U8(*v)),
|
||||
super::Value::U16(_, v) => Ok(Value::U16(*v)),
|
||||
super::Value::U32(_, v) => Ok(Value::U32(*v)),
|
||||
super::Value::U64(_, v) => Ok(Value::U64(*v)),
|
||||
super::Value::I8(_, v) => Ok(Value::I8(v)),
|
||||
super::Value::I16(_, v) => Ok(Value::I16(v)),
|
||||
super::Value::I32(_, v) => Ok(Value::I32(v)),
|
||||
super::Value::I64(_, v) => Ok(Value::I64(v)),
|
||||
super::Value::U8(_, v) => Ok(Value::U8(v)),
|
||||
super::Value::U16(_, v) => Ok(Value::U16(v)),
|
||||
super::Value::U32(_, v) => Ok(Value::U32(v)),
|
||||
super::Value::U64(_, v) => Ok(Value::U64(v)),
|
||||
super::Value::Void => Ok(Value::Void),
|
||||
},
|
||||
|
||||
ValueOrRef::Ref(loc, _, n) => env
|
||||
.get(n)
|
||||
ValueOrRef::Ref(loc, _, n) => self
|
||||
.env
|
||||
.get(&n)
|
||||
.cloned()
|
||||
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())),
|
||||
|
||||
@@ -185,8 +207,9 @@ impl<T: Clone> ValueOrRef<T> {
|
||||
fn two_plus_three() {
|
||||
let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works");
|
||||
let ir = input.type_infer().expect("test should be type-valid");
|
||||
let (_, output) = ir.eval().expect("runs successfully");
|
||||
assert_eq!("x = 5u64\n", &output);
|
||||
let evaluator = Evaluator::default();
|
||||
let (_, result) = evaluator.eval(ir).expect("runs successfully");
|
||||
assert_eq!("x = 5u64\n", &result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -194,6 +217,7 @@ fn lotsa_math() {
|
||||
let input =
|
||||
crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works");
|
||||
let ir = input.type_infer().expect("test should be type-valid");
|
||||
let (_, output) = ir.eval().expect("runs successfully");
|
||||
assert_eq!("x = 7u64\n", &output);
|
||||
let evaluator = Evaluator::default();
|
||||
let (_, result) = evaluator.eval(ir).expect("runs successfully");
|
||||
assert_eq!("x = 7u64\n", &result);
|
||||
}
|
||||
|
||||
@@ -46,11 +46,9 @@ pub use crate::syntax::ast::*;
|
||||
pub use crate::syntax::location::Location;
|
||||
pub use crate::syntax::parser::{ProgramParser, TopLevelParser};
|
||||
pub use crate::syntax::tokens::{LexerError, Token};
|
||||
#[cfg(test)]
|
||||
use ::pretty::Arena;
|
||||
use lalrpop_util::ParseError;
|
||||
#[cfg(test)]
|
||||
use proptest::{arbitrary::Arbitrary, prop_assert, prop_assert_eq};
|
||||
use proptest::{arbitrary::Arbitrary, prop_assert};
|
||||
use std::ops::Range;
|
||||
#[cfg(test)]
|
||||
use std::str::FromStr;
|
||||
@@ -324,27 +322,6 @@ fn order_of_operations() {
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn random_render_parses_equal(program: Program) {
|
||||
let mut file_database = SimpleFiles::new();
|
||||
let writer = ::pretty::termcolor::StandardStream::stderr(::pretty::termcolor::ColorChoice::Auto);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
let allocator = Arena::<()>::new();
|
||||
|
||||
let mut out_vector = vec![];
|
||||
prop_assert!(program.pretty(&allocator).render(80, &mut out_vector).is_ok());
|
||||
let string = std::str::from_utf8(&out_vector).expect("emitted valid string");
|
||||
let file_handle = file_database.add("test", string);
|
||||
let file_db_info = file_database.get(file_handle).expect("find thing just inserted");
|
||||
let parsed = Program::parse(file_handle, file_db_info.source());
|
||||
|
||||
if let Err(e) = &parsed {
|
||||
eprintln!("failed to parse:\n{}", string);
|
||||
codespan_reporting::term::emit(&mut writer.lock(), &config, &file_database, &e.into()).unwrap();
|
||||
}
|
||||
prop_assert_eq!(program, parsed.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_syntaxes_validate(program: Program) {
|
||||
let (errors, _) = program.validate();
|
||||
@@ -354,6 +331,8 @@ proptest::proptest! {
|
||||
#[test]
|
||||
fn generated_run_or_overflow(program in Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
||||
use crate::eval::{EvalError, PrimOpError};
|
||||
println!("-----------\nprogram:\n{}\n", program);
|
||||
println!("-----------\nresult:\n{:?}\n", program.eval());
|
||||
prop_assert!(matches!(program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ impl ConstantType {
|
||||
match self {
|
||||
ConstantType::Void => &[],
|
||||
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => {
|
||||
&[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)]
|
||||
&[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)]
|
||||
}
|
||||
ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => {
|
||||
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)]
|
||||
|
||||
@@ -146,7 +146,8 @@ impl Expression {
|
||||
|
||||
Value::Primitive(name) if name == "print" => {
|
||||
if let [Expression::Reference(_, name)] = &args[..] {
|
||||
let value = Expression::Reference(loc.clone(), name.clone()).eval(stdout, env)?;
|
||||
let value = Expression::Reference(loc.clone(), name.clone())
|
||||
.eval(stdout, env)?;
|
||||
let value = match value {
|
||||
Value::Number(x) => Value::U64(x),
|
||||
x => x,
|
||||
@@ -156,7 +157,10 @@ impl Expression {
|
||||
stdout.push_str(&addendum);
|
||||
Ok(Value::Void)
|
||||
} else {
|
||||
panic!("Non-reference/non-singleton argument to 'print': {:?}", args);
|
||||
panic!(
|
||||
"Non-reference/non-singleton argument to 'print': {:?}",
|
||||
args
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ proptest::proptest! {
|
||||
fn translation_maintains_semantics(input in syntax::Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
||||
let syntax_result = input.eval().map(|(x,o)| (x.strip(), o));
|
||||
let ir = input.type_infer().expect("arbitrary should generate type-safe programs");
|
||||
let ir_result = ir.eval().map(|(x,o)| (x.strip(), o));
|
||||
let ir_evaluator = crate::ir::Evaluator::default();
|
||||
let ir_result = ir_evaluator.eval(ir).map(|(x, o)| (x.strip(), o));
|
||||
match (syntax_result, ir_result) {
|
||||
(Err(e1), Err(e2)) => proptest::prop_assert_eq!(e1, e2),
|
||||
(Ok((v1, o1)), Ok((v2, o2))) => {
|
||||
|
||||
@@ -360,10 +360,8 @@ fn convert_expression(
|
||||
let arg_type = ir::TypeOrVar::new_located(loc.clone());
|
||||
constraint_db.push(Constraint::NumericType(loc.clone(), arg_type.clone()));
|
||||
constraint_db.push(Constraint::IsSigned(loc.clone(), arg_type.clone()));
|
||||
let funtype = ir::TypeOrVar::Function(
|
||||
vec![arg_type.clone()],
|
||||
Box::new(arg_type),
|
||||
);
|
||||
let funtype =
|
||||
ir::TypeOrVar::Function(vec![arg_type.clone()], Box::new(arg_type));
|
||||
let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop);
|
||||
(ir::Expression::Atomic(result_value), funtype)
|
||||
}
|
||||
@@ -409,8 +407,7 @@ fn convert_expression(
|
||||
})
|
||||
.collect();
|
||||
|
||||
let last_call =
|
||||
ir::Expression::Call(loc.clone(), return_type.clone(), Box::new(fun), new_args);
|
||||
let last_call = ir::Expression::Call(loc.clone(), return_type.clone(), fun, new_args);
|
||||
|
||||
(finalize_expressions(prereqs, last_call), return_type)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ fn finalize_expression(
|
||||
Expression::Call(loc, ty, fun, args) => Expression::Call(
|
||||
loc,
|
||||
finalize_type(ty, resolutions),
|
||||
Box::new(finalize_val_or_ref(*fun, resolutions)),
|
||||
finalize_val_or_ref(fun, resolutions),
|
||||
args.into_iter()
|
||||
.map(|x| finalize_val_or_ref(x, resolutions))
|
||||
.collect(),
|
||||
|
||||
Reference in New Issue
Block a user