typecheck return types
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
func main[] : i64
|
||||
let s: net.UDPSocket = must(net.udp_listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||
let s: net.UDPSocket = must(net.create_udp_server?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||
|
||||
io.println("Listening on port 8000...")
|
||||
while true
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(¶m.var_type.lexeme) {
|
||||
return error!(
|
||||
¶m.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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user