From 2137d49abdb00e9cb0f2b0d35631513ad73cde91 Mon Sep 17 00:00:00 2001 From: Toni Date: Wed, 18 Mar 2026 11:34:25 +0100 Subject: [PATCH] typecheck if and while conditions, disallow ptr multiplication --- src/analyzer.rs | 7 +++++- src/codegen_x86_64.rs | 7 +++++- src/parser.rs | 6 +++++ src/std/std.zr | 24 +++++++++--------- src/typechecker.rs | 57 ++++++++++++++++++++++++++++++++----------- 5 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/analyzer.rs b/src/analyzer.rs index 113b0d8..e218125 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -120,6 +120,7 @@ impl Analyzer { } } Stmt::If { + keyword: _, condition, then_branch, else_branch, @@ -128,7 +129,11 @@ impl Analyzer { self.analyze_stmt(then_branch)?; self.analyze_stmt(else_branch)?; } - Stmt::While { condition, body } => { + Stmt::While { + keyword: _, + condition, + body, + } => { self.analyze_expr(condition)?; self.analyze_stmt(body)?; } diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 3f0a3be..46fbc78 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -260,6 +260,7 @@ _builtin_environ: env.pop_scope(); } Stmt::If { + keyword: _, condition, then_branch, else_branch, @@ -276,7 +277,11 @@ _builtin_environ: self.compile_stmt(env, else_branch)?; 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_end_label = env.loop_end_label.clone(); let old_loop_continue_label = env.loop_continue_label.clone(); diff --git a/src/parser.rs b/src/parser.rs index a49f53d..142f52c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -20,11 +20,13 @@ pub enum Stmt { }, Block(Vec), If { + keyword: Token, condition: Expr, then_branch: Box, else_branch: Box, }, While { + keyword: Token, condition: Expr, body: Box, }, @@ -280,6 +282,7 @@ impl Parser { } fn if_statement(&mut self) -> Result { + let keyword = self.previous().clone(); let condition = self.expression()?; let then_branch = self.block()?; let else_branch = if self.match_token(&[TokenType::KeywordElse]) { @@ -292,6 +295,7 @@ impl Parser { Box::new(Stmt::Block(vec![])) }; Ok(Stmt::If { + keyword, condition, then_branch: Box::new(then_branch), else_branch, @@ -299,9 +303,11 @@ impl Parser { } fn while_statement(&mut self) -> Result { + let keyword = self.previous().clone(); let condition = self.expression()?; let body = self.block()?; Ok(Stmt::While { + keyword, condition, body: Box::new(body), }) diff --git a/src/std/std.zr b/src/std/std.zr index 36faf2f..45651f9 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -53,7 +53,7 @@ func mem._split_block[blk: mem.Block, needed: i64] : void new_blk->next = blk->next new_blk->prev = blk - if blk->next + if blk->next as ptr let next: mem.Block = blk->next next->prev = new_blk else @@ -83,7 +83,7 @@ func mem._request_space?[size: i64] : mem.Block blk->prev = 0 as mem.Block let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block - if tail + if tail as ptr tail->next = blk blk->prev = tail @@ -99,7 +99,7 @@ func mem.alloc?[size: i64] : any size = mem._align(size) let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block - while cur + while cur as ptr if cur->free && cur->size >= size mem._split_block(cur, size) cur->free = false @@ -133,7 +133,7 @@ func mem.free[x: any] : void if expected_next as ptr == next as ptr blk->size = blk->size + MEM_BLOCK_SIZE + next->size blk->next = next->next - if next->next + if next->next as ptr let b: mem.Block = next->next b->prev = blk 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 prev->size = prev->size + MEM_BLOCK_SIZE + blk->size prev->next = blk->next - if blk->next + if blk->next as ptr let b: mem.Block = blk->next b->prev = prev 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 if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0 - if blk->prev + if blk->prev as ptr let b: mem.Block = blk->prev b->next = blk->next else mem.write64(_builtin_heap_head(), blk->next) - if blk->next + if blk->next as ptr let b: mem.Block = blk->next b->prev = blk->prev else @@ -194,7 +194,7 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr if combined >= new_size blk->size = combined blk->next = next->next - if next->next + if next->next as ptr let b: mem.Block = next->next b->prev = blk if mem.read64(_builtin_heap_tail()) == next as ptr @@ -771,7 +771,7 @@ struct Array func array.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 if size > 0 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 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 array.set(new_array, i, array.nth(xs, start + i)) return new_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 array.set(new_array, i, array.nth(a, i)) for i in 0..b->size @@ -861,7 +861,7 @@ func alg.count[arr: Array, item: any] : i64 return count 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 array.set(out, i, fn(array.nth(arr, i))) return out diff --git a/src/typechecker.rs b/src/typechecker.rs index 2527371..a857c9f 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -51,6 +51,7 @@ impl Env { } pub fn pop_scope(&mut self) { + assert!(!self.scopes.is_empty()); self.scopes.pop(); } @@ -119,15 +120,29 @@ impl TypeChecker { env.pop_scope(); } Stmt::If { + keyword, condition, then_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, 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_stmt(env, body)?; } @@ -202,30 +217,43 @@ impl TypeChecker { match expr { Expr::Binary { left, op, right } => { let left_type = self.typecheck_expr(env, left)?; - expect_types!(left_type, ["i64", "ptr", "u8"], op.loc); - expect_types!( - self.typecheck_expr(env, right)?, - ["i64", "ptr", "u8"], - op.loc - ); match op.token_type { - TokenType::Plus - | TokenType::Minus - | TokenType::Star + TokenType::Plus | TokenType::Minus => { + expect_types!(left_type, ["i64", "ptr", "u8"], op.loc); + expect_types!( + self.typecheck_expr(env, right)?, + ["i64", "ptr", "u8"], + op.loc + ); + Ok(left_type) + } + TokenType::Star | TokenType::Slash | TokenType::Mod | TokenType::Xor | TokenType::BitAnd | TokenType::BitOr | 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::NotEqual | TokenType::Greater | TokenType::GreaterEqual | 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!(), } } @@ -347,6 +375,7 @@ impl TypeChecker { // its a function (defined/builtin/extern) if let Some(params) = fn_type.params.clone() { for (i, arg) in args.iter().enumerate() { + // arity is checked in the analyzer expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc); } } else { @@ -390,7 +419,7 @@ impl TypeChecker { expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc); Ok("u8".into()) } - Expr::AddrOf { op, expr } => match *expr.clone() { + Expr::AddrOf { op, expr } => match expr.as_ref() { Expr::Variable(name) => { if self.analyzer.borrow().functions.contains_key(&name.lexeme) { Ok("fnptr".into())