ran into another type inference problem
This commit is contained in:
5
examples/basic/function0001.ngr
Normal file
5
examples/basic/function0001.ngr
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
x = 1;
|
||||||
|
function add_x(y) x + y
|
||||||
|
a = 3;
|
||||||
|
result = add_x(a);
|
||||||
|
print result;
|
||||||
@@ -53,10 +53,9 @@ pub enum BackendError {
|
|||||||
impl From<BackendError> for Diagnostic<usize> {
|
impl From<BackendError> for Diagnostic<usize> {
|
||||||
fn from(value: BackendError) -> Self {
|
fn from(value: BackendError) -> Self {
|
||||||
match value {
|
match value {
|
||||||
BackendError::Cranelift(me) => {
|
BackendError::Cranelift(me) => Diagnostic::error()
|
||||||
Diagnostic::error().with_message(format!("Internal cranelift error: {}", me))
|
.with_message(format!("Internal cranelift error: {}", me))
|
||||||
.with_notes(vec![format!("{:?}", me)])
|
.with_notes(vec![format!("{:?}", me)]),
|
||||||
}
|
|
||||||
BackendError::BuiltinError(me) => {
|
BackendError::BuiltinError(me) => {
|
||||||
Diagnostic::error().with_message(format!("Internal runtime function error: {}", me))
|
Diagnostic::error().with_message(format!("Internal runtime function error: {}", me))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use crate::eval::PrimitiveType;
|
|||||||
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable};
|
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable};
|
||||||
use crate::syntax::{ConstantType, Location};
|
use crate::syntax::{ConstantType, Location};
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName
|
self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature,
|
||||||
|
UserFuncName,
|
||||||
};
|
};
|
||||||
use cranelift_codegen::isa::CallConv;
|
use cranelift_codegen::isa::CallConv;
|
||||||
use cranelift_codegen::Context;
|
use cranelift_codegen::Context;
|
||||||
@@ -29,7 +30,9 @@ impl ReferenceBuilder {
|
|||||||
ReferenceBuilder::Global(ty, gv) => {
|
ReferenceBuilder::Global(ty, gv) => {
|
||||||
let cranelift_type = ir::Type::from(*ty);
|
let cranelift_type = ir::Type::from(*ty);
|
||||||
let ptr_value = builder.ins().symbol_value(types::I64, *gv);
|
let ptr_value = builder.ins().symbol_value(types::I64, *gv);
|
||||||
let value = builder.ins().load(cranelift_type, MemFlags::new(), ptr_value, 0);
|
let value = builder
|
||||||
|
.ins()
|
||||||
|
.load(cranelift_type, MemFlags::new(), ptr_value, 0);
|
||||||
(value, *ty)
|
(value, *ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +438,8 @@ impl<M: Module> Backend<M> {
|
|||||||
// Look up the value for the variable. Because this might be a
|
// Look up the value for the variable. Because this might be a
|
||||||
// global variable (and that requires special logic), we just turn
|
// global variable (and that requires special logic), we just turn
|
||||||
// this into an `Expression` and re-use the logic in that implementation.
|
// this into an `Expression` and re-use the logic in that implementation.
|
||||||
let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone());
|
let fake_ref =
|
||||||
|
ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone());
|
||||||
let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?;
|
let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?;
|
||||||
|
|
||||||
let vtype_repr = builder.ins().iconst(types::I64, vtype as i64);
|
let vtype_repr = builder.ins().iconst(types::I64, vtype as i64);
|
||||||
@@ -473,6 +477,39 @@ impl<M: Module> Backend<M> {
|
|||||||
variables.insert(name, ReferenceBuilder::Local(value_type, variable));
|
variables.insert(name, ReferenceBuilder::Local(value_type, variable));
|
||||||
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
|
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression::Call(_, _, function, args) => {
|
||||||
|
let (arguments, _argument_types): (Vec<_>, Vec<_>) = args
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| self.compile_value_or_ref(x, variables, builder))
|
||||||
|
.collect::<Result<Vec<(_,_)>,BackendError>>()?
|
||||||
|
.into_iter()
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
match *function {
|
||||||
|
ValueOrRef::Value(_, _, _) => {
|
||||||
|
panic!("Can't use a value for a function")
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueOrRef::Ref(_, result_type, name) => match self.defined_functions.get(&name) {
|
||||||
|
None => panic!("Couldn't find function {} to call", name),
|
||||||
|
Some(function) => {
|
||||||
|
let func_ref = self.module.declare_func_in_func(*function, builder.func);
|
||||||
|
let call = builder.ins().call(func_ref, &arguments);
|
||||||
|
let results = builder.inst_results(call);
|
||||||
|
|
||||||
|
match results {
|
||||||
|
[] => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)),
|
||||||
|
[result] => match result_type {
|
||||||
|
Type::Primitive(ct) => Ok((*result, ct.into())),
|
||||||
|
Type::Function(_, _) => panic!("return value is a function?"),
|
||||||
|
}
|
||||||
|
_ => panic!("don't support multi-value returns yet"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
src/eval.rs
20
src/eval.rs
@@ -39,6 +39,7 @@ mod primtype;
|
|||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
use cranelift_module::ModuleError;
|
use cranelift_module::ModuleError;
|
||||||
|
use internment::ArcIntern;
|
||||||
pub use primop::PrimOpError;
|
pub use primop::PrimOpError;
|
||||||
pub use primtype::PrimitiveType;
|
pub use primtype::PrimitiveType;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
@@ -76,6 +77,15 @@ pub enum EvalError<IR> {
|
|||||||
UnknownPrimType(#[from] UnknownPrimType),
|
UnknownPrimType(#[from] UnknownPrimType),
|
||||||
#[error("Variable lookup failed for {1} at {0:?}")]
|
#[error("Variable lookup failed for {1} at {0:?}")]
|
||||||
LookupFailed(crate::syntax::Location, String),
|
LookupFailed(crate::syntax::Location, String),
|
||||||
|
#[error("Attempted to call something that wasn't a function at {0:?} (it was a {1})")]
|
||||||
|
NotAFunction(crate::syntax::Location, Value<IR>),
|
||||||
|
#[error("Wrong argument call for function ({1:?}) at {0:?}; expected {2}, saw {3}")]
|
||||||
|
WrongArgCount(
|
||||||
|
crate::syntax::Location,
|
||||||
|
Option<ArcIntern<String>>,
|
||||||
|
usize,
|
||||||
|
usize,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<IR1: Clone, IR2: Clone> PartialEq<EvalError<IR1>> for EvalError<IR2> {
|
impl<IR1: Clone, IR2: Clone> PartialEq<EvalError<IR1>> for EvalError<IR2> {
|
||||||
@@ -129,6 +139,16 @@ impl<IR1: Clone, IR2: Clone> PartialEq<EvalError<IR1>> for EvalError<IR2> {
|
|||||||
EvalError::UnknownPrimType(b) => a == b,
|
EvalError::UnknownPrimType(b) => a == b,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
EvalError::NotAFunction(a, b) => match other {
|
||||||
|
EvalError::NotAFunction(x, y) => a == x && b == y,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
|
||||||
|
EvalError::WrongArgCount(a, b, c, d) => match other {
|
||||||
|
EvalError::WrongArgCount(w, x, y, z) => a == w && b == x && c == y && d == z,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::eval::PrimitiveType;
|
use crate::eval::PrimitiveType;
|
||||||
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, Variable};
|
use crate::ir::{
|
||||||
|
Expression, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, Variable,
|
||||||
|
};
|
||||||
use crate::syntax::Location;
|
use crate::syntax::Location;
|
||||||
use crate::util::scoped_map::ScopedMap;
|
use crate::util::scoped_map::ScopedMap;
|
||||||
use proptest::strategy::{NewTree, Strategy, ValueTree};
|
use proptest::strategy::{NewTree, Strategy, ValueTree};
|
||||||
@@ -300,7 +302,8 @@ fn generate_random_expression(
|
|||||||
if !next_type.is_void() {
|
if !next_type.is_void() {
|
||||||
let name = generate_random_name(rng);
|
let name = generate_random_name(rng);
|
||||||
env.insert(name.clone(), next_type.clone());
|
env.insert(name.clone(), next_type.clone());
|
||||||
next = Expression::Bind(Location::manufactured(), name, next_type, Box::new(next));
|
next =
|
||||||
|
Expression::Bind(Location::manufactured(), name, next_type, Box::new(next));
|
||||||
}
|
}
|
||||||
stmts.push(next);
|
stmts.push(next);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ pub enum Expression<Type> {
|
|||||||
Primitive(Location, Type, Primitive, Vec<ValueOrRef<Type>>),
|
Primitive(Location, Type, Primitive, Vec<ValueOrRef<Type>>),
|
||||||
Block(Location, Type, Vec<Expression<Type>>),
|
Block(Location, Type, Vec<Expression<Type>>),
|
||||||
Print(Location, Variable),
|
Print(Location, Variable),
|
||||||
|
Call(Location, Type, Box<ValueOrRef<Type>>, Vec<ValueOrRef<Type>>),
|
||||||
Bind(Location, Variable, Type, Box<Expression<Type>>),
|
Bind(Location, Variable, Type, Box<Expression<Type>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +174,7 @@ impl<Type: Clone + TypeWithVoid> Expression<Type> {
|
|||||||
Expression::Primitive(_, t, _, _) => t.clone(),
|
Expression::Primitive(_, t, _, _) => t.clone(),
|
||||||
Expression::Block(_, t, _) => t.clone(),
|
Expression::Block(_, t, _) => t.clone(),
|
||||||
Expression::Print(_, _) => Type::void(),
|
Expression::Print(_, _) => Type::void(),
|
||||||
|
Expression::Call(_, t, _, _) => t.clone(),
|
||||||
Expression::Bind(_, _, _, _) => Type::void(),
|
Expression::Bind(_, _, _, _) => Type::void(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,6 +188,7 @@ impl<Type: Clone + TypeWithVoid> Expression<Type> {
|
|||||||
Expression::Primitive(l, _, _, _) => l,
|
Expression::Primitive(l, _, _, _) => l,
|
||||||
Expression::Block(l, _, _) => l,
|
Expression::Block(l, _, _) => l,
|
||||||
Expression::Print(l, _) => l,
|
Expression::Print(l, _) => l,
|
||||||
|
Expression::Call(l, _, _, _) => l,
|
||||||
Expression::Bind(l, _, _, _) => l,
|
Expression::Bind(l, _, _, _) => l,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,6 +224,12 @@ where
|
|||||||
Expression::Primitive(_, _, op, exprs) => {
|
Expression::Primitive(_, _, op, exprs) => {
|
||||||
allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len()))
|
allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len()))
|
||||||
}
|
}
|
||||||
|
Expression::Call(_, _, fun, args) => {
|
||||||
|
let args = args.iter().map(|x| x.pretty(allocator));
|
||||||
|
let comma_sepped_args =
|
||||||
|
allocator.intersperse(args, crate::syntax::pretty::CommaSep {});
|
||||||
|
fun.pretty(allocator).append(comma_sepped_args.parens())
|
||||||
|
}
|
||||||
Expression::Block(_, _, exprs) => match exprs.split_last() {
|
Expression::Block(_, _, exprs) => match exprs.split_last() {
|
||||||
None => allocator.text("()"),
|
None => allocator.text("()"),
|
||||||
Some((last, &[])) => last.pretty(allocator),
|
Some((last, &[])) => last.pretty(allocator),
|
||||||
@@ -660,13 +669,20 @@ impl TryFrom<TypeOrVar> for Type {
|
|||||||
fn try_from(value: TypeOrVar) -> Result<Self, Self::Error> {
|
fn try_from(value: TypeOrVar) -> Result<Self, Self::Error> {
|
||||||
match value {
|
match value {
|
||||||
TypeOrVar::Function(args, ret) => {
|
TypeOrVar::Function(args, ret) => {
|
||||||
let args = args
|
let converted_args = args
|
||||||
.into_iter()
|
.iter()
|
||||||
|
.cloned()
|
||||||
.map(Type::try_from)
|
.map(Type::try_from)
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>();
|
||||||
let ret = Type::try_from(*ret)?;
|
let converted_ret = Type::try_from((*ret).clone());
|
||||||
|
|
||||||
Ok(Type::Function(args, Box::new(ret)))
|
if let Ok(args) = converted_args {
|
||||||
|
if let Ok(ret) = converted_ret {
|
||||||
|
return Ok(Type::Function(args, Box::new(ret)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(TypeOrVar::Function(args, ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)),
|
TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)),
|
||||||
|
|||||||
@@ -103,6 +103,33 @@ where
|
|||||||
env.insert(name.clone(), value);
|
env.insert(name.clone(), value);
|
||||||
Ok(Value::Void)
|
Ok(Value::Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression::Call(loc, _, fun, args) => {
|
||||||
|
let function = fun.eval(env)?;
|
||||||
|
|
||||||
|
match function {
|
||||||
|
Value::Closure(name, mut env, arguments, body) => {
|
||||||
|
if args.len() != arguments.len() {
|
||||||
|
return Err(EvalError::WrongArgCount(
|
||||||
|
loc.clone(),
|
||||||
|
name,
|
||||||
|
arguments.len(),
|
||||||
|
args.len(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
env.new_scope();
|
||||||
|
for (name, value) in arguments.into_iter().zip(args.into_iter()) {
|
||||||
|
let value = value.eval(&mut env)?;
|
||||||
|
env.insert(name, value);
|
||||||
|
}
|
||||||
|
let result = body.eval(&mut env, stdout)?;
|
||||||
|
env.release_scope();
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
_ => Err(EvalError::NotAFunction(loc.clone(), function)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ impl<T: Clone> Expression<T> {
|
|||||||
let mut tlvs = expr.get_top_level_variables();
|
let mut tlvs = expr.get_top_level_variables();
|
||||||
tlvs.insert(name.clone(), ty.clone());
|
tlvs.insert(name.clone(), ty.clone());
|
||||||
tlvs
|
tlvs
|
||||||
},
|
}
|
||||||
_ => HashMap::new(),
|
_ => HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ lalrpop_mod!(
|
|||||||
parser,
|
parser,
|
||||||
"/syntax/parser.rs"
|
"/syntax/parser.rs"
|
||||||
);
|
);
|
||||||
mod pretty;
|
pub mod pretty;
|
||||||
mod validate;
|
mod validate;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ pub enum Expression {
|
|||||||
Reference(Location, String),
|
Reference(Location, String),
|
||||||
Cast(Location, String, Box<Expression>),
|
Cast(Location, String, Box<Expression>),
|
||||||
Primitive(Location, String, Vec<Expression>),
|
Primitive(Location, String, Vec<Expression>),
|
||||||
|
Call(Location, Box<Expression>, Vec<Expression>),
|
||||||
Block(Location, Vec<Statement>),
|
Block(Location, Vec<Statement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +147,10 @@ impl PartialEq for Expression {
|
|||||||
Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2,
|
Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
Expression::Call(_, f1, a1) => match other {
|
||||||
|
Expression::Call(_, f2, a2) => f1 == f2 && a1 == a2,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
Expression::Block(_, stmts1) => match other {
|
Expression::Block(_, stmts1) => match other {
|
||||||
Expression::Block(_, stmts2) => stmts1 == stmts2,
|
Expression::Block(_, stmts2) => stmts1 == stmts2,
|
||||||
_ => false,
|
_ => false,
|
||||||
@@ -162,6 +167,7 @@ impl Expression {
|
|||||||
Expression::Reference(loc, _) => loc,
|
Expression::Reference(loc, _) => loc,
|
||||||
Expression::Cast(loc, _, _) => loc,
|
Expression::Cast(loc, _, _) => loc,
|
||||||
Expression::Primitive(loc, _, _) => loc,
|
Expression::Primitive(loc, _, _) => loc,
|
||||||
|
Expression::Call(loc, _, _) => loc,
|
||||||
Expression::Block(loc, _) => loc,
|
Expression::Block(loc, _) => loc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,33 @@ impl Expression {
|
|||||||
Ok(Value::calculate(op, arg_values)?)
|
Ok(Value::calculate(op, arg_values)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression::Call(loc, fun, args) => {
|
||||||
|
let function = fun.eval(stdout, env)?;
|
||||||
|
|
||||||
|
match function {
|
||||||
|
Value::Closure(name, mut closure_env, arguments, body) => {
|
||||||
|
if args.len() != arguments.len() {
|
||||||
|
return Err(EvalError::WrongArgCount(
|
||||||
|
loc.clone(),
|
||||||
|
name,
|
||||||
|
arguments.len(),
|
||||||
|
args.len(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
closure_env.new_scope();
|
||||||
|
for (name, value) in arguments.into_iter().zip(args.iter()) {
|
||||||
|
let value = value.eval(stdout, env)?;
|
||||||
|
closure_env.insert(name, value);
|
||||||
|
}
|
||||||
|
let result = body.eval(stdout, &mut closure_env)?;
|
||||||
|
closure_env.release_scope();
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
_ => Err(EvalError::NotAFunction(loc.clone(), function)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Expression::Block(_, stmts) => {
|
Expression::Block(_, stmts) => {
|
||||||
let mut result = Value::Void;
|
let mut result = Value::Void;
|
||||||
|
|
||||||
|
|||||||
@@ -205,9 +205,24 @@ UnaryExpression: Expression = {
|
|||||||
Expression::Primitive(Location::new(file_idx, l..le), "-".to_string(), vec![e]),
|
Expression::Primitive(Location::new(file_idx, l..le), "-".to_string(), vec![e]),
|
||||||
<l: @L> "<" <v:"<var>"> ">" <e:UnaryExpression> <le: @L> =>
|
<l: @L> "<" <v:"<var>"> ">" <e:UnaryExpression> <le: @L> =>
|
||||||
Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)),
|
Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)),
|
||||||
|
CallExpression,
|
||||||
|
}
|
||||||
|
|
||||||
|
CallExpression: Expression = {
|
||||||
|
<s: @L> <f:CallExpression> "(" <args: CallArguments> ")" <e: @L> =>
|
||||||
|
Expression::Call(Location::new(file_idx, s..e), Box::new(f), args),
|
||||||
AtomicExpression,
|
AtomicExpression,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallArguments: Vec<Expression> = {
|
||||||
|
=> vec![],
|
||||||
|
<e:Expression> => vec![e],
|
||||||
|
<mut args:CallArguments> "," <e:Expression> => {
|
||||||
|
args.push(e);
|
||||||
|
args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// finally, we describe our lowest-level expressions as "atomic", because
|
// finally, we describe our lowest-level expressions as "atomic", because
|
||||||
// they cannot be further divided into parts
|
// they cannot be further divided into parts
|
||||||
AtomicExpression: Expression = {
|
AtomicExpression: Expression = {
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ where
|
|||||||
let comma_sepped_args = allocator.intersperse(args, CommaSep {});
|
let comma_sepped_args = allocator.intersperse(args, CommaSep {});
|
||||||
call.append(comma_sepped_args.parens())
|
call.append(comma_sepped_args.parens())
|
||||||
}
|
}
|
||||||
|
Expression::Call(_, fun, args) => {
|
||||||
|
let args = args.iter().map(|x| x.pretty(allocator));
|
||||||
|
let comma_sepped_args = allocator.intersperse(args, CommaSep {});
|
||||||
|
fun.pretty(allocator).append(comma_sepped_args.parens())
|
||||||
|
}
|
||||||
Expression::Block(_, stmts) => match stmts.split_last() {
|
Expression::Block(_, stmts) => match stmts.split_last() {
|
||||||
None => allocator.text("()"),
|
None => allocator.text("()"),
|
||||||
Some((last, &[])) => last.pretty(allocator),
|
Some((last, &[])) => last.pretty(allocator),
|
||||||
@@ -167,7 +172,7 @@ fn type_suffix(x: &Option<ConstantType>) -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct CommaSep {}
|
pub struct CommaSep {}
|
||||||
|
|
||||||
impl<'a, D, A> Pretty<'a, D, A> for CommaSep
|
impl<'a, D, A> Pretty<'a, D, A> for CommaSep
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -212,6 +212,17 @@ impl Expression {
|
|||||||
|
|
||||||
(errors, warnings)
|
(errors, warnings)
|
||||||
}
|
}
|
||||||
|
Expression::Call(_, func, args) => {
|
||||||
|
let (mut errors, mut warnings) = func.validate(variable_map);
|
||||||
|
|
||||||
|
for arg in args.iter() {
|
||||||
|
let (mut e, mut w) = arg.validate(variable_map);
|
||||||
|
errors.append(&mut e);
|
||||||
|
warnings.append(&mut w);
|
||||||
|
}
|
||||||
|
|
||||||
|
(errors, warnings)
|
||||||
|
}
|
||||||
Expression::Block(_, stmts) => {
|
Expression::Block(_, stmts) => {
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
let mut warnings = vec![];
|
let mut warnings = vec![];
|
||||||
|
|||||||
@@ -286,9 +286,7 @@ fn convert_expression(
|
|||||||
let (aexp, atype) = convert_expression(arg, constraint_db, renames, bindings);
|
let (aexp, atype) = convert_expression(arg, constraint_db, renames, bindings);
|
||||||
let (aprereqs, asimple) = simplify_expr(aexp);
|
let (aprereqs, asimple) = simplify_expr(aexp);
|
||||||
|
|
||||||
if let Some(prereq) = aprereqs {
|
merge_prereq(&mut prereqs, aprereqs);
|
||||||
prereqs.push(prereq);
|
|
||||||
}
|
|
||||||
nargs.push(asimple);
|
nargs.push(asimple);
|
||||||
atypes.push(atype);
|
atypes.push(atype);
|
||||||
}
|
}
|
||||||
@@ -313,6 +311,59 @@ fn convert_expression(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syntax::Expression::Call(loc, fun, args) => {
|
||||||
|
let return_type = ir::TypeOrVar::new();
|
||||||
|
let arg_types = args
|
||||||
|
.iter()
|
||||||
|
.map(|_| ir::TypeOrVar::new())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let (new_fun, new_fun_type) =
|
||||||
|
convert_expression(*fun, constraint_db, renames, bindings);
|
||||||
|
let target_fun_type =
|
||||||
|
ir::TypeOrVar::Function(arg_types.clone(), Box::new(return_type.clone()));
|
||||||
|
constraint_db.push(Constraint::Equivalent(
|
||||||
|
loc.clone(),
|
||||||
|
new_fun_type,
|
||||||
|
target_fun_type,
|
||||||
|
));
|
||||||
|
let mut prereqs = vec![];
|
||||||
|
|
||||||
|
let (fun_prereqs, fun) = simplify_expr(new_fun);
|
||||||
|
merge_prereq(&mut prereqs, fun_prereqs);
|
||||||
|
|
||||||
|
let new_args = args
|
||||||
|
.into_iter()
|
||||||
|
.zip(arg_types.into_iter())
|
||||||
|
.map(|(arg, target_type)| {
|
||||||
|
let (new_arg, inferred_type) =
|
||||||
|
convert_expression(arg, constraint_db, renames, bindings);
|
||||||
|
let location = new_arg.location().clone();
|
||||||
|
let (arg_prereq, new_valref) = simplify_expr(new_arg);
|
||||||
|
merge_prereq(&mut prereqs, arg_prereq);
|
||||||
|
constraint_db.push(Constraint::Equivalent(
|
||||||
|
location,
|
||||||
|
inferred_type,
|
||||||
|
target_type,
|
||||||
|
));
|
||||||
|
new_valref
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let last_call =
|
||||||
|
ir::Expression::Call(loc.clone(), return_type.clone(), Box::new(fun), new_args);
|
||||||
|
|
||||||
|
if prereqs.is_empty() {
|
||||||
|
(last_call, return_type)
|
||||||
|
} else {
|
||||||
|
prereqs.push(last_call);
|
||||||
|
(
|
||||||
|
ir::Expression::Block(loc, return_type.clone(), prereqs),
|
||||||
|
return_type,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
syntax::Expression::Block(loc, stmts) => {
|
syntax::Expression::Block(loc, stmts) => {
|
||||||
let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void);
|
let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void);
|
||||||
let mut exprs = vec![];
|
let mut exprs = vec![];
|
||||||
@@ -381,6 +432,12 @@ fn finalize_name(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn merge_prereq<T>(left: &mut Vec<T>, prereq: Option<T>) {
|
||||||
|
if let Some(item) = prereq {
|
||||||
|
left.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// use super::*;
|
// use super::*;
|
||||||
|
|||||||
@@ -91,6 +91,15 @@ fn finalize_expression(
|
|||||||
|
|
||||||
Expression::Print(loc, var) => Expression::Print(loc, var),
|
Expression::Print(loc, var) => Expression::Print(loc, var),
|
||||||
|
|
||||||
|
Expression::Call(loc, ty, fun, args) => Expression::Call(
|
||||||
|
loc,
|
||||||
|
finalize_type(ty, resolutions),
|
||||||
|
Box::new(finalize_val_or_ref(*fun, resolutions)),
|
||||||
|
args.into_iter()
|
||||||
|
.map(|x| finalize_val_or_ref(x, resolutions))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
|
||||||
Expression::Bind(loc, var, ty, subexp) => Expression::Bind(
|
Expression::Bind(loc, var, ty, subexp) => Expression::Bind(
|
||||||
loc,
|
loc,
|
||||||
var,
|
var,
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ impl From<TypeInferenceError> for Diagnostic<usize> {
|
|||||||
}
|
}
|
||||||
TypeInferenceError::CouldNotSolve(Constraint::Equivalent(loc, a, b)) => {
|
TypeInferenceError::CouldNotSolve(Constraint::Equivalent(loc, a, b)) => {
|
||||||
loc.labelled_error("internal error").with_message(format!(
|
loc.labelled_error("internal error").with_message(format!(
|
||||||
"could not determine if {} and {:#?} were equivalent",
|
"could not determine if {} and {} were equivalent",
|
||||||
a, b
|
a, b
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ pub fn solve_constraints(
|
|||||||
// constraints. Internal to the loop, we have a check that will make sure that we
|
// constraints. Internal to the loop, we have a check that will make sure that we
|
||||||
// do (eventually) stop.
|
// do (eventually) stop.
|
||||||
while changed_something && !constraint_db.is_empty() {
|
while changed_something && !constraint_db.is_empty() {
|
||||||
println!("CONSTRAINT:");
|
println!("\n\n\nCONSTRAINT:");
|
||||||
for constraint in constraint_db.iter() {
|
for constraint in constraint_db.iter() {
|
||||||
println!(" {}", constraint);
|
println!(" {}", constraint);
|
||||||
}
|
}
|
||||||
@@ -300,8 +300,9 @@ pub fn solve_constraints(
|
|||||||
Constraint::IsSomething(_, TypeOrVar::Variable(_, ref name)) => {
|
Constraint::IsSomething(_, TypeOrVar::Variable(_, ref name)) => {
|
||||||
if resolutions.get(name).is_none() {
|
if resolutions.get(name).is_none() {
|
||||||
constraint_db.push(constraint);
|
constraint_db.push(constraint);
|
||||||
|
} else {
|
||||||
|
changed_something = true;
|
||||||
}
|
}
|
||||||
changed_something = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case #1a: We have two primitive types. If they're equal, we've discharged this
|
// Case #1a: We have two primitive types. If they're equal, we've discharged this
|
||||||
@@ -355,12 +356,15 @@ pub fn solve_constraints(
|
|||||||
// type.
|
// type.
|
||||||
Constraint::Equivalent(loc, t, TypeOrVar::Variable(vloc, name))
|
Constraint::Equivalent(loc, t, TypeOrVar::Variable(vloc, name))
|
||||||
| Constraint::Equivalent(loc, TypeOrVar::Variable(vloc, name), t) => {
|
| Constraint::Equivalent(loc, TypeOrVar::Variable(vloc, name), t) => {
|
||||||
|
println!("IN THIS CASE with {}", name);
|
||||||
match resolutions.get(&name) {
|
match resolutions.get(&name) {
|
||||||
None => match t.try_into() {
|
None => match t.try_into() {
|
||||||
Ok(real_type) => {
|
Ok(real_type) => {
|
||||||
|
println!(" HERE with {} and {}", name, real_type);
|
||||||
resolutions.insert(name, real_type);
|
resolutions.insert(name, real_type);
|
||||||
}
|
}
|
||||||
Err(variable_type) => {
|
Err(variable_type) => {
|
||||||
|
println!(" REJECTED INTO RETURN with {} and {}", name, variable_type);
|
||||||
constraint_db.push(Constraint::Equivalent(
|
constraint_db.push(Constraint::Equivalent(
|
||||||
loc,
|
loc,
|
||||||
variable_type,
|
variable_type,
|
||||||
@@ -369,7 +373,9 @@ pub fn solve_constraints(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(t2) if &t == t2 => {}
|
Some(t2) if &t == t2 => {
|
||||||
|
println!(" MATCHED at {} == {}", t, t2);
|
||||||
|
}
|
||||||
Some(t2) => errors.push(TypeInferenceError::NotEquivalent(
|
Some(t2) => errors.push(TypeInferenceError::NotEquivalent(
|
||||||
loc,
|
loc,
|
||||||
t,
|
t,
|
||||||
|
|||||||
Reference in New Issue
Block a user