diff --git a/lang/src/backend/hbvm.rs b/lang/src/backend/hbvm.rs index 0cb709d3..0181eb06 100644 --- a/lang/src/backend/hbvm.rs +++ b/lang/src/backend/hbvm.rs @@ -273,7 +273,7 @@ impl Backend for HbvmBackend { tys: &Types, files: &EntSlice, ) { - let sig = tys.ins.funcs[id].sig.unwrap(); + let sig = tys.ins.funcs[id].sig; debug_assert!(self.code.is_empty()); diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 173ddbea..9eff8c2f 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -272,6 +272,10 @@ impl Ident { self.0 & ((1 << Self::LEN_BITS) - 1) } + pub fn is_type(self) -> bool { + ty::Builtin::try_from(self) == Ok(ty::Builtin::TYPE) + } + pub fn is_empty(self) -> bool { self.len() == 0 } diff --git a/lang/src/son.rs b/lang/src/son.rs index b9434aa8..8199c0ee 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -16,7 +16,7 @@ use { ty::{ self, Arg, ArrayLen, CompState, ConstData, EnumData, EnumField, FTask, FuncData, GlobalData, Loc, Module, Offset, OffsetIter, OptLayout, Sig, StringRef, StructData, - StructField, SymKey, Tuple, TypeBase, TypeIns, Types, UnionData, + StructField, SymKey, TemplateData, Tuple, TypeBase, TypeIns, Types, UnionData, }, utils::{BitSet, EntSlice, Vc}, Ident, @@ -2782,7 +2782,7 @@ impl<'a> Codegen<'a> { let fuc = self.tys.ins.funcs.push(FuncData { file, - sig: Some(Sig { args: Tuple::empty(), ret }), + sig: Sig { args: Tuple::empty(), ret }, ..Default::default() }); @@ -2894,8 +2894,8 @@ impl<'a> Codegen<'a> { fn make_func_reachable(&mut self, func: ty::Func) { let state_slot = self.ct.active() as usize; let fuc = &mut self.tys.ins.funcs[func]; - if fuc.comp_state[state_slot] == CompState::Dead { - fuc.comp_state[state_slot] = CompState::Queued(self.tys.tasks.len() as _); + if CompState::from(fuc.comp_state[state_slot]) == CompState::Dead { + fuc.comp_state[state_slot] = CompState::Queued(self.tys.tasks.len() as _).into(); self.tys.tasks.push(Some(FTask { file: fuc.file, id: func, ct: self.ct.active() })); } } @@ -4231,12 +4231,7 @@ impl<'a> Codegen<'a> { } ref e => { let ty = self.parse_ty( - TyScope { - file: self.ci.file, - parent: self.ci.parent, - name: None, - alloc_const: false, - }, + TyScope { file: self.ci.file, parent: self.ci.parent, ..Default::default() }, e, ); Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, ty)) @@ -4327,6 +4322,7 @@ impl<'a> Codegen<'a> { | ty::Kind::Slice(_) | ty::Kind::Opt(_) | ty::Kind::Func(_) + | ty::Kind::Template(_) | ty::Kind::Global(_) | ty::Kind::Const(_)) => self.error( pos, @@ -4432,21 +4428,16 @@ impl<'a> Codegen<'a> { ref e => (self.ty(e), None), }; - let ty::Kind::Func(mut fu) = ty.expand() else { - self.error(func.pos(), fa!("compiler cant (yet) call '{}'", self.ty_display(ty))); + let Some(fu) = self.compute_signature(ty, func.pos(), args) else { return Value::NEVER; }; - let Some(sig) = self.compute_signature(&mut fu, func.pos(), args) else { - return Value::NEVER; - }; - - inline |= sig.ret == ty::Id::TYPE; - - let FuncData { expr, file, is_inline, parent, .. } = self.tys.ins.funcs[fu]; + let FuncData { expr, file, is_inline, parent, sig, .. } = self.tys.ins.funcs[fu]; let ast = &self.files[file]; let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() }; + inline |= sig.ret == ty::Id::TYPE; + let arg_count = args.len() + caller.is_some() as usize; if arg_count != cargs.len() { self.error( @@ -4803,79 +4794,90 @@ impl<'a> Codegen<'a> { } } - fn compute_signature(&mut self, func: &mut ty::Func, pos: Pos, args: &[Expr]) -> Option { - let FuncData { file, expr, sig, parent, .. } = self.tys.ins.funcs[*func]; + fn compute_signature(&mut self, func: ty::Id, pos: Pos, args: &[Expr]) -> Option { + let template = match func.expand() { + ty::Kind::Func(f) => return Some(f), + ty::Kind::Template(t) => t, + _ => { + self.error(pos, fa!("compiler cant (yet) call '{}'", self.ty_display(func))); + return None; + } + }; + + let TemplateData { file, expr, parent, name, is_inline, .. } = + self.tys.ins.templates[template]; let fast = &self.files[file]; - let &Expr::Closure { args: cargs, ret, .. } = expr.get(fast) else { + let &Expr::Closure { pos, args: cargs, ret, .. } = expr.get(fast) else { unreachable!(); }; - Some(if let Some(sig) = sig { - sig - } else { - let arg_base = self.tys.tmp.args.len(); + let arg_base = self.tys.tmp.args.len(); - let base = self.ci.scope.vars.len(); - for (arg, carg) in args.iter().zip(cargs) { - let ty = self.ty_in(file, parent, &carg.ty); + let base = self.ci.scope.vars.len(); + for (arg, carg) in args.iter().zip(cargs) { + let ty = self.ty_in(file, parent, &carg.ty); - self.tys.tmp.args.push(ty); - let sym = parser::find_symbol(&fast.symbols, carg.id); - let ty = if sym.flags & idfl::COMPTIME == 0 { - // FIXME: could fuck us - continue; - } else { - if ty != ty::Id::TYPE { - self.error( - arg.pos(), - fa!( - "arbitrary comptime types are not supported yet \ + self.tys.tmp.args.push(ty); + let sym = parser::find_symbol(&fast.symbols, carg.id); + let ty = if sym.flags & idfl::COMPTIME == 0 { + // FIXME: could fuck us + continue; + } else { + if ty != ty::Id::TYPE { + self.error( + arg.pos(), + fa!( + "arbitrary comptime types are not supported yet \ (expected '{}' got '{}')", - self.ty_display(ty::Id::TYPE), - self.ty_display(ty) - ), - ); - return None; - } - let ty = self.ty(arg); - self.tys.tmp.args.push(ty); - ty - }; - - self.ci.scope.vars.push(Variable::new( - carg.id, - ty::Id::TYPE, - false, - self.ci.nodes.new_const(ty::Id::TYPE, ty), - &mut self.ci.nodes, - )); - } - - let Some(args) = self.tys.pack_args(arg_base) else { - self.error(pos, "function instance has too many arguments"); - return None; + self.ty_display(ty::Id::TYPE), + self.ty_display(ty) + ), + ); + return None; + } + let ty = self.ty(arg); + self.tys.tmp.args.push(ty); + ty }; - let ret = self.ty_in(file, parent, ret); - self.ci.scope.vars.drain(base..).for_each(|v| v.remove(&mut self.ci.nodes)); + self.ci.scope.vars.push(Variable::new( + carg.id, + ty::Id::TYPE, + false, + self.ci.nodes.new_const(ty::Id::TYPE, ty), + &mut self.ci.nodes, + )); + } - let sym = SymKey::FuncInst(*func, args); - let ct = |ins: &mut TypeIns| { - let fuc = ins.funcs[*func]; - debug_assert!(fuc.comp_state.iter().all(|&s| s == CompState::default())); - ins.funcs - .push(FuncData { base: Some(*func), sig: Some(Sig { args, ret }), ..fuc }) - .into() - }; - let ty::Kind::Func(f) = - self.tys.syms.get_or_insert(sym, &mut self.tys.ins, ct).expand() - else { - unreachable!() - }; - *func = f; + let Some(args) = self.tys.pack_args(arg_base) else { + self.error(pos, "function instance has too many arguments"); + return None; + }; + let ret = self.ty_in(file, parent, ret); - Sig { args, ret } - }) + self.ci.scope.vars.drain(base..).for_each(|v| v.remove(&mut self.ci.nodes)); + + let sym = SymKey::Type(parent, pos, args); + let ct = |ins: &mut TypeIns| { + ins.funcs + .push(FuncData { + file, + parent, + name, + pos, + expr, + sig: Sig { args, ret }, + is_inline, + is_generic: true, + comp_state: Default::default(), + }) + .into() + }; + let ty::Kind::Func(f) = self.tys.syms.get_or_insert(sym, &mut self.tys.ins, ct).expand() + else { + unreachable!() + }; + Some(f) } fn assign_pattern(&mut self, pat: &Expr, mut right: Value) { @@ -5032,8 +5034,8 @@ impl<'a> Codegen<'a> { debug_assert_eq!(func.file, file); let cct = self.ct.active(); debug_assert_eq!(cct, ct); - func.comp_state[cct as usize] = CompState::Compiled; - let sig = func.sig.expect("to emmit only concrete functions"); + func.comp_state[cct as usize] = CompState::Compiled.into(); + let sig = func.sig; let ast = &self.files[file]; let expr = func.expr.get(ast); @@ -5198,7 +5200,7 @@ impl<'a> Codegen<'a> { } fn ty_in(&mut self, file: Module, parent: ty::Id, expr: &Expr) -> ty::Id { - self.parse_ty(TyScope { file, parent, name: None, alloc_const: true }, expr) + self.parse_ty(TyScope { file, parent, alloc_const: true, ..Default::default() }, expr) } fn ty_display(&self, ty: ty::Id) -> ty::Display { @@ -5495,10 +5497,10 @@ impl<'a> Codegen<'a> { let state_slot = self.ct.active() as usize; if let ty::Kind::Func(id) = existing.expand() && let func = &mut self.tys.ins.funcs[id] - && let CompState::Queued(idx) = func.comp_state[state_slot] + && let CompState::Queued(idx) = func.comp_state[state_slot].into() && idx < self.tys.tasks.len() { - func.comp_state[state_slot] = CompState::Queued(self.tys.tasks.len()); + func.comp_state[state_slot] = CompState::Queued(self.tys.tasks.len()).into(); let task = self.tys.tasks[idx].take(); self.tys.tasks.push(task); } @@ -5643,9 +5645,9 @@ impl<'a> Codegen<'a> { { ty } else { - let (is_ct, ty) = left + let ty = left .find_pattern_path(name, right, |right, is_ct| { - let ty = if is_ct && !matches!(right, Expr::Closure { .. }) { + if is_ct && !matches!(right, Expr::Closure { .. }) { self.tys .ins .consts @@ -5653,23 +5655,23 @@ impl<'a> Codegen<'a> { .into() } else { self.parse_ty( - TyScope { file, parent, name: Some(name), alloc_const: true }, + TyScope { + file, + parent, + name: Some(name), + alloc_const: true, + is_ct, + }, right, ) - }; - (is_ct, ty) + } }) .unwrap_or_else(|_| unreachable!()); - if let ty::Kind::Func(f) = ty.expand() - && is_ct - { - self.tys.ins.funcs[f].is_inline = true; - } self.tys.syms.insert(SymKey::Decl(parent, name), ty, &self.tys.ins); ty }; - if let Err(proper_case) = self.tys.case(ty)(f.ident_str(name)) { + if let Err(proper_case) = self.tys.case(ty, self.files)(f.ident_str(name)) { self.warn_low( from_file, pos, @@ -5775,35 +5777,53 @@ impl<'a> Codegen<'a> { |s, base| s.ins.unions.push(UnionData { base, ..Default::default() }), ), Expr::Closure { pos, args, ret, .. } if let Some(name) = sc.name => { - let func = FuncData { - file: sc.file, - parent: sc.parent, - name, - sig: 'b: { - let arg_base = self.tys.tmp.args.len(); - for arg in args { - let sym = parser::find_symbol(&self.files[sc.file].symbols, arg.id); - if sym.flags & idfl::COMPTIME != 0 { - self.tys.tmp.args.truncate(arg_base); - break 'b None; - } - let ty = self.parse_ty(sc.anon(), &arg.ty); - self.tys.tmp.args.push(ty); + let sig = 'b: { + let arg_base = self.tys.tmp.args.len(); + for arg in args { + let sym = parser::find_symbol(&self.files[sc.file].symbols, arg.id); + if sym.flags & idfl::COMPTIME != 0 { + self.tys.tmp.args.truncate(arg_base); + break 'b None; } + let ty = self.parse_ty(sc.anon(), &arg.ty); + self.tys.tmp.args.push(ty); + } - let Some(args) = self.tys.pack_args(arg_base) else { - return self.error_low(sc.file, pos, "function has too many argumnets"); - }; - let ret = self.parse_ty(sc.anon(), ret); + let Some(args) = self.tys.pack_args(arg_base) else { + return self.error_low(sc.file, pos, "function has too many argumnets"); + }; + let ret = self.parse_ty(sc.anon(), ret); - Some(Sig { args, ret }) - }, - expr: ExprRef::new(expr), - returns_type: matches!(ret, &Expr::Ident { id, .. } if ty::Builtin::try_from(id) == Ok(ty::Builtin::TYPE)), - ..Default::default() + Some(Sig { args, ret }) }; + //let returns_type = matches!(ret, &Expr::Ident { id, .. } if ); - self.tys.ins.funcs.push(func).into() + match sig { + Some(sig) => { + let func = FuncData { + file: sc.file, + parent: sc.parent, + name, + pos, + sig, + expr: ExprRef::new(expr), + is_inline: sc.is_ct, + is_generic: false, + comp_state: Default::default(), + }; + self.tys.ins.funcs.push(func).into() + } + None => { + let template = TemplateData { + file: sc.file, + parent: sc.parent, + name, + expr: ExprRef::new(expr), + is_inline: sc.is_ct, + }; + self.tys.ins.templates.push(template).into() + } + } } _ if sc.alloc_const && let Some(name) = sc.name => @@ -5839,7 +5859,7 @@ impl<'a> Codegen<'a> { } let captured = self.tys.pack_args(captures_start).expect("TODO"); - let sym = SymKey::Type(sc.file, pos, captured); + let sym = SymKey::Type(sc.parent, pos, captured); if let Some(&ty) = self.tys.syms.get(sym, &self.tys.ins) { return ty; } @@ -5871,17 +5891,18 @@ impl<'a> Codegen<'a> { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] struct TyScope { file: Module, parent: ty::Id, name: Option, alloc_const: bool, + is_ct: bool, } impl TyScope { fn anon(self) -> Self { - Self { name: None, ..self } + Self { name: None, is_ct: false, ..self } } } diff --git a/lang/src/ty.rs b/lang/src/ty.rs index e382d20b..e2a08784 100644 --- a/lang/src/ty.rs +++ b/lang/src/ty.rs @@ -119,29 +119,32 @@ impl crate::ctx_map::CtxEntry for Id { Kind::Struct(s) => { let st = &ctx.structs[s]; debug_assert_ne!(st.pos, Pos::MAX); - SymKey::Type(st.file, st.pos, st.captured) + SymKey::Type(st.parent, st.pos, st.captured) } Kind::Enum(e) => { let en = &ctx.enums[e]; debug_assert_ne!(en.pos, Pos::MAX); - SymKey::Type(en.file, en.pos, en.captured) + SymKey::Type(en.parent, en.pos, en.captured) } Kind::Union(e) => { let en = &ctx.unions[e]; debug_assert_ne!(en.pos, Pos::MAX); - SymKey::Type(en.file, en.pos, en.captured) + SymKey::Type(en.parent, en.pos, en.captured) } Kind::Ptr(p) => SymKey::Pointer(&ctx.ptrs[p]), Kind::Opt(p) => SymKey::Optional(&ctx.opts[p]), Kind::Func(f) => { let fc = &ctx.funcs[f]; - if let Some(base) = fc.base { - // TODO: merge base and sig - SymKey::FuncInst(base, fc.sig.unwrap().args) + if fc.is_generic { + SymKey::Type(fc.parent, fc.pos, fc.sig.args) } else { SymKey::Decl(fc.parent, fc.name) } } + Kind::Template(t) => { + let tc = &ctx.templates[t]; + SymKey::Decl(tc.parent, tc.name) + } Kind::Global(g) => { let gb = &ctx.globals[g]; SymKey::Decl(gb.file.into(), gb.name) @@ -271,7 +274,11 @@ impl Id { Kind::Ptr(_) | Kind::Enum(_) | Kind::Builtin(_) => Loc::Reg, Kind::Struct(_) | Kind::Union(_) if tys.size_of(*self) == 0 => Loc::Reg, Kind::Struct(_) | Kind::Union(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack, - c @ (Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_)) => { + c @ (Kind::Func(_) + | Kind::Global(_) + | Kind::Module(_) + | Kind::Const(_) + | Kind::Template(_)) => { unreachable!("{c:?}") } } @@ -439,6 +446,7 @@ type_kind! { Slice, Opt, Func, + Template, Global, Module, Const, @@ -568,6 +576,10 @@ impl core::fmt::Display for Display<'_> { f.write_str("fn")?; idx.fmt(f) } + TK::Template(idx) => { + f.write_str("fn")?; + idx.fmt(f) + } TK::Global(idx) => { let global = &self.tys.ins.globals[idx]; let file = &self.files[global.file]; @@ -598,30 +610,71 @@ impl core::fmt::Display for Display<'_> { pub enum SymKey<'a> { Pointer(&'a PtrData), Optional(&'a OptData), - Type(Module, Pos, Tuple), - FuncInst(Func, Tuple), + Type(Id, Pos, Tuple), Decl(Id, Ident), Array(&'a ArrayData), Constant(&'a ConstData), } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct Sig { pub args: Tuple, pub ret: Id, } +pub struct TemplateData { + pub file: Module, + pub parent: Id, + pub name: Ident, + pub expr: ExprRef, + pub is_inline: bool, +} + #[derive(Default, Clone, Copy)] pub struct FuncData { pub file: Module, pub parent: Id, pub name: Ident, - pub base: Option, + pub pos: Pos, pub expr: ExprRef, - pub sig: Option, + pub sig: Sig, pub is_inline: bool, - pub returns_type: bool, - pub comp_state: [CompState; 2], + pub is_generic: bool, + pub comp_state: [PackedCompState; 2], +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct PackedCompState(u16); + +impl Default for PackedCompState { + fn default() -> Self { + CompState::default().into() + } +} + +impl PackedCompState { + const COMPILED: u16 = u16::MAX - 1; + const DEAD: u16 = u16::MAX; +} + +impl From for CompState { + fn from(value: PackedCompState) -> Self { + match value.0 { + PackedCompState::DEAD => CompState::Dead, + PackedCompState::COMPILED => CompState::Compiled, + v => CompState::Queued(v as _), + } + } +} + +impl From for PackedCompState { + fn from(value: CompState) -> Self { + Self(match value { + CompState::Dead => Self::DEAD, + CompState::Queued(v) => v.try_into().unwrap(), + CompState::Compiled => Self::COMPILED, + }) + } } #[derive(Default, PartialEq, Eq, Clone, Copy)] @@ -774,6 +827,7 @@ pub struct TypeIns { pub struct_fields: Vec, pub enum_fields: Vec, pub funcs: EntVec, + pub templates: EntVec, pub globals: EntVec, pub consts: EntVec, pub structs: EntVec, @@ -812,7 +866,11 @@ pub struct Types { } impl Types { - pub fn case(&self, ty: Id) -> fn(&str) -> Result<(), &'static str> { + pub fn case( + &self, + ty: Id, + files: &EntSlice, + ) -> fn(&str) -> Result<(), &'static str> { match ty.expand() { Kind::NEVER => |_| Ok(()), Kind::Enum(_) @@ -822,8 +880,23 @@ impl Types { | Kind::Ptr(_) | Kind::Slice(_) | Kind::Opt(_) => utils::is_pascal_case, - Kind::Func(f) if self.ins.funcs[f].returns_type => utils::is_pascal_case, - Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => utils::is_snake_case, + Kind::Func(f) + if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } = + self.ins.funcs[f].expr.get(&files[self.ins.funcs[f].file]) + && id.is_type() => + { + utils::is_pascal_case + } + Kind::Template(f) + if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } = + self.ins.templates[f].expr.get(&files[self.ins.templates[f].file]) + && id.is_type() => + { + utils::is_pascal_case + } + Kind::Func(_) | Kind::Template(_) | Kind::Global(_) | Kind::Module(_) => { + utils::is_snake_case + } Kind::Const(_) => utils::is_screaming_case, } } @@ -1063,6 +1136,7 @@ impl Types { | Kind::Slice(_) | Kind::Opt(_) | Kind::Func(_) + | Kind::Template(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => return None,