diff --git a/compiler/src/infer.rs b/compiler/src/infer.rs index cf73b71..f8986b1 100644 --- a/compiler/src/infer.rs +++ b/compiler/src/infer.rs @@ -12,7 +12,6 @@ use crate::{ pub enum FnKind { Id(usize), DefineField(AtomRef), - PutField(AtomRef), } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -42,16 +41,44 @@ fn join_ty(a: &Type, b: &Type) -> Type { } } +fn get_field(ty: &Type, field: AtomRef, cache: &mut TypeCache) -> Type { + match ty { + Type::ReturnOf((fn_kind, args)) => match fn_kind { + FnKind::Id(id) => { + let ret_ty = cache + .get(&InferRequest { + fn_id: *id, + arg_types: args.clone(), + }) + .unwrap() + .ret_type + .clone(); + + get_field(&ret_ty, field, cache) + } + FnKind::DefineField(atom) => { + if *atom == field { + args[0].clone() + } else { + Type::Undefined + } + } + }, + Type::Any => Type::Any, + _ => Type::Undefined, + } +} + #[derive(Debug, Hash, PartialEq, Eq)] pub struct InferRequest { - fn_id: usize, - arg_types: Vec, + pub fn_id: usize, + pub arg_types: Vec, } #[derive(Debug)] pub struct InferResult { - val_types: Vec, - ret_type: Type, + pub val_types: Vec, + pub ret_type: Type, } pub type TypeCache = HashMap; @@ -64,139 +91,143 @@ pub fn infer_types(reg_fns: &[RegFn], atoms: &[Atom]) -> TypeCache { .unwrap(); let mut cache = TypeCache::with_capacity(reg_fns.len()); - let mut worklist = vec![]; - worklist.push(InferRequest { + let req = InferRequest { fn_id: main_fn_id, arg_types: vec![], - }); + }; - while let Some(req) = worklist.pop() { - match walk_fn(&req, reg_fns, &mut cache) { - Ok(res) => { - cache.insert(req, res); - } - Err(new_req) => { - worklist.push(new_req); - worklist.push(req); - } - } - } + walk_fn(req, reg_fns, &mut cache); cache } -fn walk_fn( - req: &InferRequest, - reg_fns: &[RegFn], - cache: &mut TypeCache, -) -> Result { +fn walk_fn(req: InferRequest, reg_fns: &[RegFn], cache: &mut TypeCache) { use OpCode::*; let f = ®_fns[req.fn_id]; let mut val_types = vec![Type::Unknown; f.val_count]; let mut ret_type = Type::Unknown; - for instr in &f.code { - let is_ret = matches!(instr.op, Return | ReturnAsync | ReturnUndef); + for block in &f.blocks { + let code = &f.code[block.start..block.end]; + let code = code.iter().chain(&block.postfix); - if instr.dsts.is_empty() && !is_ret { - continue; - } + for instr in code { + let is_ret = matches!(instr.op, Return | ReturnAsync | ReturnUndef); - let ty = match instr.op { - // ──────────────────────────────────── Literals ──────────────────────────────────── - PushMinus1 | Push0 | Push1 | Push2 | Push3 | Push4 | Push5 | Push6 | Push7 | PushI8 - | PushI16 | PushI32 => Type::Int, + if instr.dsts.is_empty() && !is_ret { + continue; + } - PushTrue | PushFalse => Type::Bool, + // TODO: Some of these ops may not every show up by this point + let ty = match instr.op { + // ──────────────────────────────────── Literals ──────────────────────────────────── + PushMinus1 | Push0 | Push1 | Push2 | Push3 | Push4 | Push5 | Push6 | Push7 + | PushI8 | PushI16 | PushI32 => Type::Int, - // TODO: Fclosure will disappear soon - PushConst | PushConst8 | Fclosure | Fclosure8 => match instr.imm { - ImmOperand::Const(k) => match f.cpool.get(k as usize) { - Some(JsValue::Int(_)) => Type::Int, - Some(JsValue::Float(_)) => Type::Float, - Some(JsValue::BoolTrue) | Some(JsValue::BoolFalse) => Type::Bool, - Some(&JsValue::FnById(id)) => Type::Fn(FnKind::Id(id)), + PushTrue | PushFalse => Type::Bool, + + PushConst | PushConst8 => match instr.imm { + ImmOperand::Const(k) => match f.cpool.get(k as usize) { + Some(JsValue::Int(_)) => Type::Int, + Some(JsValue::Float(_)) => Type::Float, + Some(JsValue::BoolTrue) | Some(JsValue::BoolFalse) => Type::Bool, + Some(&JsValue::FnById(id)) => Type::Fn(FnKind::Id(id)), + _ => unreachable!(), + }, _ => unreachable!(), }, - _ => unreachable!(), - }, - // ─────────────────────────────────── Arithmetic ─────────────────────────────────── - Add | Sub | Mul | Div | Mod | Pow => { - match (&val_types[instr.args[0]], &val_types[instr.args[1]]) { - (Type::Int, Type::Int) => Type::Int, - (Type::Float, Type::Float) - | (Type::Int, Type::Float) - | (Type::Float, Type::Int) => Type::Float, - _ => Type::Any, + // ─────────────────────────────────── Arithmetic ─────────────────────────────────── + Add | Sub | Mul | Mod | Pow => { + match (&val_types[instr.args[0]], &val_types[instr.args[1]]) { + (Type::Int, Type::Int) => Type::Int, + (Type::Float, Type::Float) + | (Type::Int, Type::Float) + | (Type::Float, Type::Int) => Type::Float, + _ => Type::Any, + } } - } + Neg | Plus | Inc | Dec => match val_types[instr.args[0]] { + Type::Int => Type::Int, + Type::Float => Type::Float, + _ => Type::Any, + }, + Div => Type::Float, - Neg | Plus | Inc | Dec => match val_types[instr.args[0]] { - Type::Int => Type::Int, - Type::Float => Type::Float, - _ => Type::Any, - }, + Lt | Lte | Gt | Gte | Eq | Neq | StrictEq | StrictNeq | Not => Type::Bool, - Lt | Lte | Gt | Gte | Eq | Neq | StrictEq | StrictNeq | Not => Type::Bool, + // ────────────────────────────────── Value Movers ────────────────────────────────── + Dup | SetLoc | SetName => val_types[instr.args[0]].clone(), - // ────────────────────────────────── Value Movers ────────────────────────────────── - Dup | SetLoc => val_types[instr.args[0]].clone(), - - // ──────────────────────────────────── Arguments ─────────────────────────────────── - OpCode::GetArg0 => req.arg_types.first().cloned().unwrap_or(Type::Undefined), - OpCode::GetArg1 => req.arg_types.get(1).cloned().unwrap_or(Type::Undefined), - OpCode::GetArg2 => req.arg_types.get(2).cloned().unwrap_or(Type::Undefined), - OpCode::GetArg3 => req.arg_types.get(3).cloned().unwrap_or(Type::Undefined), - - OpCode::GetArg => match instr.imm { - ImmOperand::Arg(k) => req - .arg_types - .get(k as usize) - .cloned() - .unwrap_or(Type::Undefined), - _ => unreachable!(), - }, - - // ──────────────────────────────────── Objects ───────────────────────────────────── - OpCode::Object => Type::Empty, - OpCode::DefineField => { - let atom = match instr.imm { - ImmOperand::Atom(atom) => atom, + // ──────────────────────────────────── Arguments ─────────────────────────────────── + GetArg0 => req.arg_types.first().cloned().unwrap_or(Type::Undefined), + GetArg1 => req.arg_types.get(1).cloned().unwrap_or(Type::Undefined), + GetArg2 => req.arg_types.get(2).cloned().unwrap_or(Type::Undefined), + GetArg3 => req.arg_types.get(3).cloned().unwrap_or(Type::Undefined), + GetArg => match instr.imm { + ImmOperand::Arg(k) => req + .arg_types + .get(k as usize) + .cloned() + .unwrap_or(Type::Undefined), _ => unreachable!(), - }; + }, - let arg0_ty = val_types[instr.args[0]].clone(); - let arg1_ty = val_types[instr.args[1]].clone(); + // ──────────────────────────────────── Objects ───────────────────────────────────── + Object => Type::Empty, + DefineField => { + let field = match instr.imm { + ImmOperand::Atom(atom) => atom, + _ => unreachable!(), + }; - ReturnOf((FnKind::DefineField(atom), vec![arg0_ty, arg1_ty])) - } + let arg0_ty = val_types[instr.args[0]].clone(); + let arg1_ty = val_types[instr.args[1]].clone(); - // ────────────────────────────────────── Returns ─────────────────────────────────── - Return | ReturnAsync => val_types[instr.args[0]].clone(), - ReturnUndef => Type::Empty, + ReturnOf((FnKind::DefineField(field), vec![arg0_ty, arg1_ty])) + } + GetField => { + let field = match instr.imm { + ImmOperand::Atom(atom) => atom, + _ => unreachable!(), + }; - _ => unreachable!(), - }; + let ty = val_types[instr.args[0]].clone(); + get_field(&ty, field, cache) + } - if is_ret { - ret_type = join_ty(&ret_type, &ty); - } else { - for &dst in &instr.dsts { - val_types[dst] = join_ty(&val_types[dst], &ty); + // ──────────────────────────────── Function Calls ────────────────────────────────── + Call | Call0 | Call1 | Call2 | Call3 | TailCall => todo!(), + + // ────────────────────────────────────── Returns ─────────────────────────────────── + Return | ReturnAsync => val_types[instr.args[0]].clone(), + ReturnUndef => Type::Empty, + + _ => unreachable!("{instr:?}"), + }; + + if is_ret { + ret_type = join_ty(&ret_type, &ty); + } else { + for &dst in &instr.dsts { + val_types[dst] = join_ty(&val_types[dst], &ty); + } } } } - Ok(InferResult { - val_types, - ret_type, - }) + cache.insert( + req, + InferResult { + val_types, + ret_type, + }, + ); } -pub fn atom_to_str(a: &AtomRef, atoms: &[Atom]) -> String { +fn atom_to_str(a: &AtomRef, atoms: &[Atom]) -> String { match a { AtomRef::TaggedInt(n) => n.to_string(), AtomRef::Predefined(i) => atom_names::PREDEFINED_ATOMS