typecheck if and while conditions, disallow ptr multiplication

This commit is contained in:
2026-03-18 11:34:25 +01:00
parent 3efb3820bc
commit 2137d49abd
5 changed files with 73 additions and 28 deletions

View File

@@ -120,6 +120,7 @@ impl Analyzer {
} }
} }
Stmt::If { Stmt::If {
keyword: _,
condition, condition,
then_branch, then_branch,
else_branch, else_branch,
@@ -128,7 +129,11 @@ impl Analyzer {
self.analyze_stmt(then_branch)?; self.analyze_stmt(then_branch)?;
self.analyze_stmt(else_branch)?; self.analyze_stmt(else_branch)?;
} }
Stmt::While { condition, body } => { Stmt::While {
keyword: _,
condition,
body,
} => {
self.analyze_expr(condition)?; self.analyze_expr(condition)?;
self.analyze_stmt(body)?; self.analyze_stmt(body)?;
} }

View File

@@ -260,6 +260,7 @@ _builtin_environ:
env.pop_scope(); env.pop_scope();
} }
Stmt::If { Stmt::If {
keyword: _,
condition, condition,
then_branch, then_branch,
else_branch, else_branch,
@@ -276,7 +277,11 @@ _builtin_environ:
self.compile_stmt(env, else_branch)?; self.compile_stmt(env, else_branch)?;
emit!(&mut self.output, "{}:", end_label); emit!(&mut self.output, "{}:", end_label);
} }
Stmt::While { condition, body } => { Stmt::While {
keyword: _,
condition,
body,
} => {
let old_loop_begin_label = env.loop_begin_label.clone(); let old_loop_begin_label = env.loop_begin_label.clone();
let old_loop_end_label = env.loop_end_label.clone(); let old_loop_end_label = env.loop_end_label.clone();
let old_loop_continue_label = env.loop_continue_label.clone(); let old_loop_continue_label = env.loop_continue_label.clone();

View File

@@ -20,11 +20,13 @@ pub enum Stmt {
}, },
Block(Vec<Stmt>), Block(Vec<Stmt>),
If { If {
keyword: Token,
condition: Expr, condition: Expr,
then_branch: Box<Stmt>, then_branch: Box<Stmt>,
else_branch: Box<Stmt>, else_branch: Box<Stmt>,
}, },
While { While {
keyword: Token,
condition: Expr, condition: Expr,
body: Box<Stmt>, body: Box<Stmt>,
}, },
@@ -280,6 +282,7 @@ impl Parser {
} }
fn if_statement(&mut self) -> Result<Stmt, ZernError> { fn if_statement(&mut self) -> Result<Stmt, ZernError> {
let keyword = self.previous().clone();
let condition = self.expression()?; let condition = self.expression()?;
let then_branch = self.block()?; let then_branch = self.block()?;
let else_branch = if self.match_token(&[TokenType::KeywordElse]) { let else_branch = if self.match_token(&[TokenType::KeywordElse]) {
@@ -292,6 +295,7 @@ impl Parser {
Box::new(Stmt::Block(vec![])) Box::new(Stmt::Block(vec![]))
}; };
Ok(Stmt::If { Ok(Stmt::If {
keyword,
condition, condition,
then_branch: Box::new(then_branch), then_branch: Box::new(then_branch),
else_branch, else_branch,
@@ -299,9 +303,11 @@ impl Parser {
} }
fn while_statement(&mut self) -> Result<Stmt, ZernError> { fn while_statement(&mut self) -> Result<Stmt, ZernError> {
let keyword = self.previous().clone();
let condition = self.expression()?; let condition = self.expression()?;
let body = self.block()?; let body = self.block()?;
Ok(Stmt::While { Ok(Stmt::While {
keyword,
condition, condition,
body: Box::new(body), body: Box::new(body),
}) })

View File

@@ -53,7 +53,7 @@ func mem._split_block[blk: mem.Block, needed: i64] : void
new_blk->next = blk->next new_blk->next = blk->next
new_blk->prev = blk new_blk->prev = blk
if blk->next if blk->next as ptr
let next: mem.Block = blk->next let next: mem.Block = blk->next
next->prev = new_blk next->prev = new_blk
else else
@@ -83,7 +83,7 @@ func mem._request_space?[size: i64] : mem.Block
blk->prev = 0 as mem.Block blk->prev = 0 as mem.Block
let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block
if tail if tail as ptr
tail->next = blk tail->next = blk
blk->prev = tail blk->prev = tail
@@ -99,7 +99,7 @@ func mem.alloc?[size: i64] : any
size = mem._align(size) size = mem._align(size)
let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block
while cur while cur as ptr
if cur->free && cur->size >= size if cur->free && cur->size >= size
mem._split_block(cur, size) mem._split_block(cur, size)
cur->free = false cur->free = false
@@ -133,7 +133,7 @@ func mem.free[x: any] : void
if expected_next as ptr == next as ptr if expected_next as ptr == next as ptr
blk->size = blk->size + MEM_BLOCK_SIZE + next->size blk->size = blk->size + MEM_BLOCK_SIZE + next->size
blk->next = next->next blk->next = next->next
if next->next if next->next as ptr
let b: mem.Block = next->next let b: mem.Block = next->next
b->prev = blk b->prev = blk
if mem.read64(_builtin_heap_tail()) == next as ptr if mem.read64(_builtin_heap_tail()) == next as ptr
@@ -145,7 +145,7 @@ func mem.free[x: any] : void
if expected_blk as ptr == blk as ptr if expected_blk as ptr == blk as ptr
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
prev->next = blk->next prev->next = blk->next
if blk->next if blk->next as ptr
let b: mem.Block = blk->next let b: mem.Block = blk->next
b->prev = prev b->prev = prev
if mem.read64(_builtin_heap_tail()) == blk as ptr if mem.read64(_builtin_heap_tail()) == blk as ptr
@@ -154,12 +154,12 @@ func mem.free[x: any] : void
let block_total: i64 = blk->size + MEM_BLOCK_SIZE let block_total: i64 = blk->size + MEM_BLOCK_SIZE
if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0 if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0
if blk->prev if blk->prev as ptr
let b: mem.Block = blk->prev let b: mem.Block = blk->prev
b->next = blk->next b->next = blk->next
else else
mem.write64(_builtin_heap_head(), blk->next) mem.write64(_builtin_heap_head(), blk->next)
if blk->next if blk->next as ptr
let b: mem.Block = blk->next let b: mem.Block = blk->next
b->prev = blk->prev b->prev = blk->prev
else else
@@ -194,7 +194,7 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
if combined >= new_size if combined >= new_size
blk->size = combined blk->size = combined
blk->next = next->next blk->next = next->next
if next->next if next->next as ptr
let b: mem.Block = next->next let b: mem.Block = next->next
b->prev = blk b->prev = blk
if mem.read64(_builtin_heap_tail()) == next as ptr if mem.read64(_builtin_heap_tail()) == next as ptr
@@ -771,7 +771,7 @@ struct Array
func array.new[] : Array func array.new[] : Array
return new Array return new Array
func array.new_with_size_zeroed[size: i64] : Array func array.new_preallocated_and_zeroed[size: i64] : Array
let xs: Array = new Array let xs: Array = new Array
if size > 0 if size > 0
xs->data = must(mem.realloc?(xs->data, size * 8)) as ptr xs->data = must(mem.realloc?(xs->data, size * 8)) as ptr
@@ -817,13 +817,13 @@ func array.slice[xs: Array, start: i64, length: i64] : Array
if start < 0 || length < 0 || start + length > xs->size if start < 0 || length < 0 || start + length > xs->size
panic("array.slice out of bounds") panic("array.slice out of bounds")
let new_array: Array = array.new_with_size_zeroed(length) let new_array: Array = array.new_preallocated_and_zeroed(length)
for i in 0..length for i in 0..length
array.set(new_array, i, array.nth(xs, start + i)) array.set(new_array, i, array.nth(xs, start + i))
return new_array return new_array
func array.concat[a: Array, b: Array] : Array func array.concat[a: Array, b: Array] : Array
let new_array: Array = array.new_with_size_zeroed(a->size + b->size) let new_array: Array = array.new_preallocated_and_zeroed(a->size + b->size)
for i in 0..a->size for i in 0..a->size
array.set(new_array, i, array.nth(a, i)) array.set(new_array, i, array.nth(a, i))
for i in 0..b->size for i in 0..b->size
@@ -861,7 +861,7 @@ func alg.count[arr: Array, item: any] : i64
return count return count
func alg.map[arr: Array, fn: fnptr] : Array func alg.map[arr: Array, fn: fnptr] : Array
let out: Array = array.new_with_size_zeroed(arr->size) let out: Array = array.new_preallocated_and_zeroed(arr->size)
for i in 0..arr->size for i in 0..arr->size
array.set(out, i, fn(array.nth(arr, i))) array.set(out, i, fn(array.nth(arr, i)))
return out return out

View File

@@ -51,6 +51,7 @@ impl Env {
} }
pub fn pop_scope(&mut self) { pub fn pop_scope(&mut self) {
assert!(!self.scopes.is_empty());
self.scopes.pop(); self.scopes.pop();
} }
@@ -119,15 +120,29 @@ impl TypeChecker {
env.pop_scope(); env.pop_scope();
} }
Stmt::If { Stmt::If {
keyword,
condition, condition,
then_branch, then_branch,
else_branch, else_branch,
} => { } => {
self.typecheck_expr(env, condition)?; expect_types!(
self.typecheck_expr(env, condition)?,
["i64", "u8", "ptr", "bool"],
keyword.loc
);
self.typecheck_stmt(env, then_branch)?; self.typecheck_stmt(env, then_branch)?;
self.typecheck_stmt(env, else_branch)?; self.typecheck_stmt(env, else_branch)?;
} }
Stmt::While { condition, body } => { Stmt::While {
keyword,
condition,
body,
} => {
expect_types!(
self.typecheck_expr(env, condition)?,
["i64", "u8", "ptr", "bool"],
keyword.loc
);
self.typecheck_expr(env, condition)?; self.typecheck_expr(env, condition)?;
self.typecheck_stmt(env, body)?; self.typecheck_stmt(env, body)?;
} }
@@ -202,30 +217,43 @@ impl TypeChecker {
match expr { match expr {
Expr::Binary { left, op, right } => { Expr::Binary { left, op, right } => {
let left_type = self.typecheck_expr(env, left)?; let left_type = self.typecheck_expr(env, left)?;
match op.token_type {
TokenType::Plus | TokenType::Minus => {
expect_types!(left_type, ["i64", "ptr", "u8"], op.loc); expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
expect_types!( expect_types!(
self.typecheck_expr(env, right)?, self.typecheck_expr(env, right)?,
["i64", "ptr", "u8"], ["i64", "ptr", "u8"],
op.loc op.loc
); );
Ok(left_type)
match op.token_type { }
TokenType::Plus TokenType::Star
| TokenType::Minus
| TokenType::Star
| TokenType::Slash | TokenType::Slash
| TokenType::Mod | TokenType::Mod
| TokenType::Xor | TokenType::Xor
| TokenType::BitAnd | TokenType::BitAnd
| TokenType::BitOr | TokenType::BitOr
| TokenType::ShiftLeft | TokenType::ShiftLeft
| TokenType::ShiftRight => Ok(left_type), | TokenType::ShiftRight => {
expect_types!(left_type, ["i64", "u8"], op.loc);
expect_types!(self.typecheck_expr(env, right)?, ["i64", "u8"], op.loc);
Ok(left_type)
}
TokenType::DoubleEqual TokenType::DoubleEqual
| TokenType::NotEqual | TokenType::NotEqual
| TokenType::Greater | TokenType::Greater
| TokenType::GreaterEqual | TokenType::GreaterEqual
| TokenType::Less | TokenType::Less
| TokenType::LessEqual => Ok("bool".into()), | TokenType::LessEqual => {
expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
expect_types!(
self.typecheck_expr(env, right)?,
["i64", "ptr", "u8"],
op.loc
);
Ok("bool".into())
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
@@ -347,6 +375,7 @@ impl TypeChecker {
// its a function (defined/builtin/extern) // its a function (defined/builtin/extern)
if let Some(params) = fn_type.params.clone() { if let Some(params) = fn_type.params.clone() {
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
// arity is checked in the analyzer
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc); expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
} }
} else { } else {
@@ -390,7 +419,7 @@ impl TypeChecker {
expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc); expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc);
Ok("u8".into()) Ok("u8".into())
} }
Expr::AddrOf { op, expr } => match *expr.clone() { Expr::AddrOf { op, expr } => match expr.as_ref() {
Expr::Variable(name) => { Expr::Variable(name) => {
if self.analyzer.borrow().functions.contains_key(&name.lexeme) { if self.analyzer.borrow().functions.contains_key(&name.lexeme) {
Ok("fnptr".into()) Ok("fnptr".into())