From e9335d3fc573b843d3994a6db96778f3445d6f9d Mon Sep 17 00:00:00 2001 From: Janis Date: Fri, 30 Aug 2024 17:34:26 +0200 Subject: [PATCH] something is wrong, but typechecking --- src/ast.rs | 9 +- src/comptime.rs | 14 +++ src/mir.rs | 4 - src/parser.rs | 172 ++++++++++++++++++++++++++++------- src/triples.rs | 64 +++++++++++-- tests/legal/inverse_sqrt.sea | 2 +- 6 files changed, 212 insertions(+), 53 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index a7f6ea3..d181b60 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -241,7 +241,7 @@ impl IntegralType { } } -#[derive(Debug, Clone, Eq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum Type { Any, Void, @@ -289,8 +289,8 @@ impl core::fmt::Display for Type { } } -impl PartialEq for Type { - fn eq(&self, other: &Self) -> bool { +impl Type { + pub fn is_compatible_with(&self, other: &Self) -> bool { match (self, other) { (Self::ComptimeNumber, Self::Integer(_)) => true, (Self::Integer(_), Self::ComptimeNumber) => true, @@ -319,9 +319,6 @@ impl PartialEq for Type { _ => core::mem::discriminant(self) == core::mem::discriminant(other), } } -} - -impl Type { pub fn as_primitive_type(&self) -> Option { match self { Type::Void => Some(PrimitiveType::Void), diff --git a/src/comptime.rs b/src/comptime.rs index f229ec4..51419cb 100644 --- a/src/comptime.rs +++ b/src/comptime.rs @@ -2009,6 +2009,20 @@ impl From<(u128, IntegralType)> for ComptimeNumber { } impl ComptimeNumber { + pub fn bit_count(&self) -> u16 { + match self { + ComptimeNumber::Integral(i) => match i { + ComptimeInt::Native { ty, .. } => ty.bits, + ComptimeInt::BigInt { ty, .. } => ty.bits, + ComptimeInt::Comptime(i) => i.bits() as u16, + }, + ComptimeNumber::Bool(_) => 1, + ComptimeNumber::Floating(f) => match f { + ComptimeFloat::Binary32(_) => 32, + ComptimeFloat::Binary64(_) => 64, + }, + } + } pub fn add(self, other: Self) -> Result { match (self, other) { (ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => { diff --git a/src/mir.rs b/src/mir.rs index 5d950c0..8e06108 100644 --- a/src/mir.rs +++ b/src/mir.rs @@ -1305,16 +1305,12 @@ pub mod liveness { interval.map(|max| (node, max)) }); - eprintln!("intervals: ["); - let mut edges = HashSet::<(u32, u32)>::new(); for (from, to) in intervals { - eprint!("({from}..{to}), "); for &(other, _) in references.range(from.exclusive_start()..to.inclusive_end()) { edges.insert((from.0, other.0)); } } - eprintln!("]"); let inference_graph = petgraph::graph::UnGraph::<(), ()>::from_edges(edges.into_iter()); diff --git a/src/parser.rs b/src/parser.rs index 8c8a1fe..333d080 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,6 +12,7 @@ use crate::{ string_table::{ImmOrIndex, Index, StringTable}, symbol_table::{SymbolKind, SymbolTable}, tokens::Token, + variant, }; #[derive(Debug, thiserror::Error)] @@ -1498,7 +1499,7 @@ impl Tree { writeln_indented!( indent, writer, - "%{} = store(dst: %{}, val: %{})", + "%{} = store (dst: %{}, val: %{})", node.get(), lhs.get(), rhs.get() @@ -1560,6 +1561,28 @@ impl Tree { Ok(()) } + pub fn peer_type_of_nodes(&self, lhs: Node, rhs: Node) -> Option { + let lty = self.type_of_node(lhs); + let rty = self.type_of_node(rhs); + + let peer = lty.equal_type(&rty)?; + + if lty == Type::ComptimeNumber { + let value = self.value_of_comptime_node(lhs)?; + if value.bit_count() > rty.bit_width() { + panic!("comptime number is incompatible with type {rty}!"); + } + } + if rty == Type::ComptimeNumber { + let value = self.value_of_comptime_node(rhs)?; + if value.bit_count() > lty.bit_width() { + panic!("comptime number is incompatible with type {lty}!"); + } + } + + Some(peer) + } + pub fn type_of_node(&self, node: Node) -> crate::ast::Type { match self.nodes.get_node(node) { Tag::FunctionDecl { proto, .. } => self.type_of_node(*proto), @@ -1600,36 +1623,44 @@ impl Tree { Tag::Block { trailing_expr, .. } => trailing_expr .map(|n| self.type_of_node(n)) .unwrap_or(Type::void()), - Tag::VarDecl { - explicit_type, - assignment, // this is a Tag::Assign - .. - } => { - let lhs = explicit_type.map(|n| self.type_of_node(n)); - let rhs = assignment.map(|n| match self.nodes.get_node(n) { - Tag::Assign { rhs, .. } => self.type_of_node(*rhs), - _ => unreachable!(), - }); - if lhs.as_ref().zip(rhs.as_ref()).map(|(l, r)| l != r) == Some(true) { - eprintln!("vardecl: incompatible types {lhs:?} and {rhs:?}."); - } - lhs.or(rhs) - .expect("Type could not be automatically deduced.") - } Tag::GlobalDecl { explicit_type, assignment, // this is a Tag::Assign .. } => { - let lhs = explicit_type.map(|n| self.type_of_node(n)); - let rhs = match self.nodes.get_node(*assignment) { - Tag::Assign { rhs, .. } => self.type_of_node(*rhs), - _ => unreachable!(), + variant!(self.nodes.get_node(*assignment) => Tag::Assign { rhs ,..}); + let ty = match (explicit_type.as_ref(), rhs) { + (None, b) => self.type_of_node(*b), + (Some(a), b) => self.peer_type_of_nodes(*a, *b).expect({ + let at = self.type_of_node(*a); + let bt = self.type_of_node(*b); + &format!("incompatible types for %{a}({at}) and %{b}({bt})") + }), }; - if lhs.as_ref().zip(Some(&rhs)).map(|(l, r)| l != r) == Some(true) { - eprintln!("vardecl: incompatible types {lhs:?} and {rhs:?}."); - } - lhs.unwrap_or(rhs) + + ty + } + Tag::VarDecl { + explicit_type, + assignment, // this is a Tag::Assign + .. + } => { + let rhs = assignment.map(|n| { + variant!(self.nodes.get_node(n) => Tag::Assign { rhs ,..}); + *rhs + }); + let ty = match (explicit_type.as_ref(), rhs.as_ref()) { + (None, None) => panic!("%{node}: no type specified?"), + (None, Some(b)) => self.type_of_node(*b), + (Some(a), None) => self.type_of_node(*a), + (Some(a), Some(b)) => self.peer_type_of_nodes(*a, *b).expect({ + let at = self.type_of_node(*a); + let bt = self.type_of_node(*b); + &format!("incompatible types for %{a}({at}) and %{b}({bt})") + }), + }; + + ty } Tag::CallExpr { lhs, .. } => self.type_of_node(*lhs), Tag::ExplicitCast { typename, .. } => self.type_of_node(*typename), @@ -1637,18 +1668,23 @@ impl Tree { Tag::Ref { lhs } => self.type_of_node(*lhs).into_ptr(), Tag::Not { lhs } => self.type_of_node(*lhs), Tag::Negate { lhs } => self.type_of_node(*lhs), - Tag::Or { lhs, .. } => self.type_of_node(*lhs), - Tag::And { lhs, .. } => self.type_of_node(*lhs), - Tag::BitOr { lhs, .. } => self.type_of_node(*lhs), - Tag::BitAnd { lhs, .. } => self.type_of_node(*lhs), - Tag::BitXOr { lhs, .. } => self.type_of_node(*lhs), + Tag::Assign { lhs, rhs } + | Tag::Add { lhs, rhs } + | Tag::Sub { lhs, rhs } + | Tag::Mul { lhs, rhs } + | Tag::Rem { lhs, rhs } + | Tag::Div { lhs, rhs } + | Tag::Or { lhs, rhs } + | Tag::And { lhs, rhs } + | Tag::BitOr { lhs, rhs } + | Tag::BitAnd { lhs, rhs } + | Tag::BitXOr { lhs, rhs } => self.peer_type_of_nodes(*lhs, *rhs).expect({ + let at = self.type_of_node(*lhs); + let bt = self.type_of_node(*rhs); + &format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})") + }), Tag::Shl { lhs, .. } => self.type_of_node(*lhs), Tag::Shr { lhs, .. } => self.type_of_node(*lhs), - Tag::Add { lhs, .. } => self.type_of_node(*lhs), - Tag::Sub { lhs, .. } => self.type_of_node(*lhs), - Tag::Mul { lhs, .. } => self.type_of_node(*lhs), - Tag::Rem { lhs, .. } => self.type_of_node(*lhs), - Tag::Div { lhs, .. } => self.type_of_node(*lhs), Tag::Eq { .. } => Type::bool(), Tag::NEq { .. } => Type::bool(), Tag::Lt { .. } => Type::bool(), @@ -1736,6 +1772,72 @@ impl Tree { } } + pub fn value_of_comptime_node(&self, node: Node) -> Option { + match self.nodes.get_node(node) { + Tag::Constant { bytes, ty } => { + let bytes = match bytes { + ImmOrIndex::U64(v) => &v.to_le_bytes()[..], + + ImmOrIndex::U32(v) => &v.to_le_bytes()[..], + ImmOrIndex::Index(idx) => self.strings.get_bytes(*idx), + }; + + let number: ComptimeNumber = match ty { + Type::Bool => (bytes[0] != 0).into(), + Type::ComptimeNumber => { + BigInt::from_bytes_le(num_bigint::Sign::Plus, bytes).into() + } + Type::Integer(ty) => { + if bytes.len() > core::mem::size_of::() { + let bits = BigInt::from_bytes_le(num_bigint::Sign::Plus, bytes); + (bits, *ty).into() + } else { + let mut buf = [0u8; core::mem::size_of::()]; + buf[..bytes.len()].copy_from_slice(bytes); + let bits = u128::from_le_bytes(buf); + (bits, *ty).into() + } + } + Type::Floating(ty) => match ty { + FloatingType::Binary32 => { + (f32::from_le_bytes((&bytes[..4]).try_into().unwrap())).into() + } + FloatingType::Binary64 => { + (f64::from_le_bytes((&bytes[..8]).try_into().unwrap())).into() + } + }, + _ => unimplemented!(), + }; + Some(number) + } + Tag::Block { .. } => todo!(), + &Tag::DeclRef(lhs) => { + let start = lhs; + let end = node; + let mut last_value = None; + + ast::tree_visitor::Visitor::new_seek( + self,start, + |_: &Tree, _| { + }, + |tree: &Tree, node| match tree.nodes.get_node(node) { + &Tag::Assign { lhs, rhs } => { + if lhs == start || matches!(tree.nodes.get_node(lhs), &Tag::DeclRef(decl) if decl == start) { + last_value = Some(rhs); + } + } + _ => {} + }, + ) + .until_after(end) + .visit(self); + + self.value_of_comptime_node(last_value?) + } + _ => None, + } + } + fn fold_comptime_with_visitor(&mut self, decl: Node) { ast::tree_visitor::Visitor::new( decl, diff --git a/src/triples.rs b/src/triples.rs index 5a13177..0c2adce 100644 --- a/src/triples.rs +++ b/src/triples.rs @@ -260,6 +260,35 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { } } + fn peer_type_of_nodes(&self, lhs: AstNode, rhs: AstNode) -> Option { + let lty = self.tree.type_of_node(lhs); + let rty = self.tree.type_of_node(rhs); + + let peer = lty + .equal_type(&rty) + .expect(&format!("types {lty} and {rty} are incompatible!")); + + if lty == Type::ComptimeNumber { + eprintln!( + "%{lhs} is comptime number: {:?}", + self.tree.nodes.get_node(lhs) + ); + let value = self.tree.value_of_comptime_node(lhs)?; + if value.bit_count() > rty.bit_width() { + panic!("comptime number is incompatible with type {rty}!"); + } + } + if rty == Type::ComptimeNumber { + eprintln!("%{rhs} is comptime number"); + let value = self.tree.value_of_comptime_node(rhs)?; + if value.bit_count() > lty.bit_width() { + panic!("comptime number is incompatible with type {lty}!"); + } + } + + Some(peer) + } + fn visit(&mut self, node: AstNode) -> Node { match &self.tree.nodes[node].clone() { Tag::FunctionDecl { proto, body } => { @@ -353,7 +382,7 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { self.ir.push(Inst::Load(ty.into()), Some(Data::lhs(lhs))) } Tag::Assign { lhs, rhs } => { - let ty = self.tree.type_of_node(*rhs); + let ty = self.tree.type_of_node(node); let dest = self.visit(*lhs); let source = self.visit(*rhs); @@ -361,40 +390,61 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { .push(Inst::Store(ty.into()), Some(Data::new(source, dest))) } Tag::Add { lhs, rhs } => { - let ty = self.tree.type_of_node(*lhs); + let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::Add(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Sub { lhs, rhs } => { - let ty = self.tree.type_of_node(*lhs); + let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::Sub(ty.into()), Some(Data::new(lhs, rhs))) } + Tag::BitAnd { lhs, rhs } => { + let ty = self.tree.type_of_node(node); + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir + .push(Inst::BitAnd(ty.into()), Some(Data::new(lhs, rhs))) + } + Tag::BitOr { lhs, rhs } => { + let ty = self.tree.type_of_node(node); + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir + .push(Inst::BitOr(ty.into()), Some(Data::new(lhs, rhs))) + } + Tag::BitXOr { lhs, rhs } => { + let ty = self.tree.type_of_node(node); + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir + .push(Inst::BitXOr(ty.into()), Some(Data::new(lhs, rhs))) + } Tag::Mul { lhs, rhs } => { - let ty = self.tree.type_of_node(*lhs); + let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::Mul(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Negate { lhs } => { - let ty = self.tree.type_of_node(*lhs); + let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); self.ir.push(Inst::Negate(ty.into()), Some(Data::lhs(lhs))) } Tag::Shl { lhs, rhs } => { - let ty = self.tree.type_of_node(*lhs); + let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::ShiftLeft(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Shr { lhs, rhs } => { - let ty = self.tree.type_of_node(*lhs); + let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir diff --git a/tests/legal/inverse_sqrt.sea b/tests/legal/inverse_sqrt.sea index eba5b71..a1cedb7 100644 --- a/tests/legal/inverse_sqrt.sea +++ b/tests/legal/inverse_sqrt.sea @@ -1,7 +1,7 @@ fn inverse_sqrt(n: f32) -> f32 { let x = n; var i = *(&x as *i32); - i = 0x5f3759dfi32 - (i >> 1); + i = 0x5f3759df - (i >> 1); let y = *(&i as *f32); y * (1.5f32 - (x * 0.5f32 * y * y)) } \ No newline at end of file