something is wrong, but typechecking

This commit is contained in:
Janis 2024-08-30 17:34:26 +02:00
parent 70644ede3e
commit e9335d3fc5
6 changed files with 212 additions and 53 deletions

View file

@ -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<PrimitiveType> {
match self {
Type::Void => Some(PrimitiveType::Void),

View file

@ -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<Self> {
match (self, other) {
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => {

View file

@ -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());

View file

@ -12,6 +12,7 @@ use crate::{
string_table::{ImmOrIndex, Index, StringTable},
symbol_table::{SymbolKind, SymbolTable},
tokens::Token,
variant,
};
#[derive(Debug, thiserror::Error)]
@ -1560,6 +1561,28 @@ impl Tree {
Ok(())
}
pub fn peer_type_of_nodes(&self, lhs: Node, rhs: Node) -> Option<Type> {
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:?}.");
ty
}
lhs.unwrap_or(rhs)
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<ComptimeNumber> {
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::<u128>() {
let bits = BigInt::from_bytes_le(num_bigint::Sign::Plus, bytes);
(bits, *ty).into()
} else {
let mut buf = [0u8; core::mem::size_of::<u128>()];
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,

View file

@ -260,6 +260,35 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
}
}
fn peer_type_of_nodes(&self, lhs: AstNode, rhs: AstNode) -> Option<Type> {
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

View file

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