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 { 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,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( pub fn pretty_typed_block(
&self, &self,
w: &mut dyn std::fmt::Write, w: &mut dyn std::fmt::Write,
@ -292,7 +341,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 +354,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();

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)] #[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,

View File

@ -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
}
}

View File

@ -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,36 @@ 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,
}
}
/// 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::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),
} }
} }
} }

View File

@ -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 {