From dbd06388538b3ba38ab4e5d03c4bc695854f5ff0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 14 Feb 2019 11:11:05 +0000 Subject: [PATCH 01/32] Fix untyped ternary string IR conversion --- core/mem/allocators.odin | 2 ++ src/ir.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index af6ed329a..6dafe18c0 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -381,6 +381,8 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, return nil; } + alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2); + raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr { curr_addr := uintptr(&s.data[0]) + uintptr(s.offset); padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header)); diff --git a/src/ir.cpp b/src/ir.cpp index aca3c3ce9..bdccaa5a6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4644,6 +4644,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { } + // bool <-> llvm bool if (is_type_boolean(src) && dst == t_llvm_bool) { return ir_emit(proc, ir_instr_conv(proc, irConv_trunc, value, src_type, t)); @@ -4906,7 +4907,13 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit_load(proc, result); } - + if (is_type_untyped(src)) { + if (is_type_string(src) && is_type_string(dst)) { + irValue *result = ir_add_local_generated(proc, t, false); + ir_emit_store(proc, result, value); + return ir_emit_load(proc, result); + } + } gb_printf_err("ir_emit_conv: src -> dst\n"); gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); From f917935f9d0110bb276e5a1fffad02c6a4ae84ea Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Feb 2019 10:04:36 +0000 Subject: [PATCH 02/32] Disallow compound literals for `struct #raw_union` (fix) --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e5140ce25..79f980a79 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5778,7 +5778,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (cl->elems.count == 0) { break; // NOTE(bill): No need to init } - if (!is_type_struct(t)) { + if (t->Struct.is_raw_union) { if (cl->elems.count != 0) { gbString type_str = type_to_string(type); error(node, "Illegal compound literal type '%s'", type_str); From 79b585ada897169c56c7183806a9a811c96c3140 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Feb 2019 21:45:33 +0000 Subject: [PATCH 03/32] Add minor additions to mem, sync, and sys/win32 --- core/mem/mem.odin | 8 +++++++ core/sync/sync_windows.odin | 28 ++++++++++++++++++++++ core/sys/win32/kernel32.odin | 45 ++++++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 4bb94ba0f..47591437f 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -183,6 +183,14 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr { return uintptr(p); } + +align_forward_int :: inline proc(ptr, align: int) -> int { + return int(align_forward_uintptr(uintptr(ptr), uintptr(align))); +} +align_forward_uint :: inline proc(ptr, align: uint) -> uint { + return uint(align_forward_uintptr(uintptr(ptr), uintptr(align))); +} + context_from_allocator :: proc(a: Allocator) -> type_of(context) { context.allocator = a; return context; diff --git a/core/sync/sync_windows.odin b/core/sync/sync_windows.odin index 3854f6807..b0a9d944c 100644 --- a/core/sync/sync_windows.odin +++ b/core/sync/sync_windows.odin @@ -2,6 +2,11 @@ package sync import "core:sys/win32" +foreign { + @(link_name="llvm.x86.sse2.pause") + yield_processor :: proc() --- +} + Semaphore :: struct { _handle: win32.Handle, } @@ -14,6 +19,12 @@ Condition :: struct { event: win32.Handle, } +Ticket_Mutex :: struct { + ticket: u64, + serving: u64, +} + + current_thread_id :: proc() -> i32 { return i32(win32.get_current_thread_id()); } @@ -81,3 +92,20 @@ condition_destroy :: proc(using c: ^Condition) { win32.close_handle(event); } } + + +ticket_mutex_init :: proc(m: ^Ticket_Mutex) { + atomic_store(&m.ticket, 0, Ordering.Relaxed); + atomic_store(&m.serving, 0, Ordering.Relaxed); +} + +ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) { + ticket := atomic_add(&m.ticket, 1, Ordering.Relaxed); + for ticket != m.serving { + yield_processor(); + } +} + +ticket_mutex_unlock :: inline proc(m: ^Ticket_Mutex) { + atomic_add(&m.serving, 1, Ordering.Relaxed); +} diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin index a41fe32de..6f9390cc4 100644 --- a/core/sys/win32/kernel32.odin +++ b/core/sys/win32/kernel32.odin @@ -56,8 +56,8 @@ foreign kernel32 { @(link_name="GetFileSizeEx") get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool ---; @(link_name="GetFileAttributesA") get_file_attributes_a :: proc(filename: cstring) -> u32 ---; @(link_name="GetFileAttributesW") get_file_attributes_w :: proc(filename: Wstring) -> u32 ---; - @(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> Bool ---; - @(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> Bool ---; + @(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---; + @(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---; @(link_name="GetFileInformationByHandle") get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool ---; @(link_name="CreateDirectoryA") create_directory_a :: proc(path: cstring, security_attributes: ^Security_Attributes) -> Bool ---; @@ -168,3 +168,44 @@ foreign kernel32 { @(link_name="GetProcAddress") get_proc_address :: proc(h: Hmodule, c_str: cstring) -> rawptr ---; } + +Memory_Basic_Information :: struct { + base_address: rawptr, + allocation_base: rawptr, + allocation_protect: u32, + region_size: uint, + state: u32, + protect: u32, + type: u32, +} + +@(default_calling_convention = "std") +foreign kernel32 { + @(link_name="VirtualAlloc") virtual_alloc :: proc(address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr --- + @(link_name="VirtualAllocEx") virtual_alloc_ex :: proc(process: Handle, address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr --- + @(link_name="VirtualFree") virtual_free :: proc(address: rawptr, size: uint, free_type: u32) -> Bool --- + @(link_name="VirtualLock") virtual_lock :: proc(address: rawptr, size: uint) -> Bool --- + @(link_name="VirtualProtect") virtual_protect :: proc(address: rawptr, size: uint, new_protect: u32, old_protect: ^u32) -> Bool --- + @(link_name="VirtualQuery") virtual_query :: proc(address: rawptr, buffer: ^Memory_Basic_Information, length: uint) -> uint --- +} + +MEM_COMMIT :: 0x00001000; +MEM_RESERVE :: 0x00002000; +MEM_DECOMMIT :: 0x00004000; +MEM_RELEASE :: 0x00008000; +MEM_RESET :: 0x00080000; +MEM_RESET_UNDO :: 0x01000000; + +MEM_LARGE_PAGES :: 0x20000000; +MEM_PHYSICAL :: 0x00400000; +MEM_TOP_DOWN :: 0x00100000; +MEM_WRITE_WATCH :: 0x00200000; + +PAGE_NOACCESS :: 0x01; +PAGE_READONLY :: 0x02; +PAGE_READWRITE :: 0x04; +PAGE_WRITECOPY :: 0x08; +PAGE_EXECUTE :: 0x10; +PAGE_EXECUTE_READ :: 0x20; +PAGE_EXECUTE_READWRITE :: 0x40; +PAGE_EXECUTE_WRITECOPY :: 0x80; From a07232ea6367337d785ef07a1de659c7555357bf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 14:11:48 +0000 Subject: [PATCH 04/32] Fix missing `break` in switch statement for `deferred_in` in the IR --- src/ir.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ir.cpp b/src/ir.cpp index bdccaa5a6..cd7e8d99a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3035,6 +3035,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro break; case DeferredProcedure_in: result_as_args = in_args; + break; case DeferredProcedure_out: result_as_args = ir_value_to_array(p, result); break; From 64bd884d94fc45600eaed9566585d455b875a87a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 14:42:44 +0000 Subject: [PATCH 05/32] Add "none" calling convention --- src/ir_print.cpp | 1 + src/parser.cpp | 1 + src/parser.hpp | 3 +++ src/types.cpp | 3 +++ 4 files changed, 8 insertions(+) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a3fdc282b..c6e717f74 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1052,6 +1052,7 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; case ProcCC_StdCall: ir_write_str_lit(f, "cc 64 "); break; case ProcCC_FastCall: ir_write_str_lit(f, "cc 65 "); break; + case ProcCC_None: ir_write_str_lit(f, ""); break; default: GB_PANIC("unknown calling convention: %d", cc); } } diff --git a/src/parser.cpp b/src/parser.cpp index a62bac233..e38c34a0a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2700,6 +2700,7 @@ ProcCallingConvention string_to_calling_convention(String s) { if (s == "std") return ProcCC_StdCall; if (s == "fastcall") return ProcCC_FastCall; if (s == "fast") return ProcCC_FastCall; + if (s == "none") return ProcCC_None; return ProcCC_Invalid; } diff --git a/src/parser.hpp b/src/parser.hpp index fabb7ff8e..13dd1258c 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -166,6 +166,9 @@ enum ProcCallingConvention { // ProcCC_VectorCall, // ProcCC_ClrCall, + ProcCC_None, + + ProcCC_ForeignBlockDefault = -1, }; diff --git a/src/types.cpp b/src/types.cpp index 52c06ef71..859805f29 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2894,6 +2894,9 @@ gbString write_type_to_string(gbString str, Type *type) { case ProcCC_FastCall: str = gb_string_appendc(str, " \"fastcall\" "); break; + case ProcCC_None: + str = gb_string_appendc(str, " \"none\" "); + break; // case ProcCC_VectorCall: // str = gb_string_appendc(str, " \"vectorcall\" "); // break; From 4c51384ad64a2f63863e6bacab7aeee881659047 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 16:44:16 +0000 Subject: [PATCH 06/32] `intrinsics.vector` type (Experimental) --- core/fmt/fmt.odin | 24 +++++++ core/runtime/core.odin | 8 ++- core/types/types.odin | 5 ++ src/check_expr.cpp | 54 ++++++++++++++++ src/checker.cpp | 19 ++++++ src/checker.hpp | 5 ++ src/ir.cpp | 13 ++++ src/ir_print.cpp | 35 ++++++++++ src/types.cpp | 144 ++++++++++++++++++++++++----------------- 9 files changed, 246 insertions(+), 61 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index c4a535d77..0df06c0ec 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1008,6 +1008,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); } + case runtime.Type_Info_Simd_Vector: + if info.is_x86_mmx { + strings.write_string(fi.buf, "intrinsics.x86_mmx<>"); + } + strings.write_byte(fi.buf, '<'); + defer strings.write_byte(fi.buf, '>'); + for i in 0..info.count-1 { + if i > 0 do strings.write_string(fi.buf, ", "); + + data := uintptr(v.data) + uintptr(i*info.elem_size); + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); + } + + case runtime.Type_Info_Slice: strings.write_byte(fi.buf, '['); defer strings.write_byte(fi.buf, ']'); @@ -1448,5 +1462,15 @@ write_type :: proc(buf: ^strings.Builder, ti: ^runtime.Type_Info) { write_string(buf, "opaque "); write_type(buf, info.elem); + case runtime.Type_Info_Simd_Vector: + if info.is_x86_mmx { + write_string(buf, "intrinsics.x86_mmx"); + } else { + write_string(buf, "intrinsics.vector("); + write_i64(buf, i64(info.count)); + write_string(buf, ", "); + write_type(buf, info.elem); + write_byte(buf, ')'); + } } } diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 9307b5589..38dd8f225 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -112,9 +112,14 @@ Type_Info_Bit_Set :: struct { lower: i64, upper: i64, }; - Type_Info_Opaque :: struct { elem: ^Type_Info, +}; +Type_Info_Simd_Vector :: struct { + elem: ^Type_Info, + elem_size: int, + count: int, + is_x86_mmx: bool, } Type_Info :: struct { @@ -145,6 +150,7 @@ Type_Info :: struct { Type_Info_Bit_Field, Type_Info_Bit_Set, Type_Info_Opaque, + Type_Info_Simd_Vector, }, } diff --git a/core/types/types.odin b/core/types/types.odin index 485530fea..dc4db549f 100644 --- a/core/types/types.odin +++ b/core/types/types.odin @@ -267,3 +267,8 @@ is_opaque :: proc(info: ^rt.Type_Info) -> bool { _, ok := rt.type_info_base(info).variant.(rt.Type_Info_Opaque); return ok; } +is_simd_vector :: proc(info: ^rt.Type_Info) -> bool { + if info == nil do return false; + _, ok := rt.type_info_base(info).variant.(rt.Type_Info_Simd_Vector); + return ok; +} diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 79f980a79..75c16b705 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4072,6 +4072,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_vector: { + Operand x = {}; + Operand y = {}; + x = *operand; + if (!is_type_integer(x.type) || x.mode != Addressing_Constant) { + error(call, "Expected a constant integer for 'intrinsics.vector'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + if (x.value.value_integer.neg) { + error(call, "Negative vector element length"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + i64 count = big_int_to_i64(&x.value.value_integer); + + check_expr_or_type(c, &y, ce->args[1]); + if (y.mode != Addressing_Type) { + error(call, "Expected a type 'intrinsics.vector'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + Type *elem = y.type; + if (!is_type_valid_vector_elem(elem)) { + gbString str = type_to_string(elem); + error(call, "Invalid element type for 'intrinsics.vector', expected an integer or float with no specific endianness, got '%s'", str); + gb_string_free(str); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Type; + operand->type = alloc_type_simd_vector(count, elem); + break; + } + case BuiltinProc_atomic_fence: case BuiltinProc_atomic_fence_acq: case BuiltinProc_atomic_fence_rel: @@ -5902,6 +5942,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Type_Slice: case Type_Array: case Type_DynamicArray: + case Type_SimdVector: { Type *elem_type = nullptr; String context_name = {}; @@ -5922,6 +5963,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type add_package_dependency(c, "runtime", "__dynamic_array_reserve"); add_package_dependency(c, "runtime", "__dynamic_array_append"); + } else if (t->kind == Type_SimdVector) { + elem_type = t->SimdVector.elem; + context_name = str_lit("simd vector literal"); + max_type_count = t->SimdVector.count; } else { GB_PANIC("unreachable"); } @@ -5972,6 +6017,15 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); } } + + if (t->kind == Type_SimdVector) { + if (!is_constant) { + error(node, "Expected all constant elements for a simd vector"); + } + if (t->SimdVector.is_x86_mmx) { + error(node, "Compound literals are not allowed with intrinsics.x86_mmx"); + } + } break; } diff --git a/src/checker.cpp b/src/checker.cpp index 9cac82911..7900555a5 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -712,6 +712,15 @@ void init_universal(void) { } } + // TODO(bill): Set the correct arch for this + if (bc->metrics.arch == TargetArch_amd64 || bc->metrics.arch == TargetArch_386) { + t_vector_x86_mmx = alloc_type(Type_SimdVector); + t_vector_x86_mmx->SimdVector.is_x86_mmx = true; + + Entity *entity = alloc_entity(Entity_TypeName, nullptr, make_token_ident(str_lit("x86_mmx")), t_vector_x86_mmx); + add_global_entity(entity, intrinsics_pkg->scope); + } + t_u8_ptr = alloc_type_pointer(t_u8); t_int_ptr = alloc_type_pointer(t_int); @@ -1248,6 +1257,10 @@ void add_type_info_type(CheckerContext *c, Type *t) { add_type_info_type(c, bt->Proc.results); break; + case Type_SimdVector: + add_type_info_type(c, bt->SimdVector.elem); + break; + default: GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind); break; @@ -1419,6 +1432,10 @@ void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->Proc.results); break; + case Type_SimdVector: + add_min_dep_type_info(c, bt->SimdVector.elem); + break; + default: GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind])); break; @@ -1795,6 +1812,7 @@ void init_core_type_info(Checker *c) { t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set")); t_type_info_opaque = find_core_type(c, str_lit("Type_Info_Opaque")); + t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -1818,6 +1836,7 @@ void init_core_type_info(Checker *c) { t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set); t_type_info_opaque_ptr = alloc_type_pointer(t_type_info_opaque); + t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector); } void init_mem_allocator(Checker *c) { diff --git a/src/checker.hpp b/src/checker.hpp index abc349129..de491671e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -89,6 +89,8 @@ enum BuiltinProcId { BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures // "Intrinsics" + BuiltinProc_vector, + BuiltinProc_atomic_fence, BuiltinProc_atomic_fence_acq, BuiltinProc_atomic_fence_rel, @@ -194,6 +196,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { // "Intrinsics" + {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type + + {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/ir.cpp b/src/ir.cpp index cd7e8d99a..4df6665f7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7268,6 +7268,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { case Type_Array: et = bt->Array.elem; break; case Type_Slice: et = bt->Slice.elem; break; case Type_BitSet: et = bt->BitSet.elem; break; + case Type_SimdVector: et = bt->SimdVector.elem; break; } String proc_name = {}; @@ -9995,6 +9996,18 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info tag = ir_emit_conv(proc, variant_ptr, t_type_info_opaque_ptr); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->Opaque.elem)); break; + + case Type_SimdVector: + ir_emit_comment(proc, str_lit("Type_SimdVector")); + tag = ir_emit_conv(proc, variant_ptr, t_type_info_simd_vector_ptr); + if (t->SimdVector.is_x86_mmx) { + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), v_true); + } else { + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem))); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count)); + } + break; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c6e717f74..0cc9f52e3 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -574,6 +574,16 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { case Type_Opaque: ir_print_type(f, m, strip_opaque_type(t)); return; + + case Type_SimdVector: + if (t->SimdVector.is_x86_mmx) { + ir_write_str_lit(f, "x86_mmx"); + } else { + ir_fprintf(f, "<%lld x ", t->SimdVector.count);; + ir_print_type(f, m, t->SimdVector.elem); + ir_write_byte(f, '>'); + } + return; } } @@ -802,6 +812,31 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } ir_write_byte(f, ']'); + } else if (is_type_simd_vector(type)) { + ast_node(cl, CompoundLit, value.value_compound); + + Type *elem_type = type->SimdVector.elem; + isize elem_count = cl->elems.count; + if (elem_count == 0) { + ir_write_str_lit(f, "zeroinitializer"); + break; + } + GB_ASSERT_MSG(elem_count == type->SimdVector.count, "%td != %td", elem_count, type->SimdVector.count); + + ir_write_byte(f, '<'); + + for (isize i = 0; i < elem_count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + ir_print_compound_element(f, m, tav.value, elem_type); + } + for (isize i = elem_count; i < type->SimdVector.count; i++) { + if (i >= elem_count) ir_write_str_lit(f, ", "); + ir_print_compound_element(f, m, empty_exact_value, elem_type); + } + + ir_write_byte(f, '>'); } else if (is_type_struct(type)) { gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); defer (gb_temp_arena_memory_end(tmp)); diff --git a/src/types.cpp b/src/types.cpp index 859805f29..6dcbf029f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -215,6 +215,12 @@ struct TypeUnion { i64 lower; \ i64 upper; \ }) \ + TYPE_KIND(SimdVector, struct { \ + i64 count; \ + Type *elem; \ + bool is_x86_mmx; \ + }) \ + @@ -460,13 +466,13 @@ gb_global Type *t_type_info_map = nullptr; gb_global Type *t_type_info_bit_field = nullptr; gb_global Type *t_type_info_bit_set = nullptr; gb_global Type *t_type_info_opaque = nullptr; +gb_global Type *t_type_info_simd_vector = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; gb_global Type *t_type_info_rune_ptr = nullptr; gb_global Type *t_type_info_float_ptr = nullptr; gb_global Type *t_type_info_complex_ptr = nullptr; -gb_global Type *t_type_info_quaternion_ptr = nullptr; gb_global Type *t_type_info_any_ptr = nullptr; gb_global Type *t_type_info_typeid_ptr = nullptr; gb_global Type *t_type_info_string_ptr = nullptr; @@ -484,6 +490,7 @@ gb_global Type *t_type_info_map_ptr = nullptr; gb_global Type *t_type_info_bit_field_ptr = nullptr; gb_global Type *t_type_info_bit_set_ptr = nullptr; gb_global Type *t_type_info_opaque_ptr = nullptr; +gb_global Type *t_type_info_simd_vector_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -496,6 +503,8 @@ gb_global Type *t_source_code_location_ptr = nullptr; gb_global Type *t_map_key = nullptr; gb_global Type *t_map_header = nullptr; +gb_global Type *t_vector_x86_mmx = nullptr; + i64 type_size_of (Type *t); @@ -722,6 +731,13 @@ Type *alloc_type_bit_set() { +Type *alloc_type_simd_vector(i64 count, Type *elem) { + Type *t = alloc_type(Type_SimdVector); + t->SimdVector.count = count; + t->SimdVector.elem = elem; + return t; +} + //////////////////////////////////////////////////////////////// @@ -971,6 +987,11 @@ bool is_type_generic(Type *t) { return t->kind == Type_Generic; } +bool is_type_simd_vector(Type *t) { + t = base_type(t); + return t->kind == Type_SimdVector; +} + Type *core_array_type(Type *t) { for (;;) { @@ -1193,6 +1214,25 @@ Type *bit_set_to_int(Type *t) { return nullptr; } +bool is_type_valid_vector_elem(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + if (t->Basic.flags & BasicFlag_EndianLittle) { + return false; + } + if (t->Basic.flags & BasicFlag_EndianBig) { + return false; + } + if (is_type_integer(t)) { + return true; + } + if (is_type_float(t)) { + return true; + } + } + return false; +} + bool is_type_indexable(Type *t) { Type *bt = base_type(t); @@ -1637,6 +1677,18 @@ bool are_types_identical(Type *x, Type *y) { are_types_identical(x->Map.value, y->Map.value); } break; + + case Type_SimdVector: + if (y->kind == Type_SimdVector) { + if (x->SimdVector.is_x86_mmx == y->SimdVector.is_x86_mmx) { + if (x->SimdVector.is_x86_mmx) { + return true; + } else if (x->SimdVector.count == y->SimdVector.count) { + return are_types_identical(x->SimdVector.elem, y->SimdVector.elem); + } + } + } + break; } return false; @@ -1681,65 +1733,6 @@ 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_DynamicArray: - return false; - case Type_Map: - return false; - - case Type_Slice: - return false; - - case Type_Struct: { - if (type->Struct.is_raw_union) { - return false; - } - for_array(i, type->Struct.fields) { - Entity *v = type->Struct.fields[i]; - if (!is_type_cte_safe(v->type)) { - return false; - } - } - return true; - } - - case Type_Tuple: { - for_array(i, type->Tuple.variables) { - 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; -} - */ i64 union_variant_index(Type *u, Type *v) { u = base_type(u); GB_ASSERT(u->kind == Type_Union); @@ -2389,7 +2382,18 @@ i64 type_align_of_internal(Type *t, TypePath *path) { if (bits <= 32) return 4; if (bits <= 64) return 8; return 8; // NOTE(bill): Could be an invalid range so limit it for now + } + case Type_SimdVector: { + if (t->SimdVector.is_x86_mmx) { + return 8; + } + // align of + i64 count = t->SimdVector.count; + Type *elem = t->SimdVector.elem; + i64 size = count * type_size_of_internal(elem, path); + // IMPORTANT TODO(bill): Figure out the alignment of vector types + return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); } } @@ -2622,6 +2626,15 @@ i64 type_size_of_internal(Type *t, TypePath *path) { if (bits <= 64) return 8; return 8; // NOTE(bill): Could be an invalid range so limit it for now } + + case Type_SimdVector: { + if (t->SimdVector.is_x86_mmx) { + return 8; + } + i64 count = t->SimdVector.count; + Type *elem = t->SimdVector.elem; + return count * type_size_of_internal(elem, path); + } } // Catch all @@ -2950,6 +2963,17 @@ gbString write_type_to_string(gbString str, Type *type) { } str = gb_string_appendc(str, "]"); break; + + case Type_SimdVector: + if (type->SimdVector.is_x86_mmx) { + return "intrinsics.x86_mmx"; + } else { + str = gb_string_appendc(str, "intrinsics.vector("); + str = gb_string_append_fmt(str, "%d, ", cast(int)type->SimdVector.count); + str = write_type_to_string(str, type->SimdVector.elem); + str = gb_string_appendc(str, ")"); + } + break; } return str; From 684945ea57da61c82ab5425930c5571157359cfb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 17:45:30 +0000 Subject: [PATCH 07/32] Fix calling conventions for simd vector types --- src/check_type.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 953eebdda..8b6b67e65 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1790,6 +1790,10 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type) { return new_type; } + if (is_type_simd_vector(original_type)) { + return new_type; + } + if (build_context.ODIN_OS == "windows") { // NOTE(bill): Changing the passing parameter value type is to match C's ABI // IMPORTANT TODO(bill): This only matches the ABI on MSVC at the moment @@ -1893,7 +1897,11 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type) { } GB_ASSERT(is_type_tuple(original_type)); + Type *single_type = reduce_tuple_to_single_type(original_type); + if (is_type_simd_vector(single_type)) { + return new_type; + } if (build_context.ODIN_OS == "windows") { Type *bt = core_type(reduce_tuple_to_single_type(original_type)); @@ -1941,15 +1949,16 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type) { return new_type; } -bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type) { +bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type) { if (abi_return_type == nullptr) { return false; } - // switch (cc) { - // case ProcCC_Odin: - // case ProcCC_Contextless: - // return false; - // } + + Type *single_type = reduce_tuple_to_single_type(abi_return_type); + + if (is_type_simd_vector(single_type)) { + return false; + } if (build_context.ODIN_OS == "windows") { @@ -2075,7 +2084,7 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, // NOTE(bill): The types are the same type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(c->allocator, type->Proc.results); - type->Proc.return_by_pointer = abi_compat_return_by_value(c->allocator, pt->calling_convention, type->Proc.abi_compat_result_type); + type->Proc.return_by_pointer = abi_compat_return_by_pointer(c->allocator, pt->calling_convention, type->Proc.abi_compat_result_type); return success; } From 38ae2e9efaf8d227a138d749085599e7ee9fde54 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 18:05:41 +0000 Subject: [PATCH 08/32] Allow basic arithmetic operations for vectors --- src/ir_print.cpp | 2 +- src/types.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 0cc9f52e3..af5abe180 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1700,7 +1700,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { case irInstr_BinaryOp: { irInstrBinaryOp *bo = &value->Instr.BinaryOp; Type *type = base_type(ir_type(bo->left)); - Type *elem_type = type; + Type *elem_type = base_array_type(type); ir_fprintf(f, "%%%d = ", value->index); diff --git a/src/types.cpp b/src/types.cpp index 6dcbf029f..c6886e984 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -974,11 +974,20 @@ bool is_type_poly_proc(Type *t) { t = base_type(t); return t->kind == Type_Proc && t->Proc.is_polymorphic; } +bool is_type_simd_vector(Type *t) { + t = base_type(t); + return t->kind == Type_SimdVector; +} + Type *base_array_type(Type *t) { if (is_type_array(t)) { t = base_type(t); return t->Array.elem; } + if (is_type_simd_vector(t)) { + t = base_type(t); + return t->SimdVector.elem; + } return t; } @@ -987,10 +996,6 @@ bool is_type_generic(Type *t) { return t->kind == Type_Generic; } -bool is_type_simd_vector(Type *t) { - t = base_type(t); - return t->kind == Type_SimdVector; -} Type *core_array_type(Type *t) { From e551d2b25ea39afb95f7b8ee4309ef0cc8b502b8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 21:39:47 +0000 Subject: [PATCH 09/32] Replace `foreign export {}` with `@export` --- src/check_decl.cpp | 11 +++++----- src/check_expr.cpp | 16 ++++++++++++++ src/check_type.cpp | 6 +++++- src/checker.cpp | 54 ++++++++++++++++++++++++++++++++++------------ src/checker.hpp | 2 +- src/parser.cpp | 10 ++------- src/tokenizer.cpp | 1 - 7 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 91fd0ff01..e9d6d5860 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -551,20 +551,20 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { check_procedure_type(&tmp_ctx, proc_type, pl->type); TypeProc *pt = &proc_type->Proc; - - bool is_foreign = e->Procedure.is_foreign; - bool is_export = e->Procedure.is_export; - bool is_require_results = (pl->tags & ProcTag_require_results) != 0; - AttributeContext ac = make_attribute_context(e->Procedure.link_prefix); if (d != nullptr) { check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac); } + e->Procedure.is_export = ac.is_export; e->deprecated_message = ac.deprecated_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + bool is_foreign = e->Procedure.is_foreign; + bool is_export = e->Procedure.is_export; + bool is_require_results = (pl->tags & ProcTag_require_results) != 0; + if (e->pkg != nullptr && e->token.string == "main") { if (pt->param_count != 0 || pt->result_count != 0) { @@ -718,6 +718,7 @@ void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_ex check_decl_attributes(ctx, decl->attributes, var_decl_attribute, &ac); } + e->Variable.is_export = ac.is_export; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); e->Variable.thread_local_model = ac.thread_local_model; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 75c16b705..ea125a5eb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1241,6 +1241,14 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); return false; } + if (is_type_simd_vector(o->type)) { + switch (op.kind) { + case Token_ModMod: + case Token_ModModEq: + error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); + return false; + } + } break; case Token_AndNot: @@ -1249,6 +1257,14 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string)); return false; } + if (is_type_simd_vector(o->type)) { + switch (op.kind) { + case Token_AndNot: + case Token_AndNotEq: + error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); + return false; + } + } break; case Token_CmpAnd: diff --git a/src/check_type.cpp b/src/check_type.cpp index 8b6b67e65..1c5d5ac85 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1783,7 +1783,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type) { Type *new_type = original_type; if (is_type_boolean(original_type)) { - return t_llvm_bool; + Type *t = core_type(base_type(new_type)); + if (t == t_bool) { + return t_llvm_bool; + } + return new_type; } if (build_context.ODIN_ARCH == "386") { diff --git a/src/checker.cpp b/src/checker.cpp index 7900555a5..9bbe64839 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1952,7 +1952,18 @@ DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { } DECL_ATTRIBUTE_PROC(proc_decl_attribute) { - if (name == "deferred") { + if (name == "export") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Invalid) { + ac->is_export = true; + } else if (ev.kind == ExactValue_Bool) { + ac->is_export = ev.value_bool; + } else { + error(value, "Expected either a boolean or no parameter for 'export'"); + return false; + } + return true; + } else if (name == "deferred") { if (value != nullptr) { Operand o = {}; check_expr(c, &o, value); @@ -2064,7 +2075,20 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) { return true; } - if (name == "link_name") { + if (name == "export") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Invalid) { + ac->is_export = true; + } else if (ev.kind == ExactValue_Bool) { + ac->is_export = ev.value_bool; + } else { + error(value, "Expected either a boolean or no parameter for 'export'"); + return false; + } + if (ac->thread_local_model != "") { + error(elem, "An exported variable cannot be thread local"); + } + } else if (name == "link_name") { if (ev.kind == ExactValue_String) { ac->link_name = ev.value_string; if (!is_foreign_name_valid(ac->link_name)) { @@ -2087,8 +2111,10 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) { } else if (name == "thread_local") { if (ac->init_expr_list_count > 0) { error(elem, "A thread local variable declaration cannot have initialization values"); - } else if (c->foreign_context.curr_library || c->foreign_context.in_export) { + } else if (c->foreign_context.curr_library) { error(elem, "A foreign block variable cannot be thread local"); + } else if (ac->is_export) { + error(elem, "An exported variable cannot be thread local"); } else if (ev.kind == ExactValue_Invalid) { ac->thread_local_model = str_lit("default"); } else if (ev.kind == ExactValue_String) { @@ -2151,9 +2177,17 @@ void check_decl_attributes(CheckerContext *c, Array const &attributes, De case_ast_node(i, Ident, elem); name = i->token.string; case_end; + case_ast_node(i, Implicit, elem); + name = i->string; + case_end; case_ast_node(fv, FieldValue, elem); - GB_ASSERT(fv->field->kind == Ast_Ident); - name = fv->field->Ident.token.string; + if (fv->field->kind == Ast_Ident) { + name = fv->field->Ident.token.string; + } else if (fv->field->kind == Ast_Implicit) { + name = fv->field->Implicit.string; + } else { + GB_PANIC("Unknown Field Value name"); + } value = fv->value; case_end; default: @@ -2407,9 +2441,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->Variable.foreign_library_ident = fl; e->Variable.link_prefix = c->foreign_context.link_prefix; - - } else if (c->foreign_context.in_export) { - e->Variable.is_export = true; } Ast *init_expr = value; @@ -2475,9 +2506,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; - - } else if (c->foreign_context.in_export) { - e->Procedure.is_export = true; } d->proc_lit = init; d->type_expr = pl->type; @@ -2516,7 +2544,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { } if (e->kind != Entity_Procedure) { - if (fl != nullptr || c->foreign_context.in_export) { + if (fl != nullptr) { AstKind kind = init->kind; error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind])); if (kind == Ast_ProcType) { @@ -2544,8 +2572,6 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) { CheckerContext c = *ctx; if (foreign_library->kind == Ast_Ident) { c.foreign_context.curr_library = foreign_library; - } else if (foreign_library->kind == Ast_Implicit && foreign_library->Implicit.kind == Token_export) { - c.foreign_context.in_export = true; } else { error(foreign_library, "Foreign block name must be an identifier or 'export'"); c.foreign_context.curr_library = nullptr; diff --git a/src/checker.hpp b/src/checker.hpp index de491671e..759e343bf 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -305,6 +305,7 @@ struct DeferredProcedure { struct AttributeContext { + bool is_export; String link_name; String link_prefix; isize init_expr_list_count; @@ -423,7 +424,6 @@ struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; - bool in_export; }; typedef Array CheckerTypePath; diff --git a/src/parser.cpp b/src/parser.cpp index e38c34a0a..9d42b3828 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1199,7 +1199,6 @@ void fix_advance_to_next_stmt(AstFile *f) { case Token_package: case Token_foreign: case Token_import: - case Token_export: case Token_if: case Token_for: @@ -2452,9 +2451,7 @@ void parse_foreign_block_decl(AstFile *f, Array *decls) { Ast *parse_foreign_block(AstFile *f, Token token) { CommentGroup *docs = f->lead_comment; Ast *foreign_library = nullptr; - if (f->curr_token.kind == Token_export) { - foreign_library = ast_implicit(f, expect_token(f, Token_export)); - } else if (f->curr_token.kind == Token_OpenBrace) { + if (f->curr_token.kind == Token_OpenBrace) { foreign_library = ast_ident(f, blank_token); } else { foreign_library = parse_ident(f); @@ -3590,7 +3587,6 @@ Ast *parse_foreign_decl(AstFile *f) { Token token = expect_token(f, Token_foreign); switch (f->curr_token.kind) { - case Token_export: case Token_Ident: case Token_OpenBrace: return parse_foreign_block(f, token); @@ -3667,6 +3663,7 @@ Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind clo f->curr_token.kind != Token_EOF) { Ast *elem = nullptr; elem = parse_ident(f); + if (f->curr_token.kind == Token_Eq) { Token eq = expect_token(f, Token_Eq); Ast *value = parse_value(f); @@ -3732,9 +3729,6 @@ Ast *parse_stmt(AstFile *f) { case Token_import: return parse_import_decl(f, ImportDecl_Standard); - // case Token_export: - // return parse_export_decl(f); - case Token_if: return parse_if_stmt(f); case Token_when: return parse_when_stmt(f); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 02770e371..31afe3d2e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -82,7 +82,6 @@ TOKEN_KIND(Token__OperatorEnd, ""), \ \ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_import, "import"), \ - TOKEN_KIND(Token_export, "export"), \ TOKEN_KIND(Token_foreign, "foreign"), \ TOKEN_KIND(Token_package, "package"), \ TOKEN_KIND(Token_typeid, "typeid"), \ From a9ab90bd2488783c3523fa30315f9754937fd52e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 22:17:27 +0000 Subject: [PATCH 10/32] Make `static` an attribute rather than a keyword prefix --- core/strings/builder.odin | 4 ++-- src/check_decl.cpp | 9 ++++++-- src/check_stmt.cpp | 17 +++++++++----- src/checker.cpp | 20 +++++++---------- src/checker.hpp | 1 + src/ir.cpp | 11 ++++++++- src/parser.cpp | 47 +++++++++++++++++++-------------------- src/parser.hpp | 1 - src/tokenizer.cpp | 1 - 9 files changed, 63 insertions(+), 48 deletions(-) diff --git a/core/strings/builder.odin b/core/strings/builder.odin index e86d748cc..547c456ba 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -61,8 +61,8 @@ write_bytes :: proc(b: ^Builder, x: []byte) { append(&b.buf, ..x); } -@(private) -static DIGITS_LOWER := "0123456789abcdefx"; +@(private, static) +DIGITS_LOWER := "0123456789abcdefx"; write_quoted_string :: proc(b: ^Builder, s: string, quote: byte = '"') { write_byte(b, quote); diff --git a/src/check_decl.cpp b/src/check_decl.cpp index e9d6d5860..c2796e1da 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -718,9 +718,14 @@ void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_ex check_decl_attributes(ctx, decl->attributes, var_decl_attribute, &ac); } - e->Variable.is_export = ac.is_export; - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); e->Variable.thread_local_model = ac.thread_local_model; + e->Variable.is_export = ac.is_export; + if (ac.is_static) { + e->flags |= EntityFlag_Static; + } else { + e->flags &= ~EntityFlag_Static; + } + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); String context_name = str_lit("variable declaration"); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3262aea5d..c861cfdf3 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1665,8 +1665,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (!is_blank_ident(str)) { found = scope_lookup_current(ctx->scope, str); new_name_count += 1; - } else if (vd->is_static) { - error(name, "'static' is now allowed to be applied to '_'"); } if (found == nullptr) { entity = alloc_entity_variable(ctx->scope, token, nullptr, false); @@ -1678,9 +1676,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { entity->Variable.is_foreign = true; entity->Variable.foreign_library_ident = fl; } - if (vd->is_static) { - entity->flags |= EntityFlag_Static; - } } else { TokenPos pos = found->token.pos; error(token, @@ -1744,6 +1739,16 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; } + + e->flags &= ~EntityFlag_Static; + if (ac.is_static) { + String name = e->token.string; + if (name == "_") { + error(e->token, "The 'static' attribute is not allowed to be applied to '_'"); + } else { + e->flags |= EntityFlag_Static; + } + } } check_arity_match(ctx, vd); @@ -1751,6 +1756,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { for (isize i = 0; i < entity_count; i++) { Entity *e = entities[i]; + if (e->Variable.is_foreign) { if (vd->values.count > 0) { error(e->token, "A foreign variable declaration cannot have a default value"); @@ -1842,6 +1848,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } } + } else { // constant value declaration // NOTE(bill): Check `_` declarations diff --git a/src/checker.cpp b/src/checker.cpp index 9bbe64839..6389e3d30 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2070,6 +2070,14 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { DECL_ATTRIBUTE_PROC(var_decl_attribute) { ExactValue ev = check_decl_attribute_value(c, value); + if (name == "static") { + if (value != nullptr) { + error(elem, "'static' does not have any parameters"); + } + ac->is_static = true; + return true; + } + if (c->curr_proc_decl != nullptr) { error(elem, "Only a variable at file scope can have a '%.*s'", LIT(name)); return true; @@ -2425,10 +2433,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->flags |= EntityFlag_NotExported; } - if (vd->is_static) { - e->flags |= EntityFlag_Static; - } - if (vd->is_using) { vd->is_using = false; // NOTE(bill): This error will be only caught once error(name, "'using' is not allowed at the file scope"); @@ -2527,14 +2531,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->flags |= EntityFlag_NotExported; } - if (vd->is_static) { - if (e->kind == Entity_Constant) { - e->flags |= EntityFlag_Static; - } else { - error(name, "'static' is not allowed on this constant value declaration"); - } - } - if (vd->is_using) { if (e->kind == Entity_TypeName && init->kind == Ast_EnumType) { d->is_using = true; diff --git a/src/checker.hpp b/src/checker.hpp index 759e343bf..4420d6bb4 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -306,6 +306,7 @@ struct DeferredProcedure { struct AttributeContext { bool is_export; + bool is_static; String link_name; String link_prefix; isize init_expr_list_count; diff --git a/src/ir.cpp b/src/ir.cpp index 4df6665f7..e92fa7f60 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8166,7 +8166,16 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { if (vd->is_mutable) { irModule *m = proc->module; - if (vd->is_static) { + bool is_static = false; + if (vd->names.count > 0) { + Entity *e = entity_of_ident(vd->names[0]); + if (e->flags & EntityFlag_Static) { + // NOTE(bill): If one of the entities is static, they all are + is_static = true; + } + } + + if (is_static) { for_array(i, vd->names) { irValue *value = nullptr; if (vd->values.count > 0) { diff --git a/src/parser.cpp b/src/parser.cpp index 9d42b3828..a5526fd73 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1208,7 +1208,6 @@ void fix_advance_to_next_stmt(AstFile *f) { case Token_defer: case Token_asm: case Token_using: - case Token_static: case Token_break: case Token_continue: @@ -3751,33 +3750,33 @@ Ast *parse_stmt(AstFile *f) { return s; } - case Token_static: { - CommentGroup *docs = f->lead_comment; - Token token = expect_token(f, Token_static); + // case Token_static: { + // CommentGroup *docs = f->lead_comment; + // Token token = expect_token(f, Token_static); - Ast *decl = nullptr; - Array list = parse_lhs_expr_list(f); - if (list.count == 0) { - syntax_error(token, "Illegal use of 'static' statement"); - expect_semicolon(f, nullptr); - return ast_bad_stmt(f, token, f->curr_token); - } + // Ast *decl = nullptr; + // Array list = parse_lhs_expr_list(f); + // if (list.count == 0) { + // syntax_error(token, "Illegal use of 'static' statement"); + // expect_semicolon(f, nullptr); + // return ast_bad_stmt(f, token, f->curr_token); + // } - expect_token_after(f, Token_Colon, "identifier list"); - decl = parse_value_decl(f, list, docs); + // expect_token_after(f, Token_Colon, "identifier list"); + // decl = parse_value_decl(f, list, docs); - if (decl != nullptr && decl->kind == Ast_ValueDecl) { - if (decl->ValueDecl.is_mutable) { - decl->ValueDecl.is_static = true; - } else { - error(token, "'static' may only be currently used with variable declaration"); - } - return decl; - } + // if (decl != nullptr && decl->kind == Ast_ValueDecl) { + // if (decl->ValueDecl.is_mutable) { + // decl->ValueDecl.is_static = true; + // } else { + // error(token, "'static' may only be currently used with variable declaration"); + // } + // return decl; + // } - syntax_error(token, "Illegal use of 'static' statement"); - return ast_bad_stmt(f, token, f->curr_token); - } break; + // syntax_error(token, "Illegal use of 'static' statement"); + // return ast_bad_stmt(f, token, f->curr_token); + // } break; case Token_using: { CommentGroup *docs = f->lead_comment; diff --git a/src/parser.hpp b/src/parser.hpp index 13dd1258c..d685aa1af 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -376,7 +376,6 @@ AST_KIND(_DeclBegin, "", bool) \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ - bool is_static; \ bool is_using; \ bool is_mutable; \ }) \ diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 31afe3d2e..11fae7120 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -107,7 +107,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_bit_field, "bit_field"), \ TOKEN_KIND(Token_bit_set, "bit_set"), \ TOKEN_KIND(Token_map, "map"), \ - TOKEN_KIND(Token_static, "static"), \ TOKEN_KIND(Token_dynamic, "dynamic"), \ TOKEN_KIND(Token_auto_cast, "auto_cast"), \ TOKEN_KIND(Token_cast, "cast"), \ From 2878cd8241d6cbb57a7a2f927be7407d11ad80d8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 23:21:27 +0000 Subject: [PATCH 11/32] New build flag: -define:foo=123 --- src/build_settings.cpp | 2 + src/checker.cpp | 38 ++++++++++++++ src/main.cpp | 114 +++++++++++++++++++++++++++++------------ src/string.cpp | 1 - 4 files changed, 120 insertions(+), 35 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ca9a77902..d7b85644d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -110,6 +110,8 @@ struct BuildContext { gbAffinity affinity; isize thread_count; + + Map defined_values; // Key: }; diff --git a/src/checker.cpp b/src/checker.cpp index 6389e3d30..907dbd26c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -721,6 +721,44 @@ void init_universal(void) { add_global_entity(entity, intrinsics_pkg->scope); } + bool defined_values_double_declaration = true; + for_array(i, bc->defined_values.entries) { + String name = bc->defined_values.entries[i].key.string; + ExactValue value = bc->defined_values.entries[i].value; + GB_ASSERT(value.kind != ExactValue_Invalid); + + Type *type = nullptr; + switch (value.kind) { + case ExactValue_Bool: + type = t_untyped_bool; + break; + case ExactValue_String: + type = t_untyped_string; + break; + case ExactValue_Integer: + type = t_untyped_integer; + break; + case ExactValue_Float: + type = t_untyped_float; + break; + } + GB_ASSERT(type != nullptr); + + + + Entity *entity = alloc_entity_constant(nullptr, make_token_ident(name), type, value); + entity->state = EntityState_Resolved; + if (scope_insert(builtin_pkg->scope, entity)) { + error(entity->token, "'%.*s' defined as an argument is already declared at the global scope", LIT(name)); + defined_values_double_declaration = true; + // NOTE(bill): Just exit early before anything, even though the compiler will do that anyway + } + } + + if (defined_values_double_declaration) { + gb_exit(1); + } + t_u8_ptr = alloc_type_pointer(t_u8); t_int_ptr = alloc_type_pointer(t_int); diff --git a/src/main.cpp b/src/main.cpp index 51a6856d1..fb88e9c34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,10 +2,10 @@ #include "common.cpp" #include "timings.cpp" -#include "build_settings.cpp" #include "tokenizer.cpp" #include "big_int.cpp" #include "exact_value.cpp" +#include "build_settings.cpp" #include "parser.hpp" #include "checker.hpp" @@ -207,6 +207,7 @@ enum BuildFlagKind { BuildFlag_ThreadCount, BuildFlag_KeepTempFiles, BuildFlag_Collection, + BuildFlag_Define, BuildFlag_BuildMode, BuildFlag_Debug, BuildFlag_CrossCompile, @@ -242,6 +243,41 @@ void add_flag(Array *build_flags, BuildFlagKind kind, String name, Bu array_add(build_flags, flag); } +ExactValue build_param_to_exact_value(String name, String param) { + ExactValue value = {}; + if (str_eq_ignore_case(param, str_lit("t")) || + str_eq_ignore_case(param, str_lit("true"))) { + value = exact_value_bool(true); + } else if (str_eq_ignore_case(param, str_lit("f")) || + str_eq_ignore_case(param, str_lit("false"))) { + value = exact_value_bool(false); + } else if (param.len > 0) { + if (param[0] == '"') { + value = exact_value_string(param); + if (value.kind == ExactValue_String) { + String s = value.value_string; + if (s.len > 1 && s[0] == '"' && s[s.len-1] == '"') { + value.value_string = substring(s, 1, s.len-1); + } + } + } else if (param[0] == '-' || param[0] == '+' || gb_is_between(param[0], '0', '9')) { + if (string_contains_char(param, '.')) { + value = exact_value_float_from_string(param); + } else { + value = exact_value_integer_from_string(param); + } + if (value.kind == ExactValue_Invalid) { + gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param)); + } + } + } else { + gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param)); + } + + return value; +} + + bool parse_build_flags(Array args) { auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT); add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String); @@ -251,6 +287,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer); add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String); + add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_CrossCompile, str_lit("cross-compile"), BuildFlagParam_String); @@ -276,7 +313,8 @@ bool parse_build_flags(Array args) { String name = substring(flag, 1, flag.len); isize end = 0; for (; end < name.len; end++) { - if (name[end] == '=') break; + if (name[end] == ':') break; + if (name[end] == '=') break; // IMPORTANT TODO(bill): DEPRECATE THIS!!!! } name = substring(name, 0, end); String param = {}; @@ -306,7 +344,9 @@ bool parse_build_flags(Array args) { } else { ok = true; switch (bf.param_kind) { - default: ok = false; break; + default: + ok = false; + break; case BuildFlagParam_Boolean: { if (str_eq_ignore_case(param, str_lit("t")) || str_eq_ignore_case(param, str_lit("true")) || @@ -465,7 +505,7 @@ bool parse_build_flags(Array args) { break; } - case BuildFlag_Collection: { + case BuildFlag_Define: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; isize eq_pos = -1; @@ -476,59 +516,50 @@ bool parse_build_flags(Array args) { } } if (eq_pos < 0) { - gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param)); + gb_printf_err("Expected 'name=value', got '%.*s'\n", LIT(param)); bad_flags = true; break; } String name = substring(str, 0, eq_pos); - String path = substring(str, eq_pos+1, str.len); - if (name.len == 0 || path.len == 0) { - gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param)); + String value = substring(str, eq_pos+1, str.len); + if (name.len == 0 || value.len == 0) { + gb_printf_err("Expected 'name=value', got '%.*s'\n", LIT(param)); bad_flags = true; break; } if (!string_is_valid_identifier(name)) { - gb_printf_err("Library collection name '%.*s' must be a valid identifier\n", LIT(name)); + gb_printf_err("Defined constant name '%.*s' must be a valid identifier\n", LIT(name)); bad_flags = true; break; } if (name == "_") { - gb_printf_err("Library collection name cannot be an underscore\n"); + gb_printf_err("Defined constant name cannot be an underscore\n"); bad_flags = true; break; } - if (name == "system") { - gb_printf_err("Library collection name 'system' is reserved\n"); + HashKey key = hash_string(name); + + if (map_get(&build_context.defined_values, key) != nullptr) { + gb_printf_err("Defined constant '%.*s' already exists\n", LIT(name)); bad_flags = true; break; } - String prev_path = {}; - bool found = find_library_collection_path(name, &prev_path); - if (found) { - gb_printf_err("Library collection '%.*s' already exists with path '%.*s'\n", LIT(name), LIT(prev_path)); + ExactValue v = build_param_to_exact_value(name, value); + if (v.kind != ExactValue_Invalid) { + map_set(&build_context.defined_values, key, v); + } else { bad_flags = true; - break; } - gbAllocator a = heap_allocator(); - String fullpath = path_to_fullpath(a, path); - if (!path_is_directory(fullpath)) { - gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath)); - gb_free(a, fullpath.text); - bad_flags = true; - break; - } - - add_library_collection(name, path); - - // NOTE(bill): Allow for multiple library collections - continue; + break; } + + case BuildFlag_BuildMode: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; @@ -599,8 +630,13 @@ void show_timings(Checker *c, Timings *t) { isize tokens = p->total_token_count; isize files = 0; isize packages = p->packages.count; + isize total_file_size = 0; for_array(i, p->packages) { files += p->packages[i]->files.count; + for_array(j, p->packages[i]->files) { + AstFile *file = p->packages[i]->files[j]; + total_file_size += file->tokenizer.end - file->tokenizer.start; + } } #if 1 timings_print_all(t); @@ -608,10 +644,11 @@ void show_timings(Checker *c, Timings *t) { { timings_print_all(t); gb_printf("\n"); - gb_printf("Total Lines - %td\n", lines); - gb_printf("Total Tokens - %td\n", tokens); - gb_printf("Total Files - %td\n", files); - gb_printf("Total Packages - %td\n", packages); + gb_printf("Total Lines - %td\n", lines); + gb_printf("Total Tokens - %td\n", tokens); + gb_printf("Total Files - %td\n", files); + gb_printf("Total Packages - %td\n", packages); + gb_printf("Total File Size - %td\n", total_file_size); gb_printf("\n"); } { @@ -623,6 +660,9 @@ void show_timings(Checker *c, Timings *t) { gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); + gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); + gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); + gb_printf("\n"); } { @@ -634,6 +674,8 @@ void show_timings(Checker *c, Timings *t) { gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); + gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); + gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); gb_printf("\n"); } { @@ -643,6 +685,8 @@ void show_timings(Checker *c, Timings *t) { gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines); gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time); gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens); + gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/total_time); + gb_printf("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size); gb_printf("\n"); } #endif @@ -741,6 +785,8 @@ int main(int arg_count, char **arg_ptr) { // NOTE(bill): 'core' cannot be (re)defined by the user add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core"))); + map_init(&build_context.defined_values, heap_allocator()); + Array args = setup_args(arg_count, arg_ptr); String command = args[1]; diff --git a/src/string.cpp b/src/string.cpp index 5f4a28960..39219789f 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -308,7 +308,6 @@ String directory_from_path(String const &s) { return substring(s, 0, i); } - String concatenate_strings(gbAllocator a, String const &x, String const &y) { isize len = x.len+y.len; u8 *data = gb_alloc_array(a, u8, len+1); From 989cc893ef2aa770e788f4e56adbe3c8311fdc57 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 23:25:46 +0000 Subject: [PATCH 12/32] FIX TYPO! --- src/checker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index 907dbd26c..0253bc86b 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -721,7 +721,7 @@ void init_universal(void) { add_global_entity(entity, intrinsics_pkg->scope); } - bool defined_values_double_declaration = true; + bool defined_values_double_declaration = false; for_array(i, bc->defined_values.entries) { String name = bc->defined_values.entries[i].key.string; ExactValue value = bc->defined_values.entries[i].value; From cdfaa643ccdc54e0fba3bc43536f33e5ae8debac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Feb 2019 23:30:03 +0000 Subject: [PATCH 13/32] Reimplement -collection; remove `static` from Odin tokenizer/parser in core library --- build.bat | 4 +-- core/odin/ast/ast.odin | 1 - core/odin/parser/parser.odin | 31 ----------------- core/odin/token/token.odin | 2 -- src/main.cpp | 65 ++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 36 deletions(-) diff --git a/build.bat b/build.bat index 402d8842c..58ae69f93 100644 --- a/build.bat +++ b/build.bat @@ -42,8 +42,8 @@ del *.ilk > NUL 2> NUL cl %compiler_settings% "src\main.cpp" ^ /link %linker_settings% -OUT:%exe_name% ^ - && odin run examples/demo/demo.odin + && odin run examples/demo/demo.odin -keep-temp-files del *.obj > NUL 2> NUL -:end_of_build \ No newline at end of file +:end_of_build diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 784c89e6a..8c43ee8e3 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -365,7 +365,6 @@ Value_Decl :: struct { type: ^Expr, values: []^Expr, comment: ^Comment_Group, - is_static: bool, is_using: bool, is_mutable: bool, } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 7c7a816c9..4c43a20e5 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1033,37 +1033,6 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { expect_semicolon(p, s); return s; - case token.Static: - docs := p.lead_comment; - tok := expect_token(p, token.Static); - - list := parse_lhs_expr_list(p); - if len(list) == 0 { - error(p, tok.pos, "illegal use of 'static' statement"); - expect_semicolon(p, nil); - return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); - } - - expect_token_after(p, token.Colon, "identifier list"); - decl := parse_value_decl(p, list, docs); - if decl != nil do switch d in &decl.derived { - case ast.Value_Decl: - if d.is_mutable { - d.is_static = true; - } else { - error(p, tok.pos, "'static' may only be currently used with variable declarations"); - } - case: - error(p, tok.pos, "illegal use of 'static' statement"); - } - - error(p, tok.pos, "illegal use of 'static' statement"); - if decl != nil { - return decl; - } - - return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); - case token.Using: docs := p.lead_comment; tok := expect_token(p, token.Using); diff --git a/core/odin/token/token.odin b/core/odin/token/token.odin index 46b3fc846..f52f6301e 100644 --- a/core/odin/token/token.odin +++ b/core/odin/token/token.odin @@ -139,7 +139,6 @@ using Kind :: enum u16 { Bit_Field, Bit_Set, Map, - Static, Dynamic, Auto_Cast, Cast, @@ -274,7 +273,6 @@ tokens := [Kind.COUNT]string { "bit_field", "bit_set", "map", - "static", "dynamic", "auto_cast", "cast", diff --git a/src/main.cpp b/src/main.cpp index fb88e9c34..beda80cad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -505,6 +505,71 @@ bool parse_build_flags(Array args) { break; } + case BuildFlag_Collection: { + GB_ASSERT(value.kind == ExactValue_String); + String str = value.value_string; + isize eq_pos = -1; + for (isize i = 0; i < str.len; i++) { + if (str[i] == '=') { + eq_pos = i; + break; + } + } + if (eq_pos < 0) { + gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param)); + bad_flags = true; + break; + } + String name = substring(str, 0, eq_pos); + String path = substring(str, eq_pos+1, str.len); + if (name.len == 0 || path.len == 0) { + gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param)); + bad_flags = true; + break; + } + + if (!string_is_valid_identifier(name)) { + gb_printf_err("Library collection name '%.*s' must be a valid identifier\n", LIT(name)); + bad_flags = true; + break; + } + + if (name == "_") { + gb_printf_err("Library collection name cannot be an underscore\n"); + bad_flags = true; + break; + } + + if (name == "system") { + gb_printf_err("Library collection name 'system' is reserved\n"); + bad_flags = true; + break; + } + + String prev_path = {}; + bool found = find_library_collection_path(name, &prev_path); + if (found) { + gb_printf_err("Library collection '%.*s' already exists with path '%.*s'\n", LIT(name), LIT(prev_path)); + bad_flags = true; + break; + } + + gbAllocator a = heap_allocator(); + String fullpath = path_to_fullpath(a, path); + if (!path_is_directory(fullpath)) { + gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath)); + gb_free(a, fullpath.text); + bad_flags = true; + break; + } + + add_library_collection(name, path); + + // NOTE(bill): Allow for multiple library collections + continue; + } + + case BuildFlag_Define: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; From a0c81c79add95ce7a96b0d943545aa8b6bd71713 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Feb 2019 10:30:58 +0000 Subject: [PATCH 14/32] Fix bugs: Array Literals with constant elements; IR printing of raw procedure types --- src/ir.cpp | 2 +- src/ir_print.cpp | 7 +++++-- src/tokenizer.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e92fa7f60..eba159472 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7360,7 +7360,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { case Type_Array: { if (cl->elems.count > 0) { - // ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr))); + ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr))); auto temp_data = array_make(heap_allocator(), 0, cl->elems.count); defer (array_free(&temp_data)); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index af5abe180..97194e794 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -316,8 +316,11 @@ void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { if (t->Proc.return_by_pointer) { ir_print_type(f, m, reduce_tuple_to_single_type(t->Proc.results)); // ir_fprintf(f, "* sret noalias "); - ir_write_string(f, str_lit("* noalias ")); - if (param_count > 0) ir_write_string(f, str_lit(", ")); + // ir_write_string(f, str_lit("* noalias ")); + ir_write_string(f, str_lit("*")); + if (param_count > 0 || t->Proc.calling_convention == ProcCC_Odin) { + ir_write_string(f, str_lit(", ")); + } } isize param_index = 0; for (isize i = 0; i < param_count; i++) { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 11fae7120..d7a39e824 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -100,7 +100,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_defer, "defer"), \ TOKEN_KIND(Token_return, "return"), \ TOKEN_KIND(Token_proc, "proc"), \ - TOKEN_KIND(Token_macro, "macro"), \ TOKEN_KIND(Token_struct, "struct"), \ TOKEN_KIND(Token_union, "union"), \ TOKEN_KIND(Token_enum, "enum"), \ @@ -121,6 +120,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_align_of, "align_of"), \ TOKEN_KIND(Token_offset_of, "offset_of"), \ TOKEN_KIND(Token_type_of, "type_of"), \ + TOKEN_KIND(Token_macro, "macro"), \ TOKEN_KIND(Token_const, "const"), \ TOKEN_KIND(Token_asm, "asm"), \ TOKEN_KIND(Token_yield, "yield"), \ From 0c8746ada674840980e28211b2e09c30cb811c88 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 25 Feb 2019 12:41:43 +0000 Subject: [PATCH 15/32] Add support for custom keywords in core:odin/* packages --- core/odin/parser/parser.odin | 6 +----- core/odin/token/token.odin | 28 +++++++++++++++++++++++----- core/odin/tokenizer/tokenizer.odin | 10 ++++++++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 4c43a20e5..c10a83e47 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -857,10 +857,6 @@ parse_foreign_block :: proc(p: ^Parser, tok: token.Token) -> ^ast.Foreign_Block_ foreign_library: ^ast.Expr; switch p.curr_tok.kind { - case token.Export: - i := ast.new(ast.Implicit, tok.pos, end_pos(tok)); - i.tok = expect_token(p, token.Export); - foreign_library = i; case token.Open_Brace: i := ast.new(ast.Ident, tok.pos, end_pos(tok)); i.name = "_"; @@ -903,7 +899,7 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { tok := expect_token(p, token.Foreign); switch p.curr_tok.kind { - case token.Export, token.Ident, token.Open_Brace: + case token.Ident, token.Open_Brace: return parse_foreign_block(p, tok); case token.Import: diff --git a/core/odin/token/token.odin b/core/odin/token/token.odin index f52f6301e..7e985d46d 100644 --- a/core/odin/token/token.odin +++ b/core/odin/token/token.odin @@ -28,7 +28,7 @@ pos_compare :: proc(lhs, rhs: Pos) -> int { return strings.compare(lhs.file, rhs.file); } -using Kind :: enum u16 { +using Kind :: enum u32 { Invalid, EOF, Comment, @@ -113,7 +113,6 @@ using Kind :: enum u16 { B_Keyword_Begin, Import, - Export, Foreign, Package, Typeid, @@ -160,6 +159,9 @@ using Kind :: enum u16 { B_Keyword_End, COUNT, + + B_Custom_Keyword_Begin = COUNT+1, + // ... Custom keywords }; tokens := [Kind.COUNT]string { @@ -247,7 +249,6 @@ tokens := [Kind.COUNT]string { "", "import", - "export", "foreign", "package", "typeid", @@ -294,10 +295,19 @@ tokens := [Kind.COUNT]string { "", }; +custom_keyword_tokens: []string; + to_string :: proc(kind: Kind) -> string { - if min(Kind) <= kind && kind <= max(Kind) { + if Invalid <= kind && kind < COUNT { return tokens[kind]; } + if B_Custom_Keyword_Begin < kind { + n := int(u16(kind)-u16(B_Custom_Keyword_Begin)); + if n < len(custom_keyword_tokens) { + return custom_keyword_tokens[n]; + } + } + return "Invalid"; } @@ -314,4 +324,12 @@ is_operator :: proc(kind: Kind) -> bool { is_assignment_operator :: proc(kind: Kind) -> bool { return B_Assign_Op_Begin < kind && kind < B_Assign_Op_End || kind == Eq; } -is_keyword :: proc(kind: Kind) -> bool { return B_Keyword_Begin < kind && kind < B_Keyword_End; } +is_keyword :: proc(kind: Kind) -> bool { + switch { + case B_Keyword_Begin < kind && kind < B_Keyword_End: + return true; + case B_Custom_Keyword_Begin < kind: + return true; + } + return false; +} diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index a009d6ba3..fca475839 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -486,12 +486,18 @@ scan :: proc(t: ^Tokenizer) -> token.Token { case is_letter(ch): lit = scan_identifier(t); kind = token.Ident; - if len(lit) > 1 { + check_keyword: if len(lit) > 1 { // TODO(bill): Maybe have a hash table lookup rather than this linear search for i in token.B_Keyword_Begin .. token.B_Keyword_End { if lit == token.tokens[i] { kind = token.Kind(i); - break; + break check_keyword; + } + } + for keyword, i in token.custom_keyword_tokens { + if lit == keyword { + kind = token.Kind(i+1)+token.B_Custom_Keyword_Begin; + break check_keyword; } } } From 76a6757ee9e838326519ffb30a81dc60667d253d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 25 Feb 2019 18:03:44 +0000 Subject: [PATCH 16/32] Add `os.file_size_from_path` --- core/os/os.odin | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/os/os.odin b/core/os/os.odin index eda3b66cf..9db84ddfe 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -51,6 +51,20 @@ write_encoded_rune :: proc(fd: Handle, r: rune) { } +file_size_from_path :: proc(path: string) -> i64 { + fd, err := open(path, O_RDONLY, 0); + if err != 0 { + return -1; + } + defer close(fd); + + length: i64; + if length, err = file_size(fd); err != 0 { + return -1; + } + return length; +} + read_entire_file :: proc(name: string) -> (data: []byte, success: bool) { fd, err := open(name, O_RDONLY, 0); if err != 0 { From 6faab8e47a09a480880399ea63e716ba51428d9e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 26 Feb 2019 13:51:56 +0100 Subject: [PATCH 17/32] Fix #345: panic when using enum as map key --- src/ir.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index eba159472..d52b4beaf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3215,6 +3215,9 @@ irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) { key = ir_emit_conv(proc, key, key_type); if (is_type_integer(t)) { ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, key, hash_type)); + } else if (is_type_enum(t)) { + irValue *e = ir_emit_bitcast(proc, key, t_uint); + ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, e, hash_type)); } else if (is_type_typeid(t)) { irValue *i = ir_emit_bitcast(proc, key, t_uint); ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, i, hash_type)); From b6d6eb6ae2d22935d146df8bc715c580fe5cb321 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 2 Mar 2019 13:21:01 +0100 Subject: [PATCH 18/32] Fix #345: Panic when using enum as map key Also add a little map demo. --- examples/demo/demo.odin | 58 ++++++++++++++++++++++++++++++++++++++++- src/ir.cpp | 3 +-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index b4c67938f..ad7a2666d 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -530,7 +530,7 @@ parametric_polymorphism :: proc() { // `I` is the type of N // `T` is the type passed fmt.printf("Generating an array of type %v from the value %v of type %v\n", - typeid_of(type_of(res)), N, typeid_of(I)); + typeid_of(type_of(res)), N, typeid_of(I)); for i in 0..N-1 { res[i] = T(i*i); } @@ -691,6 +691,61 @@ using_enum :: proc() { fmt.println(len(Foo)); } +map_type :: proc() { + fmt.println("# map type"); + + // enums of type u16, u32, i16 & i32 also work + Enum_u8 :: enum u8 { + A = 0, + B = 1 << 8 - 1, + } + Enum_u64 :: enum u64 { + A = 0, + B = 1 << 64 - 1, + } + Enum_i32 :: enum i32 { + A = 0, + B = -(1 << 31), + } + Enum_i64 :: enum i64 { + A = 0, + B = -(1 << 63), + } + + map_u8: map[Enum_u8]u8; + map_u8[Enum_u8.A] = u8(Enum_u8.B); + assert(map_u8[Enum_u8.A] == u8(Enum_u8.B)); + fmt.println(map_u8); + + map_u64: map[Enum_u64]u64; + map_u64[Enum_u64.A] = u64(Enum_u64.B); + assert(map_u64[Enum_u64.A] == u64(Enum_u64.B)); + fmt.println(map_u64); + + map_i8: map[Enum_i8]i8; + map_i8[Enum_i8.A] = i8(Enum_i8.B); + assert(map_i8[Enum_i8.A] == i8(Enum_i8.B)); + fmt.println(map_i8); + + map_i64: map[Enum_i64]i64; + map_i64[Enum_i64.A] = i64(Enum_i64.B); + assert(map_i64[Enum_i64.A] == i64(Enum_i64.B)); + fmt.println(map_i64); + + demo_struct :: struct { + member: Enum_i64, + } + + map_string: map[string]demo_struct; + map_string["Hellope!"] = demo_struct{Enum_i64.B}; + assert(map_string["Hellope!"].member == Enum_i64.B); + assert("Hellope?" notin map_string); + fmt.println(map_string); + fmt.println("Hellope! in map_string:", "Hellope!" in map_string); + fmt.println("Hellope? in map_string:", "Hellope?" in map_string); + +} + explicit_procedure_overloading :: proc() { fmt.println("# explicit procedure overloading"); @@ -884,6 +939,7 @@ main :: proc() { array_programming(); named_proc_return_parameters(); using_enum(); + map_type(); explicit_procedure_overloading(); complete_switch(); cstring_example(); diff --git a/src/ir.cpp b/src/ir.cpp index d52b4beaf..e3fe83c3b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3216,8 +3216,7 @@ irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) { if (is_type_integer(t)) { ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, key, hash_type)); } else if (is_type_enum(t)) { - irValue *e = ir_emit_bitcast(proc, key, t_uint); - ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, e, hash_type)); + ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, key, hash_type)); } else if (is_type_typeid(t)) { irValue *i = ir_emit_bitcast(proc, key, t_uint); ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, i, hash_type)); From 7bd86bb3ec8cffe2758dde99f40233e093f6c0ce Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 2 Mar 2019 13:24:11 +0100 Subject: [PATCH 19/32] well, that was a stupid copy/paste bug --- examples/demo/demo.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index ad7a2666d..fd15b2a97 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -703,9 +703,9 @@ map_type :: proc() { A = 0, B = 1 << 64 - 1, } - Enum_i32 :: enum i32 { + Enum_i8 :: enum i8 { A = 0, - B = -(1 << 31), + B = -(1 << 7), } Enum_i64 :: enum i64 { A = 0, From 9b4b20e8b11850dc1c0bbd6751e4d4fc2dc04c1a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 3 Mar 2019 12:08:26 +0000 Subject: [PATCH 20/32] package dynlib --- core/dynlib/lib.odin | 3 +++ core/dynlib/lib_windows.odin | 24 ++++++++++++++++++++++++ core/sys/win32/kernel32.odin | 2 +- core/unicode/utf8/utf8.odin | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 core/dynlib/lib.odin create mode 100644 core/dynlib/lib_windows.odin diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin new file mode 100644 index 000000000..66742b835 --- /dev/null +++ b/core/dynlib/lib.odin @@ -0,0 +1,3 @@ +package dynlib + +Library :: opaque rawptr; diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin new file mode 100644 index 000000000..67ce47cc1 --- /dev/null +++ b/core/dynlib/lib_windows.odin @@ -0,0 +1,24 @@ +package dynlib + +import "core:sys/win32" +import "core:strings" + +load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { + // NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL + + wide_path := win32.utf8_to_wstring(path, context.temp_allocator); + handle := cast(Library)win32.load_library_w(wide_path); + return handle, handle != nil; +} + +unload_library :: proc(library: Library) -> bool { + ok := win32.free_library(cast(win32.Hmodule)library); + return bool(ok); +} + +symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { + c_str := strings.new_cstring(symbol, context.temp_allocator); + ptr = win32.get_proc_address(cast(win32.Hmodule)library, c_str); + found == ptr != nil; + return; +} diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin index 6f9390cc4..33ef0e8b7 100644 --- a/core/sys/win32/kernel32.odin +++ b/core/sys/win32/kernel32.odin @@ -164,7 +164,7 @@ foreign kernel32 { @(link_name="LoadLibraryA") load_library_a :: proc(c_str: cstring) -> Hmodule ---; @(link_name="LoadLibraryW") load_library_w :: proc(c_str: Wstring) -> Hmodule ---; - @(link_name="FreeLibrary") free_library :: proc(h: Hmodule) ---; + @(link_name="FreeLibrary") free_library :: proc(h: Hmodule) -> Bool ---; @(link_name="GetProcAddress") get_proc_address :: proc(h: Hmodule, c_str: cstring) -> rawptr ---; } diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin index 50b33a680..d0396b9e0 100644 --- a/core/unicode/utf8/utf8.odin +++ b/core/unicode/utf8/utf8.odin @@ -90,7 +90,7 @@ encode_rune :: proc(r: rune) -> ([4]u8, int) { buf[0] = 0xf0 | u8(r>>18); buf[1] = 0x80 | u8(r>>12) & mask; buf[2] = 0x80 | u8(r>>6) & mask; - buf[3] = 0x80 | u8(r) & mask; + buf[3] = 0x80 | u8(r) & mask; return buf, 4; } From 1f5ab0b5f134ceb33ec97578a4c899fe03eed28e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 4 Mar 2019 13:41:47 +0100 Subject: [PATCH 21/32] Fix typo in `cel` tokeniser. --- core/encoding/cel/token.odin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/encoding/cel/token.odin b/core/encoding/cel/token.odin index 46c8d61be..9c3bdf715 100644 --- a/core/encoding/cel/token.odin +++ b/core/encoding/cel/token.odin @@ -78,7 +78,7 @@ Token :: struct { } Tokenizer :: struct { - src: []byte, + src: []byte, file: string, // May not be used @@ -281,7 +281,7 @@ digit_value :: proc(r: rune) -> int { } scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) { - scan_manitissa :: proc(t: ^Tokenizer, base: int) { + scan_mantissa :: proc(t: ^Tokenizer, base: int) { for digit_value(t.curr_rune) < base || t.curr_rune == '_' { advance_to_next_rune(t); } @@ -294,7 +294,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) { advance_to_next_rune(t); } if digit_value(t.curr_rune) < 10 { - scan_manitissa(t, 10); + scan_mantissa(t, 10); } else { token_error(t, "Illegal floating point exponent"); } @@ -305,7 +305,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) { if t.curr_rune == '.' { tok = Float; advance_to_next_rune(t); - scan_manitissa(t, 10); + scan_mantissa(t, 10); } return scan_exponent(t, tok, offset); @@ -317,7 +317,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) { if seen_decimal_point { offset -= 1; tok = Float; - scan_manitissa(t, 10); + scan_mantissa(t, 10); return scan_exponent(t, tok, offset); } @@ -327,24 +327,24 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) { switch t.curr_rune { case 'b', 'B': advance_to_next_rune(t); - scan_manitissa(t, 2); + scan_mantissa(t, 2); if t.offset - offset <= 2 { token_error(t, "Illegal binary number"); } case 'o', 'O': advance_to_next_rune(t); - scan_manitissa(t, 8); + scan_mantissa(t, 8); if t.offset - offset <= 2 { token_error(t, "Illegal octal number"); } case 'x', 'X': advance_to_next_rune(t); - scan_manitissa(t, 16); + scan_mantissa(t, 16); if t.offset - offset <= 2 { token_error(t, "Illegal hexadecimal number"); } case: - scan_manitissa(t, 10); + scan_mantissa(t, 10); switch t.curr_rune { case '.', 'e', 'E': return scan_fraction(t, tok, offset); @@ -354,7 +354,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) { return tok, string(t.src[offset:t.offset]); } - scan_manitissa(t, 10); + scan_mantissa(t, 10); return scan_fraction(t, tok, offset); } From ad3b6ab71854a638f2b769fc12e8776abfff48d6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 6 Mar 2019 16:19:47 +0000 Subject: [PATCH 22/32] Implicit Selector Expressions: `.A` --- core/odin/ast/ast.odin | 4 +++ core/odin/parser/parser.odin | 7 ++++++ src/check_expr.cpp | 49 ++++++++++++++++++++++++++++++++++-- src/ir.cpp | 7 ++++++ src/parser.cpp | 24 +++++++++++++++++- src/parser.hpp | 1 + 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 8c43ee8e3..59d18a0c0 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -144,6 +144,10 @@ Selector_Expr :: struct { field: ^Ident, } +Implicit_Selector_Expr :: struct { + using node: Expr, + field: ^Ident, +} Index_Expr :: struct { using node: Expr, diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index c10a83e47..90de2ca30 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2581,6 +2581,13 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { ue.expr = expr; return ue; + case token.Period: + op := advance_token(p); + field := parse_ident(p); + ise := ast.new(ast.Implicit_Selector_Expr, op.pos, field.end); + ise.field = field; + return ise; + } return parse_atom_expr(p, parse_operand(p, lhs), lhs); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ea125a5eb..4c921f484 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -991,7 +991,6 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ o->expr = n; String name = n->Ident.token.string; - Entity *e = scope_lookup(c->scope, name); if (e == nullptr) { if (is_blank_ident(name)) { @@ -6177,7 +6176,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type continue; } - check_expr(c, o, elem); + check_expr_with_type_hint(c, o, elem, et); if (is_constant) { is_constant = o->mode == Addressing_Constant; @@ -6408,6 +6407,47 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; + case_ast_node(ise, ImplicitSelectorExpr, node); + o->type = t_invalid; + o->expr = node; + o->mode = Addressing_Invalid; + + if (type_hint == nullptr) { + gbString str = expr_to_string(node); + error(node, "Cannot determine type for implicit selector expression '%s'", str); + gb_string_free(str); + return Expr_Expr; + } + o->type = type_hint; + if (!is_type_enum(type_hint)) { + gbString typ = type_to_string(type_hint); + gbString str = expr_to_string(node); + error(node, "Invalid type '%s' for implicit selector expression '%s'", typ, str); + gb_string_free(str); + gb_string_free(typ); + return Expr_Expr; + } + GB_ASSERT(ise->selector->kind == Ast_Ident); + String name = ise->selector->Ident.token.string; + + Type *enum_type = base_type(type_hint); + GB_ASSERT(enum_type->kind == Type_Enum); + Entity *e = scope_lookup_current(enum_type->Enum.scope, name); + if (e == nullptr) { + gbString typ = type_to_string(type_hint); + error(node, "Undeclared name %.*s for type '%s'", LIT(name), typ); + gb_string_free(typ); + return Expr_Expr; + } + GB_ASSERT(are_types_identical(base_type(e->type), base_type(type_hint))); + GB_ASSERT(e->kind == Entity_Constant); + o->value = e->Constant.value; + o->mode = Addressing_Constant; + o->type = e->type; + + return Expr_Expr; + case_end; + case_ast_node(ie, IndexExpr, node); check_expr(c, o, ie->expr); if (o->mode == Addressing_Invalid) { @@ -6832,6 +6872,11 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = write_expr_to_string(str, se->selector); case_end; + case_ast_node(se, ImplicitSelectorExpr, node); + str = gb_string_append_rune(str, '.'); + str = write_expr_to_string(str, se->selector); + case_end; + case_ast_node(ta, TypeAssertion, node); str = write_expr_to_string(str, ta->expr); str = gb_string_appendc(str, ".("); diff --git a/src/ir.cpp b/src/ir.cpp index e3fe83c3b..0d5e3fc36 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6268,6 +6268,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { return ir_addr_load(proc, ir_build_addr(proc, expr)); case_end; + case_ast_node(ise, ImplicitSelectorExpr, expr); + TypeAndValue tav = type_and_value_of_expr(expr); + GB_ASSERT(tav.mode == Addressing_Constant); + + return ir_add_module_constant(proc->module, tv.type, tv.value); + case_end; + case_ast_node(te, TernaryExpr, expr); ir_emit_comment(proc, str_lit("TernaryExpr")); diff --git a/src/parser.cpp b/src/parser.cpp index a5526fd73..84c03587d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -25,6 +25,11 @@ Token ast_token(Ast *node) { return ast_token(node->SelectorExpr.selector); } return node->SelectorExpr.token; + case Ast_ImplicitSelectorExpr: + if (node->ImplicitSelectorExpr.selector != nullptr) { + return ast_token(node->ImplicitSelectorExpr.selector); + } + return node->ImplicitSelectorExpr.token; case Ast_IndexExpr: return node->IndexExpr.open; case Ast_SliceExpr: return node->SliceExpr.open; case Ast_Ellipsis: return node->Ellipsis.token; @@ -165,6 +170,9 @@ Ast *clone_ast(Ast *node) { n->SelectorExpr.expr = clone_ast(n->SelectorExpr.expr); n->SelectorExpr.selector = clone_ast(n->SelectorExpr.selector); break; + case Ast_ImplicitSelectorExpr: + n->ImplicitSelectorExpr.selector = clone_ast(n->ImplicitSelectorExpr.selector); + break; case Ast_IndexExpr: n->IndexExpr.expr = clone_ast(n->IndexExpr.expr); n->IndexExpr.index = clone_ast(n->IndexExpr.index); @@ -504,11 +512,20 @@ Ast *ast_call_expr(AstFile *f, Ast *proc, Array args, Token open, Token c Ast *ast_selector_expr(AstFile *f, Token token, Ast *expr, Ast *selector) { Ast *result = alloc_ast_node(f, Ast_SelectorExpr); + result->SelectorExpr.token = token; result->SelectorExpr.expr = expr; result->SelectorExpr.selector = selector; return result; } +Ast *ast_implicit_selector_expr(AstFile *f, Token token, Ast *selector) { + Ast *result = alloc_ast_node(f, Ast_ImplicitSelectorExpr); + result->ImplicitSelectorExpr.token = token; + result->ImplicitSelectorExpr.selector = selector; + return result; +} + + Ast *ast_index_expr(AstFile *f, Ast *expr, Ast *index, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_IndexExpr); result->IndexExpr.expr = expr; @@ -1612,7 +1629,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_offset_of: return parse_call_expr(f, ast_implicit(f, advance_token(f))); - case Token_String: return ast_basic_lit(f, advance_token(f)); @@ -2277,6 +2293,12 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { Ast *expr = parse_unary_expr(f, lhs); return ast_unary_expr(f, token, expr); } + + case Token_Period: { + Token token = expect_token(f, Token_Period); + Ast *ident = parse_ident(f); + return ast_implicit_selector_expr(f, token, ident); + } } return parse_atom_expr(f, parse_operand(f, lhs), lhs); diff --git a/src/parser.hpp b/src/parser.hpp index d685aa1af..816f3f534 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -245,6 +245,7 @@ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BinaryExpr, "binary expression", struct { Token op; Ast *left, *right; } ) \ AST_KIND(ParenExpr, "parentheses expression", struct { Ast *expr; Token open, close; }) \ AST_KIND(SelectorExpr, "selector expression", struct { Token token; Ast *expr, *selector; }) \ + AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \ AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \ AST_KIND(SliceExpr, "slice expression", struct { \ From 15d3f4c190abbeaef2cd6920d16aacaf71c35fe1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 6 Mar 2019 16:23:50 +0000 Subject: [PATCH 23/32] Allow implicit selector expressions in switch statements --- core/fmt/fmt.odin | 14 ++++++-------- src/check_stmt.cpp | 6 +++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 0df06c0ec..344f61e80 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -468,11 +468,10 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d buf: [256]byte; start := 0; - using strconv.Int_Flag; flags: strconv.Int_Flags; - if fi.hash && !fi.zero do flags |= {Prefix}; - if fi.plus do flags |= {Plus}; - if fi.space do flags |= {Space}; + if fi.hash && !fi.zero do flags |= {.Prefix}; + if fi.plus do flags |= {.Plus}; + if fi.space do flags |= {.Space}; s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags); if fi.hash && fi.zero { @@ -746,11 +745,10 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { ti = runtime.type_info_base(ti); switch info in ti.variant { case runtime.Type_Info_Integer: - using runtime.Type_Info_Endianness; switch info.endianness { - case Platform: return false; - case Little: return ODIN_ENDIAN != "little"; - case Big: return ODIN_ENDIAN != "big"; + case .Platform: return false; + case .Little: return ODIN_ENDIAN != "little"; + case .Big: return ODIN_ENDIAN != "big"; } } return false; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c861cfdf3..cead61ce8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -686,14 +686,14 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(ie, BinaryExpr, expr); Operand lhs = {}; Operand rhs = {}; - check_expr(ctx, &lhs, ie->left); + check_expr_with_type_hint(ctx, &lhs, ie->left, x.type); if (x.mode == Addressing_Invalid) { continue; } if (lhs.mode == Addressing_Invalid) { continue; } - check_expr(ctx, &rhs, ie->right); + check_expr_with_type_hint(ctx, &rhs, ie->right, x.type); if (rhs.mode == Addressing_Invalid) { continue; } @@ -732,7 +732,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (is_type_typeid(x.type)) { check_expr_or_type(ctx, &y, expr, x.type); } else { - check_expr(ctx, &y, expr); + check_expr_with_type_hint(ctx, &y, expr, x.type); } if (x.mode == Addressing_Invalid || From c67ea9784560d3e56febe8acd6270d4cfa6daa50 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 6 Mar 2019 19:08:37 +0000 Subject: [PATCH 24/32] Add implicit selector expressions for in/notin --- core/mem/allocators.odin | 28 ++++++++++++---------------- core/strconv/strconv.odin | 6 +++--- src/check_expr.cpp | 14 +++++++++++++- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 6dafe18c0..77cc0abf1 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -47,12 +47,10 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator { arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr { - using Allocator_Mode; arena := cast(^Arena)allocator_data; - switch mode { - case Alloc: + case .Alloc: total_size := size + alignment; if arena.offset + total_size > len(arena.data) { @@ -66,14 +64,14 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, arena.peak_used = max(arena.peak_used, arena.offset); return zero(ptr, size); - case Free: + case .Free: // NOTE(bill): Free all at once // Use Arena_Temp_Memory if you want to free a block - case Free_All: + case .Free_All: arena.offset = 0; - case Resize: + case .Resize: return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena)); } @@ -227,7 +225,6 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator { stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr { - using Allocator_Mode; s := cast(^Stack)allocator_data; if s.data == nil { @@ -256,9 +253,9 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, } switch mode { - case Alloc: + case .Alloc: return raw_alloc(s, size, alignment); - case Free: + case .Free: if old_memory == nil { return nil; } @@ -288,11 +285,11 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, s.prev_offset = int(header.prev_offset); - case Free_All: + case .Free_All: s.prev_offset = 0; s.curr_offset = 0; - case Resize: + case .Resize: if old_memory == nil { return raw_alloc(s, size, alignment); } @@ -374,7 +371,6 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr { - using Allocator_Mode; s := cast(^Small_Stack)allocator_data; if s.data == nil { @@ -403,9 +399,9 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, } switch mode { - case Alloc: + case .Alloc: return raw_alloc(s, size, alignment); - case Free: + case .Free: if old_memory == nil { return nil; } @@ -428,10 +424,10 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, s.offset = int(old_offset); - case Free_All: + case .Free_All: s.offset = 0; - case Resize: + case .Resize: if old_memory == nil { return raw_alloc(s, size, alignment); } diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 0526a65ba..d509d7b6a 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -475,7 +475,7 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i } i-=1; a[i] = digits[u % b]; - if Int_Flag.Prefix in flags { + if .Prefix in flags { ok := true; switch base { case 2: i-=1; a[i] = 'b'; @@ -493,9 +493,9 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i switch { case neg: i-=1; a[i] = '-'; - case Int_Flag.Plus in flags: + case .Plus in flags: i-=1; a[i] = '+'; - case Int_Flag.Space in flags: + case .Space in flags: i-=1; a[i] = ' '; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4c921f484..532b79969 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2144,8 +2144,20 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as case Token_in: case Token_notin: - check_expr(c, x, be->left); + // IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in + check_expr(c, y, be->right); + + if (is_type_bit_set(y->type)) { + Type *elem = base_type(y->type)->BitSet.elem; + check_expr_with_type_hint(c, x, be->left, elem); + } else if (is_type_map(y->type)) { + Type *key = base_type(y->type)->Map.key; + check_expr_with_type_hint(c, x, be->left, key); + } else { + check_expr(c, x, be->left); + } + if (x->mode == Addressing_Invalid) { return; } From c634d4a96d5e3fff379c72c31d268b198700277f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 6 Mar 2019 19:13:50 +0000 Subject: [PATCH 25/32] Using implicit selector expressions in the core library --- core/odin/parser/parser.odin | 4 ++-- core/os/os.odin | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 90de2ca30..17a24c5f5 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2679,7 +2679,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { return stmt; case op.kind == token.In: - if Stmt_Allow_Flag.In in flags { + if .In in flags { allow_token(p, token.In); prev_allow_range := p.allow_range; p.allow_range = true; @@ -2697,7 +2697,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { } case op.kind == token.Colon: expect_token_after(p, token.Colon, "identifier list"); - if Stmt_Allow_Flag.Label in flags && len(lhs) == 1 { + if .Label in flags && len(lhs) == 1 { switch p.curr_tok.kind { case token.Open_Brace, token.If, token.For, token.Switch: label := lhs[0]; diff --git a/core/os/os.odin b/core/os/os.odin index 9db84ddfe..d9bb318c4 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -123,20 +123,18 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { - using mem.Allocator_Mode; - switch mode { - case Alloc: + case .Alloc: return heap_alloc(size); - case Free: + case .Free: heap_free(old_memory); return nil; - case Free_All: + case .Free_All: // NOTE(bill): Does nothing - case Resize: + case .Resize: if old_memory == nil { return heap_alloc(size); } From 5c04800831d4b6b642d40e2866b88661cd8ac31e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 6 Mar 2019 20:01:46 +0000 Subject: [PATCH 26/32] Add type inference to index expressions for maps --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 532b79969..f533a1812 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6473,7 +6473,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_map(t)) { Operand key = {}; - check_expr(c, &key, ie->index); + check_expr_with_type_hint(c, &key, ie->index, t->Map.key); check_assignment(c, &key, t->Map.key, str_lit("map index")); if (key.mode == Addressing_Invalid) { o->mode = Addressing_Invalid; From 007a7989b83df0023ded1cd021ee55b341544840 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 6 Mar 2019 20:06:37 +0000 Subject: [PATCH 27/32] Add implicit selector expression examples to demo.odin --- examples/demo/demo.odin | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index fd15b2a97..d65c83059 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -746,6 +746,34 @@ map_type :: proc() { } +implicit_selector_expression :: proc() { + fmt.println("# implicit selector expression"); + + Foo :: enum {A, B, C}; + + f: Foo; + f = .A; + + BAR :: bit_set[Foo]{.B, .C}; + + switch f { + case .A: + fmt.println("HERE"); + case .B: + fmt.println("NEVER"); + case .C: + fmt.println("FOREVER"); + } + + my_map := make(map[Foo]int); + defer delete(my_map); + + my_map[.A] = 123; + my_map[Foo.B] = 345; + + fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C]); +} + explicit_procedure_overloading :: proc() { fmt.println("# explicit procedure overloading"); @@ -940,6 +968,7 @@ main :: proc() { named_proc_return_parameters(); using_enum(); map_type(); + implicit_selector_expression(); explicit_procedure_overloading(); complete_switch(); cstring_example(); From d852b0c9485267f247210f3a2f8021e8e2c96232 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 9 Mar 2019 11:08:50 +0100 Subject: [PATCH 28/32] Add win32.get_cwd to return the current working directory --- core/strings/strings.odin | 17 +++++++++++++++++ core/sys/win32/crt.odin | 14 ++++++++++++++ core/sys/win32/general.odin | 3 +++ 3 files changed, 34 insertions(+) create mode 100644 core/sys/win32/crt.odin diff --git a/core/strings/strings.odin b/core/strings/strings.odin index c8c8e560c..edf288c16 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -329,6 +329,10 @@ is_space :: proc(r: rune) -> bool { return false; } +is_null :: proc(r: rune) -> bool { + return r == 0x0000; +} + index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { for r, i in s { if p(r) == truth { @@ -476,3 +480,16 @@ trim_right_space :: proc(s: string) -> string { trim_space :: proc(s: string) -> string { return trim_right_space(trim_left_space(s)); } + +trim_left_null :: proc(s: string) -> string { + return trim_left_proc(s, is_null); +} + +trim_right_null :: proc(s: string) -> string { + return trim_right_proc(s, is_null); +} + +trim_null :: proc(s: string) -> string { + return trim_right_null(trim_left_null(s)); +} + diff --git a/core/sys/win32/crt.odin b/core/sys/win32/crt.odin new file mode 100644 index 000000000..46fba6fe8 --- /dev/null +++ b/core/sys/win32/crt.odin @@ -0,0 +1,14 @@ +package win32 + +import "core:strings"; + +foreign { + @(link_name="_wgetcwd") _get_cwd_wide :: proc(buffer: Wstring, buf_len: int) -> ^Wstring --- +} + +get_cwd :: proc(allocator := context.temp_allocator) -> string { + buffer := make([]u16, MAX_PATH_WIDE, allocator); + _get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE); + file := ucs2_to_utf8(buffer[:], allocator); + return strings.trim_right_null(file); +} \ No newline at end of file diff --git a/core/sys/win32/general.odin b/core/sys/win32/general.odin index c34294b51..f4f3de380 100644 --- a/core/sys/win32/general.odin +++ b/core/sys/win32/general.odin @@ -108,6 +108,8 @@ File_Attribute_Data :: struct { file_size_low: u32, } +// NOTE(Jeroen): The widechar version might want at least the 32k MAX_PATH_WIDE +// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilew#parameters Find_Data_W :: struct{ file_attributes: u32, creation_time: Filetime, @@ -798,6 +800,7 @@ is_key_down :: inline proc(key: Key_Code) -> bool { return get_async_key_state(i MAX_PATH :: 0x00000104; +MAX_PATH_WIDE :: 0x8000; HANDLE_FLAG_INHERIT :: 1; HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2; From 090937f8af95985baf0ffb22630768793a0c91e4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 9 Mar 2019 12:45:17 +0100 Subject: [PATCH 29/32] Add convenience functions for open + save dialogs. --- core/sys/win32/comdlg32.odin | 91 +++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/core/sys/win32/comdlg32.odin b/core/sys/win32/comdlg32.odin index 790c9d598..54b45886e 100644 --- a/core/sys/win32/comdlg32.odin +++ b/core/sys/win32/comdlg32.odin @@ -2,6 +2,7 @@ package win32 foreign import "system:comdlg32.lib" +import "core:strings" OFN_Hook_Proc :: #type proc "stdcall" (hdlg: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Uint_Ptr; @@ -61,11 +62,82 @@ Open_File_Name_W :: struct { foreign comdlg32 { @(link_name="GetOpenFileNameA") get_open_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool --- @(link_name="GetOpenFileNameW") get_open_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool --- - + @(link_name="GetSaveFileNameA") get_save_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool --- + @(link_name="GetSaveFileNameW") get_save_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool --- @(link_name="CommDlgExtendedError") comm_dlg_extended_error :: proc() -> u32 --- } -OFN_ALLOWMULTISELECT :: 0x00000200; +OPEN_TITLE :: "Select file to open"; +OPEN_FLAGS :: u32(OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST); +OPEN_FLAGS_MULTI :: u32(OPEN_FLAGS | OFN_ALLOWMULTISELECT | OFN_EXPLORER); + +SAVE_TITLE :: "Select file to save"; +SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER); +SAVE_EXT :: "txt"; + +OpenSaveMode :: enum{ + Open = 0, + Save = 1, +} + +_open_file_dialog :: proc(title: string, dir: string, filters: []string, default_filter: u32, flags: u32, default_ext: string, mode: OpenSaveMode, allocator := context.temp_allocator) -> (path: string, ok: bool) { + ok = true; + + file_buf := make([]u16, MAX_PATH_WIDE, allocator); + + // Filters need to be passed as a pair of strings (title, filter) + filter_len := u32(len(filters)); + if filter_len % 2 != 0 do return "", false; + default_filter = clamp(default_filter, 1, filter_len / 2); + + filter := strings.join(filters, "\u0000", context.temp_allocator); + filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator); + filter_w := utf8_to_wstring(filter, context.temp_allocator); + + ofn := Open_File_Name_W{ + struct_size = size_of(Open_File_Name_W), + file = Wstring(&file_buf[0]), + max_file = MAX_PATH_WIDE, + title = utf8_to_wstring(title, context.temp_allocator), + filter = filter_w, + initial_dir = utf8_to_wstring(dir, context.temp_allocator), + filter_index = u32(default_filter), + def_ext = utf8_to_wstring(default_ext, context.temp_allocator), + flags = u32(flags), + }; + + if mode == OpenSaveMode.Open { + ok = bool(get_open_file_name_w(&ofn)); + } else if mode == OpenSaveMode.Save { + ok = bool(get_save_file_name_w(&ofn)); + } else do ok = false; + + if !ok { + delete(file_buf); + return "", false; + } + + file_name := ucs2_to_utf8(file_buf[:], allocator); + path = strings.trim_right_null(file_name); + return; +} + +select_file_to_open :: proc(title := OPEN_TITLE, dir := ".", filters := []string{"All Files", "*.*"}, default_filter := u32(1), flags := OPEN_FLAGS, allocator := context.temp_allocator) -> (path: string, ok: bool) { + + path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, "", OpenSaveMode.Open, allocator); + return; +} + +select_file_to_save :: proc(title := SAVE_TITLE, dir := ".", filters := []string{"All Files", "*.*"}, default_filter := u32(1), flags := SAVE_FLAGS, default_ext := SAVE_EXT, allocator := context.temp_allocator) -> (path: string, ok: bool) { + + path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, OpenSaveMode.Save, allocator); + return; +} + +// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes +// it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you. + +OFN_ALLOWMULTISELECT :: 0x00000200; // NOTE(Jeroen): Without OFN_EXPLORER it uses the Win3 dialog. OFN_CREATEPROMPT :: 0x00002000; OFN_DONTADDTORECENT :: 0x02000000; OFN_ENABLEHOOK :: 0x00000020; @@ -91,3 +163,18 @@ OFN_PATHMUSTEXIST :: 0x00000800; OFN_READONLY :: 0x00000001; OFN_SHAREAWARE :: 0x00004000; OFN_SHOWHELP :: 0x00000010; + +CDERR_DIALOGFAILURE :: 0x0000FFFF; +CDERR_GENERALCODES :: 0x00000000; +CDERR_STRUCTSIZE :: 0x00000001; +CDERR_INITIALIZATION :: 0x00000002; +CDERR_NOTEMPLATE :: 0x00000003; +CDERR_NOHINSTANCE :: 0x00000004; +CDERR_LOADSTRFAILURE :: 0x00000005; +CDERR_FINDRESFAILURE :: 0x00000006; +CDERR_LOADRESFAILURE :: 0x00000007; +CDERR_LOCKRESFAILURE :: 0x00000008; +CDERR_MEMALLOCFAILURE :: 0x00000009; +CDERR_MEMLOCKFAILURE :: 0x0000000A; +CDERR_NOHOOK :: 0x0000000B; +CDERR_REGISTERMSGFAIL :: 0x0000000C; \ No newline at end of file From 4f24f1172edcda5094eb6b89deac8613c433c13e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 9 Mar 2019 13:48:48 +0100 Subject: [PATCH 30/32] Stylistic improvements to new comdlg helpers. --- core/sys/win32/comdlg32.odin | 57 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/core/sys/win32/comdlg32.odin b/core/sys/win32/comdlg32.odin index 54b45886e..3d9fb74c4 100644 --- a/core/sys/win32/comdlg32.odin +++ b/core/sys/win32/comdlg32.odin @@ -75,14 +75,15 @@ SAVE_TITLE :: "Select file to save"; SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER); SAVE_EXT :: "txt"; -OpenSaveMode :: enum{ +Open_Save_Mode :: enum { Open = 0, Save = 1, } -_open_file_dialog :: proc(title: string, dir: string, filters: []string, default_filter: u32, flags: u32, default_ext: string, mode: OpenSaveMode, allocator := context.temp_allocator) -> (path: string, ok: bool) { - ok = true; - +_open_file_dialog :: proc(title: string, dir: string, + filters: []string, default_filter: u32, + flags: u32, default_ext: string, + mode: Open_Save_Mode, allocator := context.temp_allocator) -> (path: string, ok: bool = true) { file_buf := make([]u16, MAX_PATH_WIDE, allocator); // Filters need to be passed as a pair of strings (title, filter) @@ -90,28 +91,31 @@ _open_file_dialog :: proc(title: string, dir: string, filters: []string, default if filter_len % 2 != 0 do return "", false; default_filter = clamp(default_filter, 1, filter_len / 2); - filter := strings.join(filters, "\u0000", context.temp_allocator); - filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator); - filter_w := utf8_to_wstring(filter, context.temp_allocator); + filter: string; + filter = strings.join(filters, "\u0000", context.temp_allocator); + filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator); ofn := Open_File_Name_W{ - struct_size = size_of(Open_File_Name_W), - file = Wstring(&file_buf[0]), - max_file = MAX_PATH_WIDE, - title = utf8_to_wstring(title, context.temp_allocator), - filter = filter_w, - initial_dir = utf8_to_wstring(dir, context.temp_allocator), + struct_size = size_of(Open_File_Name_W), + file = Wstring(&file_buf[0]), + max_file = MAX_PATH_WIDE, + title = utf8_to_wstring(title, context.temp_allocator), + filter = utf8_to_wstring(filter, context.temp_allocator), + initial_dir = utf8_to_wstring(dir, context.temp_allocator), filter_index = u32(default_filter), - def_ext = utf8_to_wstring(default_ext, context.temp_allocator), - flags = u32(flags), + def_ext = utf8_to_wstring(default_ext, context.temp_allocator), + flags = u32(flags), }; - if mode == OpenSaveMode.Open { - ok = bool(get_open_file_name_w(&ofn)); - } else if mode == OpenSaveMode.Save { + switch mode { + case .Open: + ok = bool(get_open_file_name_w(&ofn)); + case .Save: ok = bool(get_save_file_name_w(&ofn)); - } else do ok = false; - + case: + ok = false; + } + if !ok { delete(file_buf); return "", false; @@ -122,15 +126,20 @@ _open_file_dialog :: proc(title: string, dir: string, filters: []string, default return; } -select_file_to_open :: proc(title := OPEN_TITLE, dir := ".", filters := []string{"All Files", "*.*"}, default_filter := u32(1), flags := OPEN_FLAGS, allocator := context.temp_allocator) -> (path: string, ok: bool) { +select_file_to_open :: proc(title := OPEN_TITLE, dir := ".", + filters := []string{"All Files", "*.*"}, default_filter := u32(1), + flags := OPEN_FLAGS, allocator := context.temp_allocator) -> (path: string, ok: bool) { - path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, "", OpenSaveMode.Open, allocator); + path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, "", Open_Save_Mode.Open, allocator); return; } -select_file_to_save :: proc(title := SAVE_TITLE, dir := ".", filters := []string{"All Files", "*.*"}, default_filter := u32(1), flags := SAVE_FLAGS, default_ext := SAVE_EXT, allocator := context.temp_allocator) -> (path: string, ok: bool) { +select_file_to_save :: proc(title := SAVE_TITLE, dir := ".", + filters := []string{"All Files", "*.*"}, default_filter := u32(1), + flags := SAVE_FLAGS, default_ext := SAVE_EXT, + allocator := context.temp_allocator) -> (path: string, ok: bool) { - path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, OpenSaveMode.Save, allocator); + path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, Open_Save_Mode.Save, allocator); return; } From b08d944c33be661fe679a1e47624ed84577b8da0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 11 Mar 2019 09:32:15 +0000 Subject: [PATCH 31/32] Fix typo in demo.odin --- examples/demo/demo.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index d65c83059..e5c5830e1 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -856,7 +856,7 @@ cstring_example :: proc() { fmt.println(len(W), len(X), len(Y)); // IMPORTANT NOTE for cstring variables // len(cstring) is O(N) - // cast(cstring)string is O(N) + // cast(string)cstring is O(N) } deprecated_attribute :: proc() { From bdab5e00da6dee80b7582135815f2183def935bb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 11 Mar 2019 19:52:40 +0000 Subject: [PATCH 32/32] Minor code clean up --- core/odin/parser/parser.odin | 27 +++++++++++++++++++++++++-- core/runtime/core.odin | 30 ++++++++++++------------------ src/check_expr.cpp | 3 ++- src/check_type.cpp | 1 + src/parser.hpp | 1 - src/types.cpp | 1 + 6 files changed, 41 insertions(+), 22 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 17a24c5f5..ccae59a7c 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1077,9 +1077,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { stmt := parse_stmt(p); switch name { case "bounds_check": - stmt.state_flags |= {ast.Node_State_Flag.Bounds_Check}; + stmt.state_flags |= {.Bounds_Check}; case "no_bounds_check": - stmt.state_flags |= {ast.Node_State_Flag.No_Bounds_Check}; + stmt.state_flags |= {.No_Bounds_Check}; } return stmt; case "complete": @@ -1723,6 +1723,29 @@ string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention { return Invalid; } +parse_proc_tags :: proc(p: ^Parser) -> (tags: Proc_Tags) { + for p.curr_tok.kind == token.Hash { + tok := expect_token(p, token.Hash); + ident := expect_token(p, token.Ident); + + switch ident.text { + case "require_results": + tags |= {.Require_Results}; + case "bounds_check": + tags |= {.Bounds_Check}; + case "no_bounds_check": + tags |= {.No_Bounds_Check}; + case: + } + } + + if .Bounds_Check in tags && .No_Bounds_Check in tags { + p.err(p.curr_tok.pos, "#bounds_check and #no_bounds_check applied to the same procedure type"); + } + + return; +} + parse_proc_type :: proc(p: ^Parser, tok: token.Token) -> ^ast.Proc_Type { cc := ast.Proc_Calling_Convention.Invalid; if p.curr_tok.kind == token.String { diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 38dd8f225..1ce7cfac5 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -251,8 +251,10 @@ Map_Entry_Header :: struct { Map_Header :: struct { m: ^mem.Raw_Map, is_key_string: bool, + entry_size: int, entry_align: int, + value_offset: uintptr, value_size: int, } @@ -833,29 +835,23 @@ __get_map_key :: proc "contextless" (key: $K) -> Map_Key { return map_key; } +_fnv64a :: proc(data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { + h: u64 = seed; + for b in data { + h = (h ~ u64(b)) * 0x100000001b3; + } + return h; +} + default_hash :: proc(data: []byte) -> u64 { - fnv64a :: proc(data: []byte) -> u64 { - h: u64 = 0xcbf29ce484222325; - for b in data { - h = (h ~ u64(b)) * 0x100000001b3; - } - return h; - } - return fnv64a(data); + return _fnv64a(data); } default_hash_string :: proc(s: string) -> u64 do return default_hash(([]byte)(s)); source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { - fnv64a :: proc(data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { - h: u64 = seed; - for b in data { - h = (h ~ u64(b)) * 0x100000001b3; - } - return h; - } - hash := fnv64a(cast([]byte)s.file_path); + hash := _fnv64a(cast([]byte)s.file_path); hash = hash ~ (u64(s.line) * 0x100000001b3); hash = hash ~ (u64(s.column) * 0x100000001b3); return hash; @@ -863,7 +859,6 @@ source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { - __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: mem.Allocator, loc := #caller_location) -> bool { array := (^mem.Raw_Slice)(array_); @@ -942,7 +937,6 @@ __dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr { } __dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { - index: int; assert(value != nil); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f533a1812..d0e18d89f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5439,7 +5439,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { operand->mode = Addressing_NoValue; } else { GB_ASSERT(is_type_tuple(result_type)); - switch (result_type->Tuple.variables.count) { + isize count = result_type->Tuple.variables.count; + switch (count) { case 0: operand->mode = Addressing_NoValue; break; diff --git a/src/check_type.cpp b/src/check_type.cpp index 1c5d5ac85..451a388fb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2049,6 +2049,7 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, type->Proc.is_polymorphic = pt->generic; type->Proc.specialization_count = specialization_count; type->Proc.diverging = pt->diverging; + type->Proc.tags = pt->tags; if (param_count > 0) { Entity *end = params->Tuple.variables[param_count-1]; diff --git a/src/parser.hpp b/src/parser.hpp index 816f3f534..e08648eca 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -151,7 +151,6 @@ enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, ProcTag_require_results = 1<<4, - ProcTag_no_context = 1<<6, }; enum ProcCallingConvention { diff --git a/src/types.cpp b/src/types.cpp index c6886e984..5aa2ab6e1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -198,6 +198,7 @@ struct TypeUnion { bool has_proc_default_values; \ bool has_named_results; \ bool diverging; /* no return */ \ + u64 tags; \ isize specialization_count; \ ProcCallingConvention calling_convention; \ }) \