Compare commits

...

2 Commits

Author SHA1 Message Date
NotAFile cce8a3bde4 add type comparison function 2022-02-20 18:26:14 +01:00
NotAFile b890c0594d add first steps toward generic type inference 2022-02-20 17:49:28 +01:00
5 changed files with 143 additions and 13 deletions

View File

@ -133,7 +133,11 @@ impl Context {
match &expr.kind {
typed_ir::ExprKind::Literal(lit) => Ok(lit.clone()),
typed_ir::ExprKind::Path(_) => todo!("evaluate path"),
typed_ir::ExprKind::Call { called, args } => todo!("evaluate call"),
typed_ir::ExprKind::Call {
called,
args,
genargs,
} => todo!("evaluate call"),
}
}
@ -174,6 +178,7 @@ impl Context {
kind: typed_ir::ExprKind::Call {
called: self.callables.builtins.bitnot,
args: vec![a],
genargs: vec![],
},
typ: self.types.primitives.infer,
}
@ -185,6 +190,7 @@ impl Context {
kind: typed_ir::ExprKind::Call {
called: self.callables.builtins.xor,
args: vec![a, b],
genargs: vec![],
},
typ: self.types.primitives.infer,
}
@ -203,11 +209,17 @@ impl Context {
expected: called_callable.argcount(),
}));
}
let genargs_resolved = called_callable
.genargs
.iter()
.map(|genarg| genarg.1)
.collect();
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Call {
called,
args: args_resolved,
genargs: genargs_resolved,
},
typ: self.types.primitives.infer,
}
@ -270,6 +282,43 @@ impl Context {
)))
}
pub fn infer_expr_types(&mut self, expr: &typed_ir::Expr) -> typed_ir::Expr {
if self.types.is_fully_typed(expr.typ) {
// there is nothing more to infer
return expr.clone();
}
match &expr.kind {
typed_ir::ExprKind::Literal(_) => todo!(),
typed_ir::ExprKind::Path(_) => todo!(),
typed_ir::ExprKind::Call {
called,
args,
genargs,
} => {
let args_typed: Vec<_> = args.iter().map(|ex| self.infer_expr_types(ex)).collect();
let callee_def = self.callables.get(*called);
if self.types.is_fully_typed(callee_def.ret_type) {
expr.clone().with_type(callee_def.ret_type)
} else {
let param_types: Vec<_> = callee_def.args.iter().map(|param| param.1).collect();
let mut genargs = callee_def.genargs.clone();
let inferred_args: Vec<_> = param_types
.iter()
.zip(args_typed)
.map(|(param, arg)| self.types.infer_type(*param, arg.typ))
.collect();
expr.clone().with_type(callee_def.ret_type)
}
}
}
}
pub fn infer_types(&mut self, mut block: typed_ir::Block) -> typed_ir::Block {
let new_root = self.infer_expr_types(&block.expr);
block.expr = new_root;
block
}
pub fn pretty_typed_block(
&self,
w: &mut dyn std::fmt::Write,
@ -292,7 +341,11 @@ impl Context {
let expr_pretty = match &expr.kind {
typed_ir::ExprKind::Literal(_) => todo!(),
typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0),
typed_ir::ExprKind::Call { called, args } => {
typed_ir::ExprKind::Call {
called,
args,
genargs,
} => {
let args = args
.iter()
.map(|arg| {
@ -301,7 +354,20 @@ impl Context {
})
.collect::<Result<Vec<_>, std::fmt::Error>>()?;
let callable = self.callables.get(*called);
format!("{}({})", callable.name(), args.join(", "))
let genargs = genargs
.iter()
.map(|param| {
let mut type_str = String::new();
self.types.pretty_type(&mut type_str, *param)?;
Ok(type_str)
})
.collect::<Result<Vec<_>, std::fmt::Error>>()?;
format!(
"{}<{}>({})",
callable.name(),
genargs.join(", "),
args.join(", ")
)
}
};
let mut type_pretty = String::new();

View File

@ -1,4 +1,4 @@
use super::types::{GenericArg, Type, TypingContext};
use super::types::{Type, TypingContext};
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
pub struct CallableId(pub usize);
@ -6,7 +6,8 @@ pub struct CallableId(pub usize);
pub struct Callable {
pub name: String,
pub args: Vec<(Option<String>, Type)>,
pub ret_type: Option<Type>,
pub genargs: Vec<(Option<String>, Type)>,
pub ret_type: Type,
}
impl<'ty> Callable {
@ -38,22 +39,29 @@ impl CallableContext {
reduce_or: CallableId(2),
};
let logic1 = typectx.make_logic_size(1);
let logic_tvar0 = typectx.make_typevar(0, 0);
Self {
callables: vec![
Callable {
name: "builtin::xor".to_string(),
args: vec![],
ret_type: Some(typectx.primitives.logic),
args: vec![
(Some("a".to_string()), logic_tvar0),
(Some("b".to_string()), logic_tvar0),
],
genargs: vec![(Some("T".to_string()), typectx.primitives.logic)],
ret_type: logic_tvar0,
},
Callable {
name: "builtin::bitnot".to_string(),
args: vec![],
ret_type: Some(typectx.primitives.logic),
args: vec![(Some("a".to_string()), logic_tvar0)],
genargs: vec![(Some("T".to_string()), typectx.primitives.logic)],
ret_type: logic_tvar0,
},
Callable {
name: "builtin::reduce_or".to_string(),
args: vec![(Some("a".to_string()), typectx.primitives.logic)],
ret_type: Some(logic1),
genargs: vec![],
ret_type: logic1,
},
],
builtins,

View File

@ -32,7 +32,11 @@ pub struct Expr {
pub enum ExprKind {
Literal(ElabData),
Path(DefId),
Call { called: CallableId, args: Vec<Expr> },
Call {
called: CallableId,
args: Vec<Expr>,
genargs: Vec<Type>,
},
}
#[derive(Debug, Clone)]
@ -47,3 +51,10 @@ pub struct Block {
pub signals: Vec<Signal>,
pub expr: Expr,
}
impl Expr {
pub fn with_type(mut self, typ: Type) -> Self {
self.typ = typ;
self
}
}

View File

@ -30,6 +30,9 @@ enum TypeKind {
Callable(FnSig),
/// A type that was not given and needs to be inferred
Infer,
/// A reference to a type variable as DeBruijn index
/// (scope, param)
TypeVar(u32, u32),
}
#[derive(Debug, Clone)]
@ -131,6 +134,12 @@ impl TypingContext {
.unwrap()
}
pub fn make_typevar(&mut self, dbi: u32, tvar: u32) -> Type {
self.add(TypeStruct {
kind: TypeKind::TypeVar(dbi, tvar),
})
}
pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option<Type> {
// TODO: return proper error type here
match &self.get(typ).kind {
@ -159,7 +168,36 @@ impl TypingContext {
TypeKind::UInt(_) => todo!(),
TypeKind::Callable(_sig) => todo!("callable generic params"),
// need to know what the type is to parameterize it
TypeKind::Infer => None,
TypeKind::Infer | &TypeKind::TypeVar(_, _) => None,
}
}
/// Given the type of a variable in two locations, infer what the true type should be
pub fn infer_type(&mut self, typ_a: Type, typ_b: Type) -> Type {
match (&self.get(typ_a).kind, &self.get(typ_b).kind) {
(a, b) => panic!("cannot infer between: {:?}, {:?}", a, b),
}
}
/// return whether the type has no unfilled parameters
pub fn is_fully_typed(&self, typ: Type) -> bool {
match &self.get(typ).kind {
TypeKind::ElabType(_) => todo!(),
TypeKind::Logic(data) => {
if let ElabValue::Concrete(_) = data.value {
true
} else {
false
}
}
TypeKind::UInt(_) => todo!(),
TypeKind::Callable(_) => todo!(),
TypeKind::Infer => false,
TypeKind::TypeVar(dbi, _tvar) => {
// if the DeBruijn index is 0, there is no further information to gain
// from a surrounding scope
*dbi != 0
}
}
}
@ -185,6 +223,7 @@ impl TypingContext {
TypeKind::Infer => write!(w, "?"),
TypeKind::UInt(_) => todo!("print uint"),
TypeKind::Callable(_sig) => todo!("print callable"),
TypeKind::TypeVar(_, tvar) => write!(w, "T{}", tvar),
}
}
}

View File

@ -67,8 +67,14 @@ fn main() {
frontendcontext
.pretty_typed_block(&mut pretty_block, &block)
.unwrap();
println!("{}", &pretty_block);
let typed_inferred = frontendcontext.infer_types(block);
let mut pretty_block = String::new();
frontendcontext
.pretty_typed_block(&mut pretty_block, &typed_inferred)
.unwrap();
println!("{}", &pretty_block);
}
println!("{}", &pretty_block);
}
/*
match lowered {