Compare commits

...

2 Commits

Author SHA1 Message Date
e774f06e65 validate types in New/Struct/Cast 2026-03-18 11:47:29 +01:00
2137d49abd typecheck if and while conditions, disallow ptr multiplication 2026-03-18 11:34:25 +01:00
5 changed files with 102 additions and 33 deletions

View File

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

View File

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

View File

@@ -20,11 +20,13 @@ pub enum Stmt {
},
Block(Vec<Stmt>),
If {
keyword: Token,
condition: Expr,
then_branch: Box<Stmt>,
else_branch: Box<Stmt>,
},
While {
keyword: Token,
condition: Expr,
body: Box<Stmt>,
},
@@ -280,6 +282,7 @@ impl Parser {
}
fn if_statement(&mut self) -> Result<Stmt, ZernError> {
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<Stmt, ZernError> {
let keyword = self.previous().clone();
let condition = self.expression()?;
let body = self.block()?;
Ok(Stmt::While {
keyword,
condition,
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->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

View File

@@ -51,6 +51,7 @@ impl Env {
}
pub fn pop_scope(&mut self) {
assert!(!self.scopes.is_empty());
self.scopes.pop();
}
@@ -119,16 +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 } => {
self.typecheck_expr(env, condition)?;
Stmt::While {
keyword,
condition,
body,
} => {
expect_types!(
self.typecheck_expr(env, condition)?,
["i64", "u8", "ptr", "bool"],
keyword.loc
);
self.typecheck_stmt(env, body)?;
}
Stmt::For {
@@ -191,8 +205,15 @@ impl TypeChecker {
Stmt::Extern(_) => {
// handled in the analyzer
}
Stmt::Struct { name: _, fields: _ } => {
// TODO: validate types
Stmt::Struct { name: _, fields } => {
for field in fields {
if !self.is_valid_type_name(&field.var_type.lexeme) {
return error!(
&field.var_type.loc,
format!("unknown type: {}", &field.var_type.lexeme)
);
}
}
}
}
Ok(())
@@ -202,30 +223,43 @@ impl TypeChecker {
match expr {
Expr::Binary { left, op, right } => {
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!(
self.typecheck_expr(env, right)?,
["i64", "ptr", "u8"],
op.loc
);
match op.token_type {
TokenType::Plus
| TokenType::Minus
| TokenType::Star
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 +381,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 +425,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())
@@ -402,7 +437,20 @@ impl TypeChecker {
error!(&op.loc, "can only take address of variables and functions")
}
},
Expr::New(struct_name) => Ok(struct_name.lexeme.clone()),
Expr::New(struct_name) => {
if !self
.analyzer
.borrow()
.structs
.contains_key(&struct_name.lexeme)
{
return error!(
&struct_name.loc,
format!("unknown struct name: {}", &struct_name.lexeme)
);
}
Ok(struct_name.lexeme.clone())
}
Expr::MemberAccess { left, field } => {
let left_type = self.typecheck_expr(env, left)?;
@@ -423,7 +471,12 @@ impl TypeChecker {
}
Expr::Cast { expr, type_name } => {
self.typecheck_expr(env, expr)?;
// TODO: validate target type
if !self.is_valid_type_name(&type_name.lexeme) {
return error!(
&type_name.loc,
format!("unknown type: {}", &type_name.lexeme)
);
}
Ok(type_name.lexeme.clone())
}
}