From 789b297f324206a18128f61679bfd36b9c6265b2 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Thu, 4 May 2017 20:34:50 +0100 Subject: [PATCH] Add hidden __tag for union variables. --- code/demo.odin | 1 + core/fmt.odin | 3 +- core/os.odin | 38 +++++++++++++- core/os_linux.odin | 71 ++++++++++++++------------ core/os_windows.odin | 118 +++++++++++++++++++------------------------ core/os_x.odin | 66 +++++++++++++----------- src/check_expr.c | 6 +++ src/ir.c | 23 +++++++-- src/parser.c | 2 +- src/types.c | 11 ++++ 10 files changed, 205 insertions(+), 134 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 323754a23..54ad01201 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,4 +1,5 @@ #import "fmt.odin"; +#import "os.odin"; main :: proc() { immutable program := "+ + * - /"; diff --git a/core/fmt.odin b/core/fmt.odin index 59f7bea16..c0418ad31 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -194,7 +194,6 @@ write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) { fi := Fmt_Info{buf = buf}; fmt_int(&fi, u64(8*info.size), false, 64, 'd'); } - case Float: match info.size { case 4: write_string(buf, "f32"); @@ -212,6 +211,8 @@ write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) { } case String: write_string(buf, "string"); case Boolean: write_string(buf, "bool"); + case Any: + write_string(buf, "any"); case Atomic: write_string(buf, "atomic "); write_type(buf, info.elem); diff --git a/core/os.odin b/core/os.odin index 8d9eadb75..fa9b23649 100644 --- a/core/os.odin +++ b/core/os.odin @@ -1,3 +1,37 @@ #load "os_windows.odin" when ODIN_OS == "windows"; -#load "os_x.odin" when ODIN_OS == "osx"; -#load "os_linux.odin" when ODIN_OS == "linux"; \ No newline at end of file +#load "os_x.odin" when ODIN_OS == "osx"; +#load "os_linux.odin" when ODIN_OS == "linux"; + +write_string :: proc(fd: Handle, str: string) -> (int, Errno) { + return write(fd, []byte(str)); +} + +read_entire_file :: proc(name: string) -> ([]byte, bool) { + fd, err := open(name, O_RDONLY, 0); + if err != ERROR_NONE { + return nil, false; + } + defer close(fd); + + length: i64; + if length, err = file_size(fd); err != ERROR_NONE { + return nil, false; + } + + if length == 0 { + return nil, true; + } + + data := make([]byte, length); + if data == nil { + return nil, false; + } + + bytes_read, read_err := read(fd, data); + if read_err != ERROR_NONE { + free(data); + return nil, false; + } + return data[0.. (i64, Errno) { return res, 0; } +file_size :: proc(fd: Handle) -> (i64, bool) { + prev, _ := seek(fd, 0, SEEK_CUR); + size, err := seek(fd, 0, SEEK_END); + seek(fd, prev, SEEK_SET); + return size, err != 0; +} + // NOTE(bill): Uses startup to initialize it stdin: Handle = 0; @@ -204,44 +211,46 @@ access :: proc(path: string, mask: int) -> bool #inline { return _unix_access(cstr, mask) == 0; } -read_entire_file :: proc(name: string) -> ([]byte, bool) { - fd: Handle; - err: Errno; - size: i64; - fd, err = open_simple(name, O_RDONLY); - if(err != 0) { - fmt.println("Failed to open file."); - return nil, false; - } - defer close(fd); - // We have a file - size, err = seek(fd, 0, SEEK_END); - if(err != 0) { - fmt.println("Failed to seek to end of file."); - return nil, false; - } +// read_entire_file :: proc(name: string) -> ([]byte, bool) { +// fd: Handle; +// err: Errno; +// size: i64; - _, err = seek(fd, 0, SEEK_SET); - if(err != 0) { - fmt.println("Failed to seek to beginning of file."); - return nil, false; - } +// fd, err = open_simple(name, O_RDONLY); +// if(err != 0) { +// fmt.println("Failed to open file."); +// return nil, false; +// } +// defer close(fd); - // We have a file size! +// // We have a file +// size, err = seek(fd, 0, SEEK_END); +// if(err != 0) { +// fmt.println("Failed to seek to end of file."); +// return nil, false; +// } - data := make([]u8, size+1); - if data == nil { - fmt.println("Failed to allocate file buffer."); - return nil, false; - } +// _, err = seek(fd, 0, SEEK_SET); +// if(err != 0) { +// fmt.println("Failed to seek to beginning of file."); +// return nil, false; +// } - read(fd, data); - data[size] = 0; +// // We have a file size! - return data, true; -} +// data := make([]u8, size+1); +// if data == nil { +// fmt.println("Failed to allocate file buffer."); +// return nil, false; +// } + +// read(fd, data); +// data[size] = 0; + +// return data, true; +// } heap_alloc :: proc(size: int) -> rawptr { assert(size > 0); diff --git a/core/os_windows.odin b/core/os_windows.odin index 1722efd3a..df3e36e44 100644 --- a/core/os_windows.odin +++ b/core/os_windows.odin @@ -109,33 +109,61 @@ close :: proc(fd: Handle) { win32.CloseHandle(win32.Handle(fd)); } -write_string :: proc(fd: Handle, str: string) -> (int, Errno) { - return write(fd, []byte(str)); -} + write :: proc(fd: Handle, data: []byte) -> (int, Errno) { if len(data) == 0 { return 0, ERROR_NONE; } - bytes_written: i32; - e := win32.WriteFile(win32.Handle(fd), &data[0], i32(len(data)), &bytes_written, nil); - if e == win32.FALSE { - err := win32.GetLastError(); - return 0, Errno(err); + single_write_length: i32; + total_write: i64; + length := i64(len(data)); + + for total_write < length { + remaining := length - total_write; + to_read: i32; + MAX :: 1<<31-1; + if remaining <= MAX { + to_read = i32(remaining); + } else { + to_read = MAX; + } + e := win32.WriteFile(win32.Handle(fd), &data[total_write], to_read, &single_write_length, nil); + if single_write_length <= 0 || e == win32.FALSE { + err := win32.GetLastError(); + return int(total_write), Errno(e); + } + total_write += i64(single_write_length); } - return int(bytes_written), ERROR_NONE; + return int(total_write), ERROR_NONE; } read :: proc(fd: Handle, data: []byte) -> (int, Errno) { if len(data) == 0 { return 0, ERROR_NONE; } - bytes_read: i32; - e := win32.ReadFile(win32.Handle(fd), &data[0], u32(len(data)), &bytes_read, nil); - if e == win32.FALSE { - err := win32.GetLastError(); - return 0, Errno(err); + + single_read_length: i32; + total_read: i64; + length := i64(len(data)); + + for total_read < length { + remaining := length - total_read; + to_read: u32; + MAX :: 1<<32-1; + if remaining <= MAX { + to_read = u32(remaining); + } else { + to_read = MAX; + } + + e := win32.ReadFile(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil); + if single_read_length <= 0 || e == win32.FALSE { + err := win32.GetLastError(); + return int(total_read), Errno(e); + } + total_read += i64(single_read_length); } - return int(bytes_read), ERROR_NONE; + return int(total_read), ERROR_NONE; } seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { @@ -159,6 +187,16 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE; } +file_size :: proc(fd: Handle) -> (i64, Errno) { + length: i64; + err: Errno; + if win32.GetFileSizeEx(win32.Handle(fd), &length) == 0 { + err = Errno(win32.GetLastError()); + } + return length, err; +} + + // NOTE(bill): Uses startup to initialize it stdin := get_std_handle(win32.STD_INPUT_HANDLE); @@ -204,56 +242,6 @@ last_write_time_by_name :: proc(name: string) -> File_Time { } -read_entire_file :: proc(name: string) -> ([]byte, bool) { - buf: [300]byte; - copy(buf[..], []byte(name)); - - fd, err := open(name, O_RDONLY, 0); - if err != ERROR_NONE { - return nil, false; - } - defer close(fd); - - length: i64; - if ok := win32.GetFileSizeEx(win32.Handle(fd), &length) != 0; !ok { - return nil, false; - } - - if length == 0 { - return nil, true; - } - - data := make([]byte, length); - if data == nil { - return nil, false; - } - - single_read_length: i32; - total_read: i64; - - for total_read < length { - remaining := length - total_read; - to_read: u32; - MAX :: 1<<32-1; - if remaining <= MAX { - to_read = u32(remaining); - } else { - to_read = MAX; - } - - win32.ReadFile(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil); - if single_read_length <= 0 { - free(data); - return nil, false; - } - - total_read += i64(single_read_length); - } - - return data, true; -} - - heap_alloc :: proc(size: int) -> rawptr { return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size); diff --git a/core/os_x.odin b/core/os_x.odin index 0e3b7d115..a330d1350 100644 --- a/core/os_x.odin +++ b/core/os_x.odin @@ -196,6 +196,14 @@ seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errn return final_offset, 0; } +file_size :: proc(fd: Handle) -> (i64, bool) { + prev, _ := seek(fd, 0, SEEK_CUR); + size, err := seek(fd, 0, SEEK_END); + seek(fd, prev, SEEK_SET); + return size, err != 0; +} + + // NOTE(bill): Uses startup to initialize it stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE); @@ -221,43 +229,43 @@ access :: proc(path: string, mask: int) -> bool #inline { return unix_access(cstr, mask) == 0; } -read_entire_file :: proc(name: string) -> ([]byte, bool) { +// read_entire_file :: proc(name: string) -> ([]byte, bool) { - handle, err := open_simple(name, O_RDONLY); - if(err != 0) { - fmt.println("Failed to open file."); - return nil, false; - } - defer(close(handle)); +// handle, err := open_simple(name, O_RDONLY); +// if(err != 0) { +// fmt.println("Failed to open file."); +// return nil, false; +// } +// defer(close(handle)); - // We have a file! +// // We have a file! - size: AddressSize; - size, err = seek(handle, 0, SEEK_END); - if(err != 0) { - fmt.println("Failed to seek to end of file."); - return nil, false; - } +// size: AddressSize; +// size, err = seek(handle, 0, SEEK_END); +// if(err != 0) { +// fmt.println("Failed to seek to end of file."); +// return nil, false; +// } - _, err = seek(handle, 0, SEEK_SET); - if(err != 0) { - fmt.println("Failed to seek to beginning of file."); - return nil, false; - } +// _, err = seek(handle, 0, SEEK_SET); +// if(err != 0) { +// fmt.println("Failed to seek to beginning of file."); +// return nil, false; +// } - // We have a file size! +// // We have a file size! - data := make([]u8, size+1); - if data == nil { - fmt.println("Failed to allocate file buffer."); - return nil, false; - } +// data := make([]u8, size+1); +// if data == nil { +// fmt.println("Failed to allocate file buffer."); +// return nil, false; +// } - read(handle, data); - data[size] = 0; +// read(handle, data); +// data[size] = 0; - return data, true; -} +// return data, true; +// } heap_alloc :: proc(size: int) -> rawptr #inline { assert(size > 0); diff --git a/src/check_expr.c b/src/check_expr.c index eef6d1483..a2267f96c 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -422,6 +422,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls, e->identifier = name; if (str_eq(name_token.string, str_lit("_"))) { fields[field_index++] = e; + } else if (str_eq(name_token.string, str_lit("__tag"))) { + error_node(name, "`__tag` is a reserved identifier for fields"); } else { HashKey key = hash_string(name_token.string); Entity **found = map_entity_get(&entity_map, key); @@ -660,6 +662,10 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { union_type->Record.field_count = field_count; union_type->Record.are_offsets_set = false; union_type->Record.is_ordered = true; + { + Entity *__tag = make_entity_field(c->allocator, NULL, make_token_ident(str_lit("__tag")), t_int, false, -1); + union_type->Record.union__tag = __tag; + } for_array(i, ut->variants) { AstNode *variant = ut->variants.e[i]; diff --git a/src/ir.c b/src/ir.c index 2a7a3a6f4..956877ff8 100644 --- a/src/ir.c +++ b/src/ir.c @@ -2244,8 +2244,13 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { } else if (is_type_union(t)) { type_set_offsets(a, t); GB_ASSERT(t->Record.field_count > 0); - GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); - result_type = make_type_pointer(a, t->Record.fields[index]->type); + if (index == -1) { + index = t->Record.field_count+1; + result_type = t_int_ptr; + } else { + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + } } else if (is_type_tuple(t)) { GB_ASSERT(t->Tuple.variable_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); @@ -2316,8 +2321,12 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { result_type = t->Record.fields[index]->type; } else if (is_type_union(t)) { type_set_offsets(a, t); - GB_ASSERT(t->Record.field_count > 0); - GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + if (index == -1) { + index = t->Record.field_count+1; + result_type = t_int_ptr; + } else { + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + } result_type = t->Record.fields[index]->type; } else if (is_type_tuple(t)) { GB_ASSERT(t->Tuple.variable_count > 0); @@ -2394,7 +2403,11 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) { type = type->Record.fields[index]->type; e = ir_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); } else if (type->kind == Type_Record) { - type = type->Record.fields[index]->type; + if (index == -1) { + type = t_int; + } else { + type = type->Record.fields[index]->type; + } e = ir_emit_struct_ep(proc, e, index); } else if (type->kind == Type_Tuple) { type = type->Tuple.variables[index]->type; diff --git a/src/parser.c b/src/parser.c index efa4acb83..09234a538 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1844,7 +1844,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { return ast_proc_lit(f, type, NULL, tags, foreign_library, foreign_name, link_name); } if (tags != 0) { - syntax_error(token, "A procedure type cannot have tags"); + // syntax_error(token, "A procedure type cannot have tags"); } return type; diff --git a/src/types.c b/src/types.c index 82012258e..4001253f8 100644 --- a/src/types.c +++ b/src/types.c @@ -92,6 +92,8 @@ typedef struct TypeRecord { // Entity_TypeName - union Entity **variants; i32 variant_count; + Entity * union__tag; + i64 * offsets; bool are_offsets_set; @@ -1415,6 +1417,15 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n sel.index.count = prev_count; } } + if (type->Record.kind == TypeRecord_Union) { + if (str_eq(field_name, str_lit("__tag"))) { + Entity *e = type->Record.union__tag; + GB_ASSERT(e != NULL); + selection_add_index(&sel, -1); // HACK(bill): Leaky memory + sel.entity = e; + return sel; + } + } } return sel;