add first steps toward generic type inference
This commit is contained in:
parent
270713b3f9
commit
b890c0594d
|
@ -133,7 +133,11 @@ impl Context {
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
typed_ir::ExprKind::Literal(lit) => Ok(lit.clone()),
|
typed_ir::ExprKind::Literal(lit) => Ok(lit.clone()),
|
||||||
typed_ir::ExprKind::Path(_) => todo!("evaluate path"),
|
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 {
|
kind: typed_ir::ExprKind::Call {
|
||||||
called: self.callables.builtins.bitnot,
|
called: self.callables.builtins.bitnot,
|
||||||
args: vec![a],
|
args: vec![a],
|
||||||
|
genargs: vec![],
|
||||||
},
|
},
|
||||||
typ: self.types.primitives.infer,
|
typ: self.types.primitives.infer,
|
||||||
}
|
}
|
||||||
|
@ -185,6 +190,7 @@ impl Context {
|
||||||
kind: typed_ir::ExprKind::Call {
|
kind: typed_ir::ExprKind::Call {
|
||||||
called: self.callables.builtins.xor,
|
called: self.callables.builtins.xor,
|
||||||
args: vec![a, b],
|
args: vec![a, b],
|
||||||
|
genargs: vec![],
|
||||||
},
|
},
|
||||||
typ: self.types.primitives.infer,
|
typ: self.types.primitives.infer,
|
||||||
}
|
}
|
||||||
|
@ -203,11 +209,17 @@ impl Context {
|
||||||
expected: called_callable.argcount(),
|
expected: called_callable.argcount(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
let genargs_resolved = called_callable
|
||||||
|
.genargs
|
||||||
|
.iter()
|
||||||
|
.map(|genarg| genarg.1)
|
||||||
|
.collect();
|
||||||
typed_ir::Expr {
|
typed_ir::Expr {
|
||||||
id,
|
id,
|
||||||
kind: typed_ir::ExprKind::Call {
|
kind: typed_ir::ExprKind::Call {
|
||||||
called,
|
called,
|
||||||
args: args_resolved,
|
args: args_resolved,
|
||||||
|
genargs: genargs_resolved,
|
||||||
},
|
},
|
||||||
typ: self.types.primitives.infer,
|
typ: self.types.primitives.infer,
|
||||||
}
|
}
|
||||||
|
@ -270,6 +282,44 @@ impl Context {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn infer_expr_types(&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 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 args_typed: Vec<_> =
|
||||||
|
args.iter().map(|ex| self.infer_expr_types(ex)).collect();
|
||||||
|
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)| {})
|
||||||
|
.collect();
|
||||||
|
expr.clone().with_type(callee_def.ret_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infer_types(&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(
|
pub fn pretty_typed_block(
|
||||||
&self,
|
&self,
|
||||||
w: &mut dyn std::fmt::Write,
|
w: &mut dyn std::fmt::Write,
|
||||||
|
@ -292,7 +342,11 @@ impl Context {
|
||||||
let expr_pretty = match &expr.kind {
|
let expr_pretty = match &expr.kind {
|
||||||
typed_ir::ExprKind::Literal(_) => todo!(),
|
typed_ir::ExprKind::Literal(_) => todo!(),
|
||||||
typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0),
|
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
|
let args = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
|
@ -301,7 +355,20 @@ impl Context {
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, std::fmt::Error>>()?;
|
.collect::<Result<Vec<_>, std::fmt::Error>>()?;
|
||||||
let callable = self.callables.get(*called);
|
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();
|
let mut type_pretty = String::new();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::types::{GenericArg, Type, TypingContext};
|
use super::types::{Type, TypingContext};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
|
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
|
||||||
pub struct CallableId(pub usize);
|
pub struct CallableId(pub usize);
|
||||||
|
@ -6,7 +6,8 @@ pub struct CallableId(pub usize);
|
||||||
pub struct Callable {
|
pub struct Callable {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub args: Vec<(Option<String>, Type)>,
|
pub args: Vec<(Option<String>, Type)>,
|
||||||
pub ret_type: Option<Type>,
|
pub genargs: Vec<(Option<String>, Type)>,
|
||||||
|
pub ret_type: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ty> Callable {
|
impl<'ty> Callable {
|
||||||
|
@ -38,22 +39,29 @@ impl CallableContext {
|
||||||
reduce_or: CallableId(2),
|
reduce_or: CallableId(2),
|
||||||
};
|
};
|
||||||
let logic1 = typectx.make_logic_size(1);
|
let logic1 = typectx.make_logic_size(1);
|
||||||
|
let logic_tvar0 = typectx.make_typevar(0, 0);
|
||||||
Self {
|
Self {
|
||||||
callables: vec![
|
callables: vec![
|
||||||
Callable {
|
Callable {
|
||||||
name: "builtin::xor".to_string(),
|
name: "builtin::xor".to_string(),
|
||||||
args: vec![],
|
args: vec![
|
||||||
ret_type: Some(typectx.primitives.logic),
|
(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 {
|
Callable {
|
||||||
name: "builtin::bitnot".to_string(),
|
name: "builtin::bitnot".to_string(),
|
||||||
args: vec![],
|
args: vec![(Some("a".to_string()), logic_tvar0)],
|
||||||
ret_type: Some(typectx.primitives.logic),
|
genargs: vec![(Some("T".to_string()), typectx.primitives.logic)],
|
||||||
|
ret_type: logic_tvar0,
|
||||||
},
|
},
|
||||||
Callable {
|
Callable {
|
||||||
name: "builtin::reduce_or".to_string(),
|
name: "builtin::reduce_or".to_string(),
|
||||||
args: vec![(Some("a".to_string()), typectx.primitives.logic)],
|
args: vec![(Some("a".to_string()), typectx.primitives.logic)],
|
||||||
ret_type: Some(logic1),
|
genargs: vec![],
|
||||||
|
ret_type: logic1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
builtins,
|
builtins,
|
||||||
|
|
|
@ -32,7 +32,11 @@ pub struct Expr {
|
||||||
pub enum ExprKind {
|
pub enum ExprKind {
|
||||||
Literal(ElabData),
|
Literal(ElabData),
|
||||||
Path(DefId),
|
Path(DefId),
|
||||||
Call { called: CallableId, args: Vec<Expr> },
|
Call {
|
||||||
|
called: CallableId,
|
||||||
|
args: Vec<Expr>,
|
||||||
|
genargs: Vec<Type>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -47,3 +51,10 @@ pub struct Block {
|
||||||
pub signals: Vec<Signal>,
|
pub signals: Vec<Signal>,
|
||||||
pub expr: Expr,
|
pub expr: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Expr {
|
||||||
|
pub fn with_type(mut self, typ: Type) -> Self {
|
||||||
|
self.typ = typ;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@ enum TypeKind {
|
||||||
Callable(FnSig),
|
Callable(FnSig),
|
||||||
/// A type that was not given and needs to be inferred
|
/// A type that was not given and needs to be inferred
|
||||||
Infer,
|
Infer,
|
||||||
|
/// A reference to a type variable as DeBruijn index
|
||||||
|
/// (scope, param)
|
||||||
|
TypeVar(u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -131,6 +134,12 @@ impl TypingContext {
|
||||||
.unwrap()
|
.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> {
|
pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option<Type> {
|
||||||
// TODO: return proper error type here
|
// TODO: return proper error type here
|
||||||
match &self.get(typ).kind {
|
match &self.get(typ).kind {
|
||||||
|
@ -159,7 +168,29 @@ impl TypingContext {
|
||||||
TypeKind::UInt(_) => todo!(),
|
TypeKind::UInt(_) => todo!(),
|
||||||
TypeKind::Callable(_sig) => todo!("callable generic params"),
|
TypeKind::Callable(_sig) => todo!("callable generic params"),
|
||||||
// need to know what the type is to parameterize it
|
// need to know what the type is to parameterize it
|
||||||
TypeKind::Infer => None,
|
TypeKind::Infer | &TypeKind::TypeVar(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 +216,7 @@ impl TypingContext {
|
||||||
TypeKind::Infer => write!(w, "?"),
|
TypeKind::Infer => write!(w, "?"),
|
||||||
TypeKind::UInt(_) => todo!("print uint"),
|
TypeKind::UInt(_) => todo!("print uint"),
|
||||||
TypeKind::Callable(_sig) => todo!("print callable"),
|
TypeKind::Callable(_sig) => todo!("print callable"),
|
||||||
|
TypeKind::TypeVar(_, tvar) => write!(w, "T{}", tvar),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,14 @@ fn main() {
|
||||||
frontendcontext
|
frontendcontext
|
||||||
.pretty_typed_block(&mut pretty_block, &block)
|
.pretty_typed_block(&mut pretty_block, &block)
|
||||||
.unwrap();
|
.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 {
|
match lowered {
|
||||||
|
|
Loading…
Reference in New Issue