typecheck return types

This commit is contained in:
2026-03-17 17:11:14 +01:00
parent 664a51c88d
commit 4154901339
7 changed files with 72 additions and 50 deletions

View File

@@ -51,9 +51,9 @@ impl Analyzer {
("_builtin_read64".into(), FnType::new("i64", vec!["ptr"])),
(
"_builtin_set64".into(),
FnType::new("void", vec!["ptr", "any"]),
FnType::new("void", vec!["ptr", "i64"]),
),
("_builtin_syscall".into(), FnType::new_variadic("any")),
("_builtin_syscall".into(), FnType::new_variadic("i64")),
("io.printf".into(), FnType::new_variadic("void")),
("_builtin_environ".into(), FnType::new("ptr", vec![])),
]),
@@ -145,7 +145,7 @@ impl Analyzer {
self.analyze_stmt(body)?;
}
Stmt::Return(expr) => {
Stmt::Return { expr, keyword: _ } => {
self.analyze_expr(expr)?;
}
Stmt::For {

View File

@@ -339,7 +339,7 @@ _builtin_environ:
emit!(&mut self.output, " ret");
}
}
Stmt::Return(expr) => {
Stmt::Return { expr, keyword: _ } => {
self.compile_expr(env, expr)?;
emit!(&mut self.output, " mov rsp, rbp");
emit!(&mut self.output, " pop rbp");

View File

@@ -41,7 +41,10 @@ pub enum Stmt {
body: Box<Stmt>,
exported: bool,
},
Return(Expr),
Return {
expr: Expr,
keyword: Token,
},
Break,
Continue,
Extern(Token),
@@ -262,7 +265,11 @@ impl Parser {
} else if self.match_token(&[TokenType::KeywordFor]) {
self.for_statement()
} else if self.match_token(&[TokenType::KeywordReturn]) {
Ok(Stmt::Return(self.expression()?))
let keyword = self.previous().clone();
Ok(Stmt::Return {
expr: self.expression()?,
keyword,
})
} else if self.match_token(&[TokenType::KeywordBreak]) {
Ok(Stmt::Break)
} else if self.match_token(&[TokenType::KeywordContinue]) {

View File

@@ -82,7 +82,7 @@ func net.close[s: i64] : void
_builtin_syscall(SYS_close, s)
func net.pack_addr[a: u8, b: u8, c: u8, d: u8] : i64
return (a << 24) | (b << 16) | (c << 8) | d
return (a as i64 << 24) | (b << 16) | (c << 8) | d
struct net.UDPSocket
fd: i64
@@ -101,7 +101,7 @@ func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket
if s->fd < 0
mem.free(s)
err.set(ERR_SYSCALL_FAILED, "net.create_udp_client?: failed to create a socket")
return 0
return 0 as net.UDPSocket
s->addr = mem.alloc(16)
mem.zero(s->addr, 16)
@@ -115,16 +115,16 @@ func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket
s->addr[7] = packed_host & 255
return s
func net.udp_listen?[packed_host: i64, port: i64] : net.UDPSocket
func net.create_udp_server?[packed_host: i64, port: i64] : net.UDPSocket
err.clear()
let s: net.UDPSocket = net.create_udp_client?(packed_host, port)
if err.check()
return 0
return 0 as net.UDPSocket
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
net.UDPSocket.close_and_free(s)
err.set(ERR_SYSCALL_FAILED, "net.udp_listen?: failed to bind")
return 0
err.set(ERR_SYSCALL_FAILED, "net.create_udp_server?: failed to bind")
return 0 as net.UDPSocket
return s

View File

@@ -10,7 +10,7 @@ func err.code[] : i64
return mem.read64(_builtin_err_code())
func err.msg[] : str
return mem.read64(_builtin_err_msg())
return mem.read64(_builtin_err_msg()) as str
func err.check[] : bool
return err.code() != 0
@@ -73,7 +73,7 @@ func mem._request_space?[size: i64] : mem.Block
let blk_ptr: ptr = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) as ptr
if blk_ptr == -1
err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed")
return 0
return 0 as mem.Block
let blk: mem.Block = blk_ptr as mem.Block
@@ -173,11 +173,11 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
if new_size < 0
err.set(ERR_ALLOC_FAILED, "mem.realloc? called with negative size")
return 0
return 0 as ptr
if new_size == 0
mem.free(x)
return 0
return 0 as ptr
new_size = mem._align(new_size)
@@ -204,7 +204,7 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
let new_ptr: ptr = mem.alloc?(new_size)
if err.check()
return 0
return 0 as ptr
mem.copy(x, new_ptr, math.min(blk->size, new_size))
mem.free(x)
@@ -232,16 +232,16 @@ func mem.read8[x: ptr] : u8
return x[0]
func mem.read16[x: ptr] : i64
return x[0] | (x[1] << 8)
return x[0] as i64 | (x[1] << 8)
func mem.read16be[x: ptr] : i64
return (x[0] << 8) | x[1]
return (x[0] << 8) as i64 | x[1]
func mem.read32[x: ptr] : i64
return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24)
return x[0] as i64 | (x[1] << 8) | (x[2] << 16) | (x[3] << 24)
func mem.read32be[x: ptr] : i64
return (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3]
return (x[0] as i64 << 24) | (x[1] << 16) | (x[2] << 8) | x[3]
func mem.read64[x: ptr] : i64
return _builtin_read64(x)
@@ -349,7 +349,7 @@ func io.Buffer.alloc?[size: i64] : io.Buffer
buffer->data = mem.alloc?(size)
if err.check()
mem.free(buffer)
return 0
return 0 as io.Buffer
return buffer
func io.Buffer.free[buf: io.Buffer] : void
@@ -364,7 +364,7 @@ func io.read_text_file?[path: str] : str
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
if fd < 0
err.set(ERR_OPEN_FAILED, "io.read_text_file?: failed to open file")
return 0
return 0 as str
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
_builtin_syscall(SYS_lseek, fd, 0, 0)
@@ -372,7 +372,7 @@ func io.read_text_file?[path: str] : str
let buffer: str = mem.alloc?(size + 1) as str
if err.check()
_builtin_syscall(SYS_close, fd)
return 0
return 0 as str
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
_builtin_syscall(SYS_close, fd)
@@ -380,7 +380,7 @@ func io.read_text_file?[path: str] : str
if n != size
mem.free(buffer)
err.set(ERR_READ_FAILED, "io.read_text_file?: failed to read file")
return 0
return 0 as str
buffer[n] = 0
return buffer
@@ -390,7 +390,7 @@ func io.read_binary_file?[path: str] : io.Buffer
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
if fd < 0
err.set(ERR_OPEN_FAILED, "io.read_binary_file?: failed to open file")
return 0
return 0 as io.Buffer
let buf: io.Buffer = new io.Buffer
buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
@@ -400,7 +400,7 @@ func io.read_binary_file?[path: str] : io.Buffer
if err.check()
io.Buffer.free(buf)
_builtin_syscall(SYS_close, fd)
return 0
return 0 as io.Buffer
let n: i64 = _builtin_syscall(SYS_read, fd, buf->data, buf->size)
_builtin_syscall(SYS_close, fd)
@@ -408,7 +408,7 @@ func io.read_binary_file?[path: str] : io.Buffer
if n != buf->size
io.Buffer.free(buf)
err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file")
return 0
return 0 as io.Buffer
return buf
@@ -664,7 +664,7 @@ func str.hex_encode[s: str, s_len: i64] : str
out[j] = 0
return out
func str._hex_digit_to_int[d: u8] : i64
func str._hex_digit_to_int[d: u8] : u8
if d >= 'a' && d <= 'f'
return d - 'a' + 10
if d >= 'A' && d <= 'F'
@@ -902,13 +902,13 @@ func os.urandom?[n: i64]: ptr
err.clear()
let buffer: ptr = mem.alloc(n)
if err.check()
return 0
return 0 as ptr
let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0)
if fd < 0
mem.free(buffer)
err.set(ERR_READ_FAILED, "os.urandom?: failed to open /dev/urandom")
return 0
return 0 as ptr
let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n)
_builtin_syscall(SYS_close, fd)
@@ -916,7 +916,7 @@ func os.urandom?[n: i64]: ptr
if n != bytes_read
mem.free(buffer)
err.set(ERR_READ_FAILED, "os.urandom?: failed to read /dev/urandom")
return 0
return 0 as ptr
return buffer
@@ -968,7 +968,7 @@ func os.list_directory?[path: str] : Array
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
if fd < 0
err.set(ERR_OPEN_FAILED, "os.list_directory?: failed to open dir")
return 0
return 0 as Array
let files: Array = []
let buf: ptr = mem.alloc(1024)
@@ -983,7 +983,7 @@ func os.list_directory?[path: str] : Array
_builtin_syscall(SYS_close, fd)
err.set(ERR_SYSCALL_FAILED, "os.list_directory?: getdents64() failed")
return 0
return 0 as Array
else if n == 0
break

View File

@@ -1,6 +1,3 @@
#![allow(dead_code)]
#![allow(unused_variables)]
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::{
@@ -72,11 +69,15 @@ impl Env {
pub struct TypeChecker {
analyzer: Rc<RefCell<Analyzer>>,
current_function_return_type: String,
}
impl TypeChecker {
pub fn new(analyzer: Rc<RefCell<Analyzer>>) -> TypeChecker {
TypeChecker { analyzer }
TypeChecker {
analyzer,
current_function_return_type: String::new(),
}
}
pub fn typecheck_stmt(&mut self, env: &mut Env, stmt: &Stmt) -> Result<(), ZernError> {
@@ -106,11 +107,15 @@ impl TypeChecker {
env.define_var(name.lexeme.clone(), actual_type);
}
Stmt::Const { name, value } => {}
Stmt::Const { name: _, value: _ } => {
// handled in the analyzer
}
Stmt::Block(stmts) => {
env.push_scope();
for stmt in stmts {
self.typecheck_stmt(env, stmt)?;
}
env.pop_scope();
}
Stmt::If {
condition,
@@ -140,14 +145,12 @@ impl TypeChecker {
env.pop_scope();
}
Stmt::Function {
name,
name: _,
params,
return_type,
body,
exported,
exported: _,
} => {
env.push_scope();
if !self.is_valid_type_name(&return_type.lexeme) {
return error!(
&return_type.loc,
@@ -155,7 +158,11 @@ impl TypeChecker {
);
}
for (i, param) in params.iter().enumerate() {
self.current_function_return_type = return_type.lexeme.clone();
env.push_scope();
for param in params {
if !self.is_valid_type_name(&param.var_type.lexeme) {
return error!(
&param.var_name.loc,
@@ -170,15 +177,22 @@ impl TypeChecker {
env.pop_scope();
}
Stmt::Return(expr) => {
// TODO
Stmt::Return { expr, keyword } => {
let expected = if self.current_function_return_type == "void" {
"i64".into()
} else {
self.current_function_return_type.clone()
};
expect_type!(self.typecheck_expr(env, expr)?, expected, keyword.loc);
}
Stmt::Break => {}
Stmt::Continue => {}
Stmt::Extern(_) => {
// handled in the analyzer
}
Stmt::Struct { name, fields } => {}
Stmt::Struct { name: _, fields: _ } => {
// TODO: validate types
}
}
Ok(())
}
@@ -265,7 +279,7 @@ impl TypeChecker {
match left.as_ref() {
Expr::Variable(name) => {
let var_type = match env.get_var_type(&name.lexeme) {
let existing_var_type = match env.get_var_type(&name.lexeme) {
Some(x) => x,
None => {
return error!(
@@ -274,7 +288,7 @@ impl TypeChecker {
);
}
};
expect_type!(*var_type, value_type, name.loc);
expect_type!(value_type, *existing_var_type, name.loc);
}
Expr::Index {
expr,
@@ -403,6 +417,7 @@ impl TypeChecker {
}
Expr::Cast { expr, type_name } => {
self.typecheck_expr(env, expr)?;
// TODO: validate target type
Ok(type_name.lexeme.clone())
}
}