Browse Source

Block Expressions and `give`

Ginger Bill 8 years ago
parent
commit
23d32f34e5
12 changed files with 601 additions and 102 deletions
  1. 4 4
      code/demo.odin
  2. 8 8
      core/fmt.odin
  3. 167 66
      core/os_windows.odin
  4. 47 4
      core/sys/windows.odin
  5. 21 1
      src/checker/checker.c
  6. 13 1
      src/checker/decl.c
  7. 138 0
      src/checker/expr.c
  8. 10 14
      src/checker/stmt.c
  9. 59 0
      src/checker/types.c
  10. 68 3
      src/parser.c
  11. 65 1
      src/ssa.c
  12. 1 0
      src/tokenizer.c

+ 4 - 4
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);
 }
-

+ 8 - 8
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);
 }
 
 

+ 167 - 66
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;
 }
 
+
+

+ 47 - 4
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);
 }
 
 

+ 21 - 1
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) {

+ 13 - 1
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);

+ 138 - 0
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);

+ 10 - 14
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);

+ 59 - 0
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;
+}
+
 
 
 

+ 68 - 3
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:

+ 65 - 1
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:

+ 1 - 0
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"), \