From 23d32f34e526cfb657a72e5b2dab86d1df765f0f Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Fri, 30 Dec 2016 15:45:10 +0000 Subject: [PATCH] Block Expressions and `give` --- code/demo.odin | 8 +- core/fmt.odin | 16 +-- core/os_windows.odin | 241 ++++++++++++++++++++++++++++++------------ core/sys/windows.odin | 51 ++++++++- src/checker/checker.c | 22 +++- src/checker/decl.c | 14 ++- src/checker/expr.c | 138 ++++++++++++++++++++++++ src/checker/stmt.c | 24 ++--- src/checker/types.c | 59 +++++++++++ src/parser.c | 71 ++++++++++++- src/ssa.c | 66 +++++++++++- src/tokenizer.c | 1 + 12 files changed, 605 insertions(+), 106 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); + +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; +} + +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 open(path string, mode int, perm u32) -> (Handle, Error) { + using win32; + if path.count == 0 { + return INVALID_HANDLE, ERROR_FILE_NOT_FOUND; } - File struct { - handle File_Handle; - last_write_time File_Time; + 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; } -} -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; + 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 create(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_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; +proc close(fd Handle) { + win32.CloseHandle(fd as win32.HANDLE); } -proc close(using f ^File) { - win32.CloseHandle(handle.p 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; -} - -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; + 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 false; + 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 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 (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"), \