Block Expressions and give

This commit is contained in:
Ginger Bill
2016-12-30 15:45:10 +00:00
parent d714bece47
commit 23d32f34e5
12 changed files with 605 additions and 106 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -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"), \