aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-12-30 15:45:10 +0000
committerGinger Bill <bill@gingerbill.org>2016-12-30 15:45:10 +0000
commit23d32f34e526cfb657a72e5b2dab86d1df765f0f (patch)
tree7998bcf40ca9f581be6296bf4f68c102a5d234c8
parentd714bece47ea058e482389452cd428dad9c28fd0 (diff)
Block Expressions and `give`
-rw-r--r--code/demo.odin8
-rw-r--r--core/fmt.odin16
-rw-r--r--core/os_windows.odin233
-rw-r--r--core/sys/windows.odin51
-rw-r--r--src/checker/checker.c22
-rw-r--r--src/checker/decl.c14
-rw-r--r--src/checker/expr.c138
-rw-r--r--src/checker/stmt.c24
-rw-r--r--src/checker/types.c59
-rw-r--r--src/parser.c71
-rw-r--r--src/ssa.c66
-rw-r--r--src/tokenizer.c1
12 files changed, 601 insertions, 102 deletions
diff --git a/code/demo.odin b/code/demo.odin
index 897a5a878..bf484ba2a 100644
--- a/code/demo.odin
+++ b/code/demo.odin
@@ -12,8 +12,8 @@ import {
}
proc main() {
- var x = ~(0 as u32);
- var y = x << 32;
- fmt.println(y);
+ var a, b, c = {
+ give 1, 2, 123*321;
+ };
+ fmt.println(a, b, c);
}
-
diff --git a/core/fmt.odin b/core/fmt.odin
index 5bcc7344a..cfd442d2c 100644
--- a/core/fmt.odin
+++ b/core/fmt.odin
@@ -6,26 +6,26 @@ import {
const PRINT_BUF_SIZE = 1<<12;
-proc fprint(f ^os.File, args ..any) -> int {
+proc fprint(fd os.Handle, args ..any) -> int {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprint(^buf, ..args);
- os.write(f, buf);
+ os.write(fd, buf);
return buf.count;
}
-proc fprintln(f ^os.File, args ..any) -> int {
+proc fprintln(fd os.Handle, args ..any) -> int {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprintln(^buf, ..args);
- os.write(f, buf);
+ os.write(fd, buf);
return buf.count;
}
-proc fprintf(f ^os.File, fmt string, args ..any) -> int {
+proc fprintf(fd os.Handle, fmt string, args ..any) -> int {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprintf(^buf, fmt, ..args);
- os.write(f, buf);
+ os.write(fd, buf);
return buf.count;
}
@@ -42,11 +42,11 @@ proc printf(fmt string, args ..any) -> int {
-proc fprint_type(f ^os.File, info ^Type_Info) {
+proc fprint_type(fd os.Handle, info ^Type_Info) {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprint_type(^buf, info);
- os.write(f, buf);
+ os.write(fd, buf);
}
diff --git a/core/os_windows.odin b/core/os_windows.odin
index b0c0d3e9d..6dfeef42c 100644
--- a/core/os_windows.odin
+++ b/core/os_windows.odin
@@ -3,73 +3,191 @@ import {
"fmt.odin";
}
+
type {
+ Handle uint;
File_Time u64;
+ Error int;
+}
- File_Handle raw_union {
- p rawptr;
- i int;
- }
+const INVALID_HANDLE Handle = ~(0 as Handle);
- File struct {
- handle File_Handle;
- last_write_time File_Time;
- }
+const {
+ O_RDONLY = 0x00000;
+ O_WRONLY = 0x00001;
+ O_RDWR = 0x00002;
+ O_CREAT = 0x00040;
+ O_EXCL = 0x00080;
+ O_NOCTTY = 0x00100;
+ O_TRUNC = 0x00200;
+ O_NONBLOCK = 0x00800;
+ O_APPEND = 0x00400;
+ O_SYNC = 0x01000;
+ O_ASYNC = 0x02000;
+ O_CLOEXEC = 0x80000;
}
-proc open(name string) -> (File, bool) {
- using win32;
- var buf [300]byte;
- var f File;
- copy(buf[:], name as []byte);
- f.handle.p = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil) as rawptr;
- var success = f.handle.p != INVALID_HANDLE_VALUE;
- f.last_write_time = last_write_time(^f);
- return f, success;
+const {
+ ERROR_NONE Error = 0;
+ ERROR_FILE_NOT_FOUND Error = 2;
+ ERROR_PATH_NOT_FOUND Error = 3;
+ ERROR_ACCESS_DENIED Error = 5;
+ ERROR_NO_MORE_FILES Error = 18;
+ ERROR_HANDLE_EOF Error = 38;
+ ERROR_NETNAME_DELETED Error = 64;
+ ERROR_FILE_EXISTS Error = 80;
+ ERROR_BROKEN_PIPE Error = 109;
+ ERROR_BUFFER_OVERFLOW Error = 111;
+ ERROR_INSUFFICIENT_BUFFER Error = 122;
+ ERROR_MOD_NOT_FOUND Error = 126;
+ ERROR_PROC_NOT_FOUND Error = 127;
+ ERROR_DIR_NOT_EMPTY Error = 145;
+ ERROR_ALREADY_EXISTS Error = 183;
+ ERROR_ENVVAR_NOT_FOUND Error = 203;
+ ERROR_MORE_DATA Error = 234;
+ ERROR_OPERATION_ABORTED Error = 995;
+ ERROR_IO_PENDING Error = 997;
+ ERROR_NOT_FOUND Error = 1168;
+ ERROR_PRIVILEGE_NOT_HELD Error = 1314;
+ WSAEACCES Error = 10013;
+ WSAECONNRESET Error = 10054;
+}
+
+const { // Windows reserves errors >= 1<<29 for application use
+ ERROR_FILE_IS_PIPE Error = 1<<29 + iota;
}
-proc create(name string) -> (File, bool) {
+
+
+
+proc open(path string, mode int, perm u32) -> (Handle, Error) {
using win32;
+ if path.count == 0 {
+ return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
+ }
+
+ var access u32;
+ match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
+ case O_RDONLY: access = FILE_GENERIC_READ;
+ case O_WRONLY: access = FILE_GENERIC_WRITE;
+ case O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+ }
+
+ if mode&O_CREAT != 0 {
+ access |= FILE_GENERIC_WRITE;
+ }
+ if mode&O_APPEND != 0 {
+ access &~= FILE_GENERIC_WRITE;
+ access |= FILE_APPEND_DATA;
+ }
+
+ var share_mode = (FILE_SHARE_READ|FILE_SHARE_WRITE) as u32;
+ var sa ^SECURITY_ATTRIBUTES = nil;
+ var sa_inherit = SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
+ if mode&O_CLOEXEC == 0 {
+ sa = ^sa_inherit;
+ }
+
+ var create_mode u32;
+ match {
+ case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
+ create_mode = CREATE_NEW;
+ case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
+ create_mode = CREATE_ALWAYS;
+ case mode&O_CREAT == O_CREAT:
+ create_mode = OPEN_ALWAYS;
+ case mode&O_TRUNC == O_TRUNC:
+ create_mode = TRUNCATE_EXISTING;
+ default:
+ create_mode = OPEN_EXISTING;
+ }
+
var buf [300]byte;
- var f File;
- copy(buf[:], name as []byte);
- f.handle.p = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil) as rawptr;
- var success = f.handle.p != INVALID_HANDLE_VALUE;
- f.last_write_time = last_write_time(^f);
- return f, success;
+ copy(buf[:], path as []byte);
+
+ var handle = CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil) as Handle;
+ if handle == INVALID_HANDLE {
+ return handle, ERROR_NONE;
+ }
+ var err = GetLastError();
+ return INVALID_HANDLE, err as Error;
}
-proc close(using f ^File) {
- win32.CloseHandle(handle.p as win32.HANDLE);
+proc close(fd Handle) {
+ win32.CloseHandle(fd as win32.HANDLE);
}
-proc write(using f ^File, buf []byte) -> bool {
+proc write(fd Handle, data []byte) -> (int, Error) {
var bytes_written i32;
- return win32.WriteFile(handle.p as win32.HANDLE, buf.data, buf.count as i32, ^bytes_written, nil) != 0;
+ var e = win32.WriteFile(fd as win32.HANDLE, data.data, data.count as i32, ^bytes_written, nil);
+ if e != 0 {
+ return 0, e as Error;
+ }
+ return bytes_written as int, ERROR_NONE;
+}
+
+proc read(fd Handle, data []byte) -> (int, Error) {
+ var bytes_read i32;
+ var e = win32.ReadFile(fd as win32.HANDLE, data.data, data.count as u32, ^bytes_read, nil);
+ if e != win32.FALSE {
+ var err = win32.GetLastError();
+ return 0, err as Error;
+ }
+ return bytes_read as int, ERROR_NONE;
}
-proc file_has_changed(f ^File) -> bool {
- var last_write_time = last_write_time(f);
- if f.last_write_time != last_write_time {
- f.last_write_time = last_write_time;
- return true;
+proc seek(fd Handle, offset i64, whence int) -> (i64, Error) {
+ using win32;
+ var w u32;
+ match whence {
+ case 0: w = FILE_BEGIN;
+ case 1: w = FILE_CURRENT;
+ case 2: w = FILE_END;
+ }
+ var hi = (offset>>32) as i32;
+ var lo = offset as i32;
+ var ft = GetFileType(fd as HANDLE);
+ if ft == FILE_TYPE_PIPE {
+ return 0, ERROR_FILE_IS_PIPE;
+ }
+ var dw_ptr = SetFilePointer(fd as HANDLE, lo, ^hi, w);
+ if dw_ptr == INVALID_SET_FILE_POINTER {
+ var err = GetLastError();
+ return 0, err as Error;
}
- return false;
+ return (hi as i64)<<32 + (dw_ptr as i64), ERROR_NONE;
+}
+
+
+// NOTE(bill): Uses startup to initialize it
+var {
+ stdin = get_std_handle(win32.STD_INPUT_HANDLE);
+ stdout = get_std_handle(win32.STD_OUTPUT_HANDLE);
+ stderr = get_std_handle(win32.STD_ERROR_HANDLE);
+}
+
+proc get_std_handle(h int) -> Handle {
+ var fd = win32.GetStdHandle(h as i32);
+ win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
+ return fd as Handle;
}
-proc last_write_time(f ^File) -> File_Time {
+
+
+
+proc last_write_time(fd Handle) -> File_Time {
var file_info win32.BY_HANDLE_FILE_INFORMATION;
- win32.GetFileInformationByHandle(f.handle.p as win32.HANDLE, ^file_info);
- var l = file_info.last_write_time.low_date_time as File_Time;
- var h = file_info.last_write_time.high_date_time as File_Time;
- return l | h << 32;
+ win32.GetFileInformationByHandle(fd as win32.HANDLE, ^file_info);
+ var lo = file_info.last_write_time.lo as File_Time;
+ var hi = file_info.last_write_time.hi as File_Time;
+ return lo | hi << 32;
}
proc last_write_time_by_name(name string) -> File_Time {
var last_write_time win32.FILETIME;
- var data win32.WIN32_FILE_ATTRIBUTE_DATA;
+ var data win32.FILE_ATTRIBUTE_DATA;
var buf [1024]byte;
assert(buf.count > name.count);
@@ -80,46 +198,27 @@ proc last_write_time_by_name(name string) -> File_Time {
last_write_time = data.last_write_time;
}
- var l = last_write_time.low_date_time as File_Time;
- var h = last_write_time.high_date_time as File_Time;
+ var l = last_write_time.lo as File_Time;
+ var h = last_write_time.hi as File_Time;
return l | h << 32;
}
-const {
- FILE_STANDARD_INPUT = iota;
- FILE_STANDARD_OUTPUT;
- FILE_STANDARD_ERROR;
-
- FILE_STANDARD_COUNT;
-}
-// NOTE(bill): Uses startup to initialize it
-var {
- __std_files = [FILE_STANDARD_COUNT]File{
- {handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE) transmute File_Handle },
- {handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE) transmute File_Handle },
- {handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE) transmute File_Handle },
- };
-
- stdin = ^__std_files[FILE_STANDARD_INPUT];
- stdout = ^__std_files[FILE_STANDARD_OUTPUT];
- stderr = ^__std_files[FILE_STANDARD_ERROR];
-}
proc read_entire_file(name string) -> ([]byte, bool) {
var buf [300]byte;
copy(buf[:], name as []byte);
- var f, file_ok = open(name);
- if !file_ok {
+ var fd, err = open(name, O_RDONLY, 0);
+ if err != ERROR_NONE {
return nil, false;
}
- defer close(^f);
+ defer close(fd);
var length i64;
- var file_size_ok = win32.GetFileSizeEx(f.handle.p as win32.HANDLE, ^length) != 0;
+ var file_size_ok = win32.GetFileSizeEx(fd as win32.HANDLE, ^length) != 0;
if !file_size_ok {
return nil, false;
}
@@ -142,7 +241,7 @@ proc read_entire_file(name string) -> ([]byte, bool) {
to_read = MAX;
}
- win32.ReadFile(f.handle.p as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil);
+ win32.ReadFile(fd as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil);
if single_read_length <= 0 {
free(data.data);
return nil, false;
@@ -178,3 +277,5 @@ proc current_thread_id() -> int {
return GetCurrentThreadId() as int;
}
+
+
diff --git a/core/sys/windows.odin b/core/sys/windows.odin
index 90f8bd424..3e22e1c40 100644
--- a/core/sys/windows.odin
+++ b/core/sys/windows.odin
@@ -23,6 +23,9 @@ type {
const {
INVALID_HANDLE_VALUE = (-1 as int) as HANDLE;
+ FALSE BOOL = 0;
+ TRUE BOOL = 1;
+
CS_VREDRAW = 0x0001;
CS_HREDRAW = 0x0002;
CS_OWNDC = 0x0020;
@@ -88,7 +91,7 @@ type {
}
FILETIME struct #ordered {
- low_date_time, high_date_time u32;
+ lo, hi u32;
}
BY_HANDLE_FILE_INFORMATION struct #ordered {
@@ -104,7 +107,7 @@ type {
file_index_low u32;
}
- WIN32_FILE_ATTRIBUTE_DATA struct #ordered {
+ FILE_ATTRIBUTE_DATA struct #ordered {
file_attributes u32;
creation_time,
last_access_time,
@@ -174,8 +177,8 @@ proc GetCurrentThreadId() -> u32 #foreign #dll_import
proc CloseHandle (h HANDLE) -> i32 #foreign #dll_import
proc GetStdHandle(h i32) -> HANDLE #foreign #dll_import
proc CreateFileA (filename ^u8, desired_access, share_mode u32,
- security rawptr,
- creation, flags_and_attribs u32, template_file HANDLE) -> HANDLE #foreign #dll_import
+ security rawptr,
+ creation, flags_and_attribs u32, template_file HANDLE) -> HANDLE #foreign #dll_import
proc ReadFile (h HANDLE, buf rawptr, to_read u32, bytes_read ^i32, overlapped rawptr) -> BOOL #foreign #dll_import
proc WriteFile (h HANDLE, buf rawptr, len i32, written_result ^i32, overlapped rawptr) -> i32 #foreign #dll_import
@@ -183,6 +186,23 @@ proc GetFileSizeEx (file_handle HANDLE, file_size ^i64) -> BOOL #for
proc GetFileAttributesExA (filename ^u8, info_level_id GET_FILEEX_INFO_LEVELS, file_info rawptr) -> BOOL #foreign #dll_import
proc GetFileInformationByHandle(file_handle HANDLE, file_info ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign #dll_import
+proc GetFileType(file_handle HANDLE) -> u32 #foreign #dll_import
+proc SetFilePointer(file_handle HANDLE, distance_to_move i32, distance_to_move_high ^i32, move_method u32) -> u32 #foreign #dll_import
+
+proc SetHandleInformation(obj HANDLE, mask, flags u32) -> BOOL #foreign #dll_import
+
+const {
+ HANDLE_FLAG_INHERIT = 1;
+ HANDLE_FLAG_PROTECT_FROM_CLOSE = 2;
+}
+
+
+const {
+ FILE_BEGIN = 0;
+ FILE_CURRENT = 1;
+ FILE_END = 2;
+}
+
const {
FILE_SHARE_READ = 0x00000001;
FILE_SHARE_WRITE = 0x00000002;
@@ -192,6 +212,8 @@ const {
FILE_GENERIC_WRITE = 0x40000000;
FILE_GENERIC_READ = 0x80000000;
+ FILE_APPEND_DATA = 0x0004;
+
STD_INPUT_HANDLE = -10;
STD_OUTPUT_HANDLE = -11;
STD_ERROR_HANDLE = -12;
@@ -201,6 +223,27 @@ const {
OPEN_EXISTING = 3;
OPEN_ALWAYS = 4;
TRUNCATE_EXISTING = 5;
+
+ FILE_ATTRIBUTE_READONLY = 0x00000001;
+ FILE_ATTRIBUTE_HIDDEN = 0x00000002;
+ FILE_ATTRIBUTE_SYSTEM = 0x00000004;
+ FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
+ FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
+ FILE_ATTRIBUTE_DEVICE = 0x00000040;
+ FILE_ATTRIBUTE_NORMAL = 0x00000080;
+ FILE_ATTRIBUTE_TEMPORARY = 0x00000100;
+ FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200;
+ FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;
+ FILE_ATTRIBUTE_COMPRESSED = 0x00000800;
+ FILE_ATTRIBUTE_OFFLINE = 0x00001000;
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000;
+ FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;
+
+ FILE_TYPE_DISK = 0x0001;
+ FILE_TYPE_CHAR = 0x0002;
+ FILE_TYPE_PIPE = 0x0003;
+
+ INVALID_SET_FILE_POINTER = ~(0 as u32);
}
diff --git a/src/checker/checker.c b/src/checker/checker.c
index 2159eb95b..c61884253 100644
--- a/src/checker/checker.c
+++ b/src/checker/checker.c
@@ -32,6 +32,16 @@ typedef struct TypeAndValue {
ExactValue value;
} TypeAndValue;
+bool is_operand_value(Operand o) {
+ switch (o.mode) {
+ case Addressing_Value:
+ case Addressing_Variable:
+ case Addressing_Constant:
+ return true;
+ }
+ return false;
+}
+
typedef struct DeclInfo {
@@ -92,6 +102,14 @@ typedef enum ExprKind {
Expr_Stmt,
} ExprKind;
+// Statements and Declarations
+typedef enum StmtFlag {
+ Stmt_BreakAllowed = 1<<0,
+ Stmt_ContinueAllowed = 1<<1,
+ Stmt_FallthroughAllowed = 1<<2,
+ Stmt_GiveAllowed = 1<<3,
+} StmtFlag;
+
typedef enum BuiltinProcId {
BuiltinProc_Invalid,
@@ -357,9 +375,11 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) {
void check_open_scope(Checker *c, AstNode *node) {
GB_ASSERT(node != NULL);
+ node = unparen_expr(node);
GB_ASSERT(node->kind == AstNode_Invalid ||
is_ast_node_stmt(node) ||
- is_ast_node_type(node));
+ is_ast_node_type(node) ||
+ node->kind == AstNode_BlockExpr);
Scope *scope = make_scope(c->context.scope, c->allocator);
add_scope(c, node, scope);
if (node->kind == AstNode_ProcType) {
diff --git a/src/checker/decl.c b/src/checker/decl.c
index cc2c48f78..0c4d3f075 100644
--- a/src/checker/decl.c
+++ b/src/checker/decl.c
@@ -92,7 +92,14 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
}
if (rhs_count > 0 && lhs_count != rhs_count) {
- error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
+ error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
+ }
+
+ if (lhs[0]->kind == Entity_Variable &&
+ lhs[0]->Variable.is_let) {
+ if (lhs_count != rhs_count) {
+ error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count);
+ }
}
gb_temp_arena_memory_end(tmp);
@@ -350,6 +357,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
}
+ if (proc_type->Proc.calling_convention != ProcCC_Odin) {
+ error_node(d->proc_decl, "An internal procedure may only have the Odin calling convention");
+ proc_type->Proc.calling_convention = ProcCC_Odin;
+ }
+
d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
diff --git a/src/checker/expr.c b/src/checker/expr.c
index 299dd55c9..390979cbb 100644
--- a/src/checker/expr.c
+++ b/src/checker/expr.c
@@ -14,6 +14,10 @@ void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type
void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr, Type *named_type);
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
void update_expr_type (Checker *c, AstNode *e, Type *type, bool final);
+bool check_is_terminating (AstNode *node);
+bool check_has_break (AstNode *stmt, bool implicit);
+void check_stmt (Checker *c, AstNode *node, u32 flags);
+void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
gb_inline Type *check_type(Checker *c, AstNode *expression) {
@@ -3652,6 +3656,42 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
return false;
}
+
+bool check_is_giving(AstNode *node, AstNode **give_expr);
+
+bool check_giving_list(AstNodeArray stmts, AstNode **give_expr) {
+ // Iterate backwards
+ for (isize n = stmts.count-1; n >= 0; n--) {
+ AstNode *stmt = stmts.e[n];
+ if (stmt->kind != AstNode_EmptyStmt) {
+ return check_is_giving(stmt, give_expr);
+ }
+ }
+
+ if (give_expr) *give_expr = NULL;
+ return false;
+}
+
+bool check_is_giving(AstNode *node, AstNode **give_expr) {
+ switch (node->kind) {
+ case_ast_node(es, ExprStmt, node);
+ return check_is_giving(es->expr, give_expr);
+ case_end;
+
+ case_ast_node(ye, GiveExpr, node);
+ if (give_expr) *give_expr = node;
+ return true;
+ case_end;
+
+ case_ast_node(be, BlockExpr, node);
+ return check_giving_list(be->stmts, give_expr);
+ case_end;
+ }
+
+ if (give_expr) *give_expr = NULL;
+ return false;
+}
+
ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) {
ExprKind kind = Expr_Stmt;
@@ -3700,7 +3740,105 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
o->mode = Addressing_Value;
o->type = proc_type;
+ case_end;
+
+ case_ast_node(be, BlockExpr, node);
+ if (c->proc_stack.count == 0) {
+ error_node(node, "A block expression is only allowed withing a procedure");
+ goto error;
+ }
+
+ for (isize i = be->stmts.count-1; i >= 0; i--) {
+ if (be->stmts.e[i]->kind != AstNode_EmptyStmt) {
+ break;
+ }
+ be->stmts.count--;
+ }
+
+ check_open_scope(c, node);
+ check_stmt_list(c, be->stmts, Stmt_GiveAllowed);
+ check_close_scope(c);
+
+
+ if (be->stmts.count == 0) {
+ error_node(node, "Empty block expression");
+ goto error;
+ }
+ AstNode *give_node = NULL;
+ if (!check_is_giving(node, &give_node) || give_node == NULL) {
+ error_node(node, "Missing give statement at");
+ goto error;
+ }
+ GB_ASSERT(give_node != NULL && give_node->kind == AstNode_GiveExpr);
+ be->give_node = give_node;
+
+ o->type = type_of_expr(&c->info, give_node);
+ o->mode = Addressing_Value;
+ case_end;
+
+ case_ast_node(ye, GiveExpr, node);
+ if (c->proc_stack.count == 0) {
+ error_node(node, "A give expression is only allowed withing a procedure");
+ goto error;
+ }
+
+ if (ye->results.count == 0) {
+ error_node(node, "No give values");
+ goto error;
+ }
+
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+ Array(Operand) operands;
+ array_init_reserve(&operands, c->tmp_allocator, 2*ye->results.count);
+
+ for_array(i, ye->results) {
+ AstNode *rhs = ye->results.e[i];
+ Operand o = {0};
+ check_multi_expr(c, &o, rhs);
+ if (!is_operand_value(o)) {
+ error_node(rhs, "Expected a value for a `give`");
+ continue;
+ }
+ if (o.type->kind != Type_Tuple) {
+ array_add(&operands, o);
+ } else {
+ TypeTuple *tuple = &o.type->Tuple;
+ for (isize j = 0; j < tuple->variable_count; j++) {
+ o.type = tuple->variables[j]->type;
+ array_add(&operands, o);
+ }
+ }
+ }
+
+ Type *give_type = NULL;
+
+ if (operands.count == 0) {
+ error_node(node, "`give` has no type");
+ gb_temp_arena_memory_end(tmp);
+ goto error;
+ } else if (operands.count == 1) {
+ give_type = default_type(operands.e[0].type);
+ } else {
+ Type *tuple = make_type_tuple(c->allocator);
+
+ Entity **variables = gb_alloc_array(c->allocator, Entity *, operands.count);
+ isize variable_index = 0;
+ for_array(i, operands) {
+ Type *type = default_type(operands.e[i].type);
+ variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
+ }
+ tuple->Tuple.variables = variables;
+ tuple->Tuple.variable_count = operands.count;
+
+ give_type = tuple;
+ }
+ gb_temp_arena_memory_end(tmp);
+
+
+ o->type = give_type;
+ o->mode = Addressing_Value;
case_end;
case_ast_node(cl, CompoundLit, node);
diff --git a/src/checker/stmt.c b/src/checker/stmt.c
index 7ae8355d2..fd10fab31 100644
--- a/src/checker/stmt.c
+++ b/src/checker/stmt.c
@@ -1,16 +1,3 @@
-bool check_is_terminating(AstNode *node);
-bool check_has_break (AstNode *stmt, bool implicit);
-void check_stmt (Checker *c, AstNode *node, u32 flags);
-
-
-// Statements and Declarations
-typedef enum StmtFlag {
- Stmt_BreakAllowed = GB_BIT(0),
- Stmt_ContinueAllowed = GB_BIT(1),
- Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
-} StmtFlag;
-
-
void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
if (stmts.count == 0) {
return;
@@ -40,6 +27,10 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
if (n->kind == AstNode_ReturnStmt && i+1 < max) {
error_node(n, "Statements after this `return` are never executed");
+ } else if (n->kind == AstNode_ExprStmt && i+1 < max) {
+ if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
+ error_node(n, "A `give` must be the last statement in a block");
+ }
}
check_stmt(c, n, new_flags);
@@ -48,7 +39,6 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
}
bool check_is_terminating_list(AstNodeArray stmts) {
-
// Iterate backwards
for (isize n = stmts.count-1; n >= 0; n--) {
AstNode *stmt = stmts.e[n];
@@ -355,6 +345,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (operand.expr->kind == AstNode_CallExpr) {
return;
}
+ if (operand.expr->kind == AstNode_GiveExpr) {
+ if ((flags&Stmt_GiveAllowed) != 0) {
+ return;
+ }
+ error_node(node, "Illegal use of `give`");
+ }
gbString expr_str = expr_to_string(operand.expr);
error_node(node, "Expression is not used: `%s`", expr_str);
gb_string_free(expr_str);
diff --git a/src/checker/types.c b/src/checker/types.c
index c3fff7e01..e58876025 100644
--- a/src/checker/types.c
+++ b/src/checker/types.c
@@ -775,6 +775,65 @@ Type *default_type(Type *type) {
return type;
}
+// NOTE(bill): Valid Compile time execution #run type
+bool is_type_cte_safe(Type *type) {
+ type = default_type(base_type(type));
+ switch (type->kind) {
+ case Type_Basic:
+ switch (type->Basic.kind) {
+ case Basic_rawptr:
+ case Basic_any:
+ return false;
+ }
+ return true;
+
+ case Type_Pointer:
+ return false;
+
+ case Type_Array:
+ return is_type_cte_safe(type->Array.elem);
+
+ case Type_Vector: // NOTE(bill): This should always to be true but this is for sanity reasons
+ return is_type_cte_safe(type->Vector.elem);
+
+ case Type_Slice:
+ return false;
+
+ case Type_Maybe:
+ return is_type_cte_safe(type->Maybe.elem);
+
+ case Type_Record: {
+ if (type->Record.kind != TypeRecord_Struct) {
+ return false;
+ }
+ for (isize i = 0; i < type->Record.field_count; i++) {
+ Entity *v = type->Record.fields[i];
+ if (!is_type_cte_safe(v->type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case Type_Tuple: {
+ for (isize i = 0; i < type->Tuple.variable_count; i++) {
+ Entity *v = type->Tuple.variables[i];
+ if (!is_type_cte_safe(v->type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case Type_Proc:
+ // TODO(bill): How should I handle procedures in the CTE stage?
+ // return type->Proc.calling_convention == ProcCC_Odin;
+ return false;
+ }
+
+ return false;
+}
+
diff --git a/src/parser.c b/src/parser.c
index b620e911b..09a7d1ae1 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -144,6 +144,15 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
bool triple_indexed; \
}) \
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
+ AST_NODE_KIND(BlockExpr, "block expr", struct { \
+ AstNodeArray stmts; \
+ Token open, close; \
+ AstNode *give_node; \
+ }) \
+ AST_NODE_KIND(GiveExpr, "give expression", struct { \
+ Token token; \
+ AstNodeArray results; \
+ }) \
AST_NODE_KIND(_ExprEnd, "", i32) \
AST_NODE_KIND(_StmtBegin, "", i32) \
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
@@ -432,6 +441,10 @@ Token ast_node_token(AstNode *node) {
return node->DerefExpr.op;
case AstNode_DemaybeExpr:
return node->DemaybeExpr.op;
+ case AstNode_BlockExpr:
+ return node->BlockExpr.open;
+ case AstNode_GiveExpr:
+ return node->GiveExpr.token;
case AstNode_BadStmt:
return node->BadStmt.begin;
case AstNode_EmptyStmt:
@@ -729,6 +742,24 @@ AstNode *make_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token
return result;
}
+
+AstNode *make_block_expr(AstFile *f, AstNodeArray stmts, Token open, Token close) {
+ AstNode *result = make_node(f, AstNode_BlockExpr);
+ result->BlockExpr.stmts = stmts;
+ result->BlockExpr.open = open;
+ result->BlockExpr.close = close;
+ return result;
+}
+
+AstNode *make_give_expr(AstFile *f, Token token, AstNodeArray results) {
+ AstNode *result = make_node(f, AstNode_GiveExpr);
+ result->GiveExpr.token = token;
+ result->GiveExpr.results = results;
+ return result;
+}
+
+
+
AstNode *make_bad_stmt(AstFile *f, Token begin, Token end) {
AstNode *result = make_node(f, AstNode_BadStmt);
result->BadStmt.begin = begin;
@@ -1508,6 +1539,9 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
}
}
+AstNodeArray parse_lhs_expr_list(AstFile *f);
+AstNodeArray parse_rhs_expr_list(AstFile *f);
+
AstNode *parse_operand(AstFile *f, bool lhs) {
AstNode *operand = NULL; // Operand
switch (f->curr_token.kind) {
@@ -1623,6 +1657,17 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
return type;
}
+ case Token_OpenBrace: {
+ AstNodeArray stmts = {0};
+ Token open, close;
+ open = expect_token(f, Token_OpenBrace);
+ f->expr_level++;
+ stmts = parse_stmt_list(f);
+ f->expr_level--;
+ close = expect_token(f, Token_CloseBrace);
+ return make_block_expr(f, stmts, open, close);
+ }
+
default: {
AstNode *type = parse_identifier_or_type(f);
if (type != NULL) {
@@ -2619,12 +2664,15 @@ AstNode *parse_return_stmt(AstFile *f) {
syntax_error(f->curr_token, "You cannot use a return statement in the file scope");
return make_bad_stmt(f, f->curr_token, f->curr_token);
}
+ if (f->expr_level > 0) {
+ syntax_error(f->curr_token, "You cannot use a return statement within an expression");
+ return make_bad_stmt(f, f->curr_token, f->curr_token);
+ }
Token token = expect_token(f, Token_return);
AstNodeArray results = make_ast_node_array(f);
- if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace &&
- f->curr_token.pos.line == token.pos.line) {
+ if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
results = parse_rhs_expr_list(f);
}
@@ -2632,6 +2680,23 @@ AstNode *parse_return_stmt(AstFile *f) {
return make_return_stmt(f, token, results);
}
+
+AstNode *parse_give_stmt(AstFile *f) {
+ if (f->curr_proc == NULL) {
+ syntax_error(f->curr_token, "You cannot use a give statement in the file scope");
+ return make_bad_stmt(f, f->curr_token, f->curr_token);
+ }
+ if (f->expr_level == 0) {
+ syntax_error(f->curr_token, "A give statement must be used within an expression");
+ return make_bad_stmt(f, f->curr_token, f->curr_token);
+ }
+
+ Token token = expect_token(f, Token_give);
+ AstNodeArray results = parse_rhs_expr_list(f);
+ expect_semicolon(f, results.e[0]);
+ return make_expr_stmt(f, make_give_expr(f, token, results));
+}
+
AstNode *parse_for_stmt(AstFile *f) {
if (f->curr_proc == NULL) {
syntax_error(f->curr_token, "You cannot use a for statement in the file scope");
@@ -2861,7 +2926,6 @@ AstNode *parse_stmt(AstFile *f) {
expect_semicolon(f, s);
return s;
- // TODO(bill): other keywords
case Token_if: return parse_if_stmt(f);
case Token_when: return parse_when_stmt(f);
case Token_for: return parse_for_stmt(f);
@@ -2869,6 +2933,7 @@ AstNode *parse_stmt(AstFile *f) {
case Token_defer: return parse_defer_stmt(f);
case Token_asm: return parse_asm_stmt(f);
case Token_return: return parse_return_stmt(f);
+ case Token_give: return parse_give_stmt(f);
case Token_break:
case Token_continue:
diff --git a/src/ssa.c b/src/ssa.c
index f85dbaa2d..8dc454d17 100644
--- a/src/ssa.c
+++ b/src/ssa.c
@@ -2571,7 +2571,7 @@ ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id
return *value;
}
-
+void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts);
ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
expr = unparen_expr(expr);
@@ -2626,6 +2626,70 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
return ssa_addr_load(proc, ssa_build_addr(proc, expr));
case_end;
+ case_ast_node(be, BlockExpr, expr);
+ ssa_emit_comment(proc, str_lit("BlockExpr"));
+ ssa_open_scope(proc);
+
+ AstNodeArray stmts = be->stmts;
+ stmts.count--;
+ ssa_build_stmt_list(proc, stmts);
+
+ AstNode *give_stmt = be->stmts.e[be->stmts.count-1];
+ GB_ASSERT(give_stmt->kind == AstNode_ExprStmt);
+ AstNode *give_expr = give_stmt->ExprStmt.expr;
+ GB_ASSERT(give_expr->kind == AstNode_GiveExpr);
+ ssaValue *value = ssa_build_expr(proc, give_expr);
+
+ ssa_close_scope(proc, ssaDeferExit_Default, NULL);
+
+ return value;
+ case_end;
+
+ case_ast_node(ge, GiveExpr, expr);
+ ssa_emit_comment(proc, str_lit("GiveExpr"));
+
+ ssaValue *v = NULL;
+ Type *give_type = type_of_expr(proc->module->info, expr);
+ GB_ASSERT(give_type != NULL);
+ if (give_type->kind != Type_Tuple) {
+ v = ssa_emit_conv(proc, ssa_build_expr(proc, ge->results.e[0]), give_type);
+ } else {
+ TypeTuple *tuple = &give_type->Tuple;
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
+
+ ssaValueArray results;
+ array_init_reserve(&results, proc->module->tmp_allocator, tuple->variable_count);
+
+ for_array(res_index, ge->results) {
+ ssaValue *res = ssa_build_expr(proc, ge->results.e[res_index]);
+ Type *t = ssa_type(res);
+ if (t->kind == Type_Tuple) {
+ for (isize i = 0; i < t->Tuple.variable_count; i++) {
+ Entity *e = t->Tuple.variables[i];
+ ssaValue *v = ssa_emit_struct_ev(proc, res, i);
+ array_add(&results, v);
+ }
+ } else {
+ array_add(&results, res);
+ }
+ }
+
+ v = ssa_add_local_generated(proc, give_type);
+ for_array(i, results) {
+ Entity *e = tuple->variables[i];
+ ssaValue *res = ssa_emit_conv(proc, results.e[i], e->type);
+ ssaValue *field = ssa_emit_struct_ep(proc, v, i);
+ ssa_emit_store(proc, field, res);
+ }
+
+ v = ssa_emit_load(proc, v);
+
+ gb_temp_arena_memory_end(tmp);
+ }
+
+ return v;
+ case_end;
+
case_ast_node(ue, UnaryExpr, expr);
switch (ue->op.kind) {
case Token_Pointer:
diff --git a/src/tokenizer.c b/src/tokenizer.c
index f87a48b3b..51510af3d 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -104,6 +104,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_range, "range"), \
TOKEN_KIND(Token_defer, "defer"), \
TOKEN_KIND(Token_return, "return"), \
+ TOKEN_KIND(Token_give, "give"), \
TOKEN_KIND(Token_struct, "struct"), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_raw_union, "raw_union"), \