diff --git a/code/demo.odin b/code/demo.odin index 26a91e452..9a4db13cc 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,55 +1,598 @@ + import ( "fmt.odin"; "strconv.odin"; - "thread.odin"; - win32 "sys/windows.odin"; + "mem.odin"; + "thread.odin" when ODIN_OS == "windows"; + win32 "sys/windows.odin" when ODIN_OS == "windows"; + +/* + "atomics.odin"; + "bits.odin"; + "hash.odin"; + "math.odin"; + "opengl.odin"; + "os.odin"; + "raw.odin"; + "sort.odin"; + "strings.odin"; + "sync.odin"; + "types.odin"; + "utf8.odin"; + "utf16.odin"; +*/ ) +general_stuff :: proc() { + { // `do` for inline statmes rather than block + foo :: proc() do fmt.println("Foo!"); + if false do foo(); + for false do foo(); + when false do foo(); + + if false do foo(); + else do foo(); + } + + { // Removal of `++` and `--` (again) + x: int; + x += 1; + x -= 1; + } + { // Casting syntaxes + i := i32(137); + ptr := &i; + + fp1 := (^f32)(ptr); + // ^f32(ptr) == ^(f32(ptr)) + fp2 := cast(^f32)ptr; + + f1 := (^f32)(ptr)^; + f2 := (cast(^f32)ptr)^; + + // Questions: Should there be two ways to do it? + } + + /* + * Remove *_val_of built-in procedures + * size_of, align_of, offset_of + * type_of, type_info_of + */ + + { // `expand_to_tuple` built-in procedure + Foo :: struct { + x: int; + b: bool; + } + f := Foo{137, true}; + x, b := expand_to_tuple(f); + fmt.println(x, b); + fmt.println(expand_to_tuple(f)); + } + + { + // .. half-closed range + // ... open range + + for in 0..2 {} // 0, 1 + for in 0...2 {} // 0, 1, 2 + } +} + +nested_struct_declarations :: proc() { + { + FooInteger :: int; + Foo :: struct { + i: FooInteger; + }; + f := Foo{FooInteger(137)}; + } + { + Foo :: struct { + Integer :: int; + + i: Integer; + } + f := Foo{Foo.Integer(137)}; + + } +} + +default_struct_values :: proc() { + { + Vector3 :: struct { + x: f32; + y: f32; + z: f32; + } + v: Vector3; + fmt.println(v); + } + { + // Default values must be constants + Vector3 :: struct { + x: f32 = 1; + y: f32 = 4; + z: f32 = 9; + } + v: Vector3; + fmt.println(v); + + v = Vector3{}; + fmt.println(v); + + // Uses the same semantics as a default values in a procedure + v = Vector3{137}; + fmt.println(v); + + v = Vector3{z = 137}; + fmt.println(v); + } + + { + Vector3 :: struct { + x := 1.0; + y := 4.0; + z := 9.0; + } + stack_default: Vector3; + stack_literal := Vector3{}; + heap_one := new(Vector3); defer free(heap_one); + heap_two := new_clone(Vector3{}); defer free(heap_two); + + fmt.println("stack_default - ", stack_default); + fmt.println("stack_literal - ", stack_literal); + fmt.println("heap_one - ", heap_one^); + fmt.println("heap_two - ", heap_two^); + + + N :: 4; + stack_array: [N]Vector3; + heap_array := new([N]Vector3); defer free(heap_array); + heap_slice := make([]Vector3, N); defer free(heap_slice); + fmt.println("stack_array[1] - ", stack_array[1]); + fmt.println("heap_array[1] - ", heap_array[1]); + fmt.println("heap_slice[1] - ", heap_slice[1]); + } +} + + + + +union_type :: proc() { + { + val: union{int, bool}; + val = 137; + if i, ok := val.(int); ok { + fmt.println(i); + } + val = true; + fmt.println(val); + + val = nil; + + match v in val { + case int: fmt.println("int", v); + case bool: fmt.println("bool", v); + case: fmt.println("nil"); + } + } + { + // There is a duality between `any` and `union` + // An `any` has a pointer to the data and allows for any type (open) + // A `union` has as binary blob to store the data and allows only certain types (closed) + // The following code is with `any` but has the same syntax + val: any; + val = 137; + if i, ok := val.(int); ok { + fmt.println(i); + } + val = true; + fmt.println(val); + + val = nil; + + match v in val { + case int: fmt.println("int", v); + case bool: fmt.println("bool", v); + case: fmt.println("nil"); + } + } + + Vector3 :: struct { + x, y, z: f32; + }; + Quaternion :: struct { + x, y, z: f32; + w: f32 = 1; + }; + + // More realistic examples + { + // NOTE(bill): For the above basic examples, you may not have any + // particular use for it. However, my main use for them is not for these + // simple cases. My main use is for hierarchical types. Many prefer + // subtyping, embedding the base data into the derived types. Below is + // an example of this for a basic game Entity. + + Entity :: struct { + id: u64; + name: string; + position: Vector3; + orientation: Quaternion; + + derived: any; + } + + Frog :: struct { + using entity: Entity; + jump_height: f32; + } + + Monster :: struct { + using entity: Entity; + is_robot: bool; + is_zombie: bool; + } + + // See `parametric_polymorphism` procedure for details + new_entity :: proc(T: type) -> ^Entity { + t := new(T); + t.derived = t^; + return t; + } + + entity := new_entity(Monster); + + match e in entity.derived { + case Frog: + fmt.println("Ribbit"); + case Monster: + if e.is_robot do fmt.println("Robotic"); + if e.is_zombie do fmt.println("Grrrr!"); + } + } + + { + // NOTE(bill): A union can be used to achieve something similar. Instead + // of embedding the base data into the derived types, the derived data + // in embedded into the base type. Below is the same example of the + // basic game Entity but using an union. + + Entity :: struct { + id: u64; + name: string; + position: Vector3; + orientation: Quaternion; + + derived: union {Frog, Monster}; + } + + Frog :: struct { + using entity: ^Entity; + jump_height: f32; + } + + Monster :: struct { + using entity: ^Entity; + is_robot: bool; + is_zombie: bool; + } + + // See `parametric_polymorphism` procedure for details + new_entity :: proc(T: type) -> ^Entity { + t := new(Entity); + t.derived = T{entity = t}; + return t; + } + + entity := new_entity(Monster); + + match e in entity.derived { + case Frog: + fmt.println("Ribbit"); + case Monster: + if e.is_robot do fmt.println("Robotic"); + if e.is_zombie do fmt.println("Grrrr!"); + } + + // NOTE(bill): As you can see, the usage code has not changed, only its + // memory layout. Both approaches have their own advantages but they can + // be used together to achieve different results. The subtyping approach + // can allow for a greater control of the memory layout and memory + // allocation, e.g. storing the derivatives together. However, this is + // also its disadvantage. You must either preallocate arrays for each + // derivative separation (which can be easily missed) or preallocate a + // bunch of "raw" memory; determining the maximum size of the derived + // types would require the aid of metaprogramming. Unions solve this + // particular problem as the data is stored with the base data. + // Therefore, it is possible to preallocate, e.g. [100]Entity. + + // It should be noted that the union approach can have the same memory + // layout as the any and with the same type restrictions by using a + // pointer type for the derivatives. + + /* + Entity :: struct { + ... + derived: union{^Frog, ^Monster}; + } + + Frog :: struct { + using entity: Entity; + ... + } + Monster :: struct { + using entity: Entity; + ... + + } + new_entity :: proc(T: type) -> ^Entity { + t := new(T); + t.derived = t; + return t; + } + */ + } +} + +parametric_polymorphism :: proc() { + print_value :: proc(value: $T) { + fmt.printf("print_value: %v %v\n", value, value); + } + + v1: int = 1; + v2: f32 = 2.1; + v3: f64 = 3.14; + v4: string = "message"; + + print_value(v1); + print_value(v2); + print_value(v3); + print_value(v4); + + fmt.println(); + + add :: proc(p, q: $T) -> T { + x: T = p + q; + return x; + } + + a := add(3, 4); + fmt.printf("a: %T = %v\n", a, a); + + b := add(3.2, 4.3); + fmt.printf("b: %T = %v\n", b, b); + + // This is how `new` is implemented + alloc_type :: proc(T: type) -> ^T { + t := cast(^T)alloc(size_of(T), align_of(T)); + t^ = T{}; // Use default initialization value + return t; + } + + copy :: proc(dst, src: []$T) -> int { + n := min(len(dst), len(src)); + if n > 0 { + mem.copy(&dst[0], &src[0], n*size_of(T)); + } + return n; + } + + double_params :: proc(a: $A, b: $B) -> A { + return a + A(b); + } + + fmt.println(double_params(12, 1.345)); + + + + { // Polymorphic Types and Type Specialization + Table :: struct(Key, Value: type) { + Slot :: struct { + occupied: bool; + hash: u32; + key: Key; + value: Value; + } + SIZE_MIN :: 32; + + count: int; + allocator: Allocator; + slots: []Slot; + } + + // Only allow types that are specializations of a (polymorphic) slice + make_slice :: proc(T: type/[]$E, len: int) -> T { + return make(T, len); + } + + + // Only allow types that are specializations of `Table` + allocate :: proc(table: ^$T/Table, capacity: int) { + c := context; + if table.allocator.procedure != nil do c.allocator = table.allocator; + + push_context c { + table.slots = make_slice([]T.Slot, max(capacity, T.SIZE_MIN)); + } + } + + expand :: proc(table: ^$T/Table) { + c := context; + if table.allocator.procedure != nil do c.allocator = table.allocator; + + push_context c { + old_slots := table.slots; + + cap := max(2*cap(table.slots), T.SIZE_MIN); + allocate(table, cap); + + for s in old_slots do if s.occupied { + put(table, s.key, s.value); + } + + free(old_slots); + } + } + + // Polymorphic determination of a polymorphic struct + // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) { + put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) { + hash := get_hash(key); // Ad-hoc method which would fail in a different scope + index := find_index(table, key, hash); + if index < 0 { + if f64(table.count) >= 0.75*f64(cap(table.slots)) { + expand(table); + } + assert(table.count <= cap(table.slots)); + + hash := get_hash(key); + index = int(hash % u32(cap(table.slots))); + + for table.slots[index].occupied { + if index += 1; index >= cap(table.slots) { + index = 0; + } + } + + table.count += 1; + } + + slot := &table.slots[index]; + slot.occupied = true; + slot.hash = hash; + slot.key = key; + slot.value = value; + } + + + // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) { + find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) { + hash := get_hash(key); + index := find_index(table, key, hash); + if index < 0 { + return Value{}, false; + } + return table.slots[index].value, true; + } + + find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int { + if cap(table.slots) <= 0 do return -1; + + index := int(hash % u32(cap(table.slots))); + for table.slots[index].occupied { + if table.slots[index].hash == hash { + if table.slots[index].key == key { + return index; + } + } + + if index += 1; index >= cap(table.slots) { + index = 0; + } + } + + return -1; + } + + get_hash :: proc(s: string) -> u32 { // djb2 + hash: u32 = 0x1505; + for i in 0..len(s) do hash = (hash<<5) + hash + u32(s[i]); + return hash; + } + + + table: Table(string, int); + + for i in 0..36 do put(&table, "Hellope", i); + for i in 0..42 do put(&table, "World!", i); + + found, _ := find(&table, "Hellope"); + fmt.printf("`found` is %v\n", found); + + found, _ = find(&table, "World!"); + fmt.printf("`found` is %v\n", found); + + // I would not personally design a hash table like this in production + // but this is a nice basic example + // A better approach would either use a `u64` or equivalent for the key + // and let the user specify the hashing function or make the user store + // the hashing procedure with the table + } +} + + + + prefix_table := [...]string{ "White", "Red", - "Orange", - "Yellow", "Green", "Blue", "Octarine", "Black", }; -worker_proc :: proc(t: ^thread.Thread) -> int { - for iteration in 1...5 { - fmt.printf("Th/read %d is on iteration %d\n", t.user_index, iteration); - fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration); - win32.sleep(1); - } - return 0; -} - - -main :: proc() { - threads := make([]^thread.Thread, 0, len(prefix_table)); - - for i in 0..len(prefix_table) { - if t := thread.create(worker_proc); t != nil { - t.init_context = context; - t.use_init_context = true; - t.user_index = len(threads); - append(&threads, t); - thread.start(t); +threading_example :: proc() { + when ODIN_OS == "windows" { + unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) { + __bounds_check_error_loc(loc, index, len(array)); + array[index] = array[len(array)-1]; + pop(array); + } + ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) { + __bounds_check_error_loc(loc, index, len(array)); + copy(array[index..], array[index+1..]); + pop(array); } - } - for len(threads) > 0 { - for i := 0; i < len(threads); i += 1 { - if t := threads[i]; thread.is_done(t) { - fmt.printf("Thread %d is done\n", t.user_index); - thread.destroy(t); + worker_proc :: proc(t: ^thread.Thread) -> int { + for iteration in 1...5 { + fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration); + fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration); + win32.sleep(1); + } + return 0; + } - threads[i] = threads[len(threads)-1]; - pop(&threads); - i -= 1; + threads := make([]^thread.Thread, 0, len(prefix_table)); + defer free(threads); + + for i in 0..len(prefix_table) { + if t := thread.create(worker_proc); t != nil { + t.init_context = context; + t.use_init_context = true; + t.user_index = len(threads); + append(&threads, t); + thread.start(t); + } + } + + for len(threads) > 0 { + for i := 0; i < len(threads); { + if t := threads[i]; thread.is_done(t) { + fmt.printf("Thread %d is done\n", t.user_index); + thread.destroy(t); + + ordered_remove(&threads, i); + } else { + i += 1; + } } } } } + +main :: proc() { + if true { + fmt.println("\ngeneral_stuff:"); general_stuff(); + fmt.println("\nnested_struct_declarations:"); nested_struct_declarations(); + fmt.println("\ndefault_struct_values:"); default_struct_values(); + fmt.println("\nunion_type:"); union_type(); + fmt.println("\nparametric_polymorphism:"); parametric_polymorphism(); + } + fmt.println("\nthreading_example:"); threading_example(); +} + diff --git a/core/_preload.odin b/core/_preload.odin index 8fdfa2f79..c58c73bf1 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -160,11 +160,10 @@ Allocator :: struct #ordered { Context :: struct #ordered { + allocator: Allocator; thread_id: int; - allocator: Allocator; - - user_data: rawptr; + user_data: any; user_index: int; derived: any; // May be used for derived data types @@ -173,9 +172,9 @@ Context :: struct #ordered { DEFAULT_ALIGNMENT :: align_of([vector 4]f32); SourceCodeLocation :: struct #ordered { - fully_pathed_filename: string; - line, column: i64; - procedure: string; + file_path: string; + line, column: i64; + procedure: string; } @@ -371,7 +370,7 @@ pop :: proc(array: ^$T/[]$E) -> E #cc_contextless { if array == nil do return E{}; assert(len(array) > 0); res := array[len(array)-1]; - (cast(^raw.Slice)array).len -= 1; + (^raw.Slice)(array).len -= 1; return res; } @@ -379,7 +378,7 @@ pop :: proc(array: ^$T/[dynamic]$E) -> E #cc_contextless { if array == nil do return E{}; assert(len(array) > 0); res := array[len(array)-1]; - (cast(^raw.DynamicArray)array).len -= 1; + (^raw.DynamicArray)(array).len -= 1; return res; } @@ -445,25 +444,25 @@ __get_map_key :: proc(key: $K) -> __MapKey #cc_contextless { match _ in ti { case TypeInfo.Integer: match 8*size_of(key) { - case 8: map_key.hash = u128((cast( ^u8)&key)^); - case 16: map_key.hash = u128((cast( ^u16)&key)^); - case 32: map_key.hash = u128((cast( ^u32)&key)^); - case 64: map_key.hash = u128((cast( ^u64)&key)^); - case 128: map_key.hash = u128((cast(^u128)&key)^); + case 8: map_key.hash = u128(( ^u8)(&key)^); + case 16: map_key.hash = u128(( ^u16)(&key)^); + case 32: map_key.hash = u128(( ^u32)(&key)^); + case 64: map_key.hash = u128(( ^u64)(&key)^); + case 128: map_key.hash = u128((^u128)(&key)^); case: panic("Unhandled integer size"); } case TypeInfo.Rune: map_key.hash = u128((cast(^rune)&key)^); case TypeInfo.Pointer: - map_key.hash = u128(uint((cast(^rawptr)&key)^)); + map_key.hash = u128(uint((^rawptr)(&key)^)); case TypeInfo.Float: match 8*size_of(key) { - case 32: map_key.hash = u128((cast(^u32)&key)^); - case 64: map_key.hash = u128((cast(^u64)&key)^); + case 32: map_key.hash = u128((^u32)(&key)^); + case 64: map_key.hash = u128((^u64)(&key)^); case: panic("Unhandled float size"); } case TypeInfo.String: - str := (cast(^string)&key)^; + str := (^string)(&key)^; map_key.hash = __default_hash_string(str); map_key.str = str; case: @@ -494,9 +493,9 @@ new_clone :: proc(data: $T) -> ^T #inline { } free :: proc(ptr: rawptr) do free_ptr(ptr); -free :: proc(str: $T/string) do free_ptr((cast(^raw.String)&str).data); -free :: proc(array: $T/[dynamic]$E) do free_ptr((cast(^raw.DynamicArray)&array).data); -free :: proc(slice: $T/[]$E) do free_ptr((cast(^raw.Slice)&slice).data); +free :: proc(str: $T/string) do free_ptr((^raw.String )(&str).data); +free :: proc(array: $T/[dynamic]$E) do free_ptr((^raw.DynamicArray)(&array).data); +free :: proc(slice: $T/[]$E) do free_ptr((^raw.Slice )(&slice).data); free :: proc(m: $T/map[$K]$V) { raw := cast(^raw.DynamicMap)&m; free(raw.hashes); @@ -508,14 +507,14 @@ free :: proc(m: $T/map[$K]$V) { /* make :: proc(T: type/[]$E, len: int, using location := #caller_location) -> T { cap := len; - __slice_expr_error(fully_pathed_filename, int(line), int(column), 0, len, cap); + __slice_expr_error(file_path, int(line), int(column), 0, len, cap); data := cast(^E)alloc(len * size_of(E), align_of(E)); for i in 0..len do (data+i)^ = E{}; s := raw.Slice{data = data, len = len, cap = len}; return (cast(^T)&s)^; } make :: proc(T: type/[]$E, len, cap: int, using location := #caller_location) -> T { - __slice_expr_error(fully_pathed_filename, int(line), int(column), 0, len, cap); + __slice_expr_error(file_path, int(line), int(column), 0, len, cap); data := cast(^E)alloc(len * size_of(E), align_of(E)); for i in 0..len do (data+i)^ = E{}; s := raw.Slice{data = data, len = len, cap = len}; @@ -523,14 +522,14 @@ make :: proc(T: type/[]$E, len, cap: int, using location := #caller_location) -> } make :: proc(T: type/[dynamic]$E, len: int = 8, using location := #caller_location) -> T { cap := len; - __slice_expr_error(fully_pathed_filename, int(line), int(column), 0, len, cap); + __slice_expr_error(file_path, int(line), int(column), 0, len, cap); data := cast(^E)alloc(cap * size_of(E), align_of(E)); for i in 0..len do (data+i)^ = E{}; s := raw.DynamicArray{data = data, len = len, cap = cap, allocator = context.allocator}; return (cast(^T)&s)^; } make :: proc(T: type/[dynamic]$E, len, cap: int, using location := #caller_location) -> T { - __slice_expr_error(fully_pathed_filename, int(line), int(column), 0, len, cap); + __slice_expr_error(file_path, int(line), int(column), 0, len, cap); data := cast(^E)alloc(cap * size_of(E), align_of(E)); for i in 0..len do (data+i)^ = E{}; s := raw.DynamicArray{data = data, len = len, cap = cap, allocator = context.allocator}; @@ -604,9 +603,9 @@ default_allocator :: proc() -> Allocator { assert :: proc(condition: bool, message := "", using location := #caller_location) -> bool #cc_contextless { if !condition { if len(message) > 0 { - fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n", fully_pathed_filename, line, column, message); + fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n", file_path, line, column, message); } else { - fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion\n", fully_pathed_filename, line, column); + fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion\n", file_path, line, column); } __debug_trap(); } @@ -615,9 +614,9 @@ assert :: proc(condition: bool, message := "", using location := #caller_locatio panic :: proc(message := "", using location := #caller_location) #cc_contextless { if len(message) > 0 { - fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n", fully_pathed_filename, line, column, message); + fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n", file_path, line, column, message); } else { - fmt.fprintf(os.stderr, "%s(%d:%d) Panic\n", fully_pathed_filename, line, column); + fmt.fprintf(os.stderr, "%s(%d:%d) Panic\n", file_path, line, column); } __debug_trap(); } @@ -683,6 +682,15 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #cc_contextless #inline { return utf8.decode_rune(s); } +__bounds_check_error_loc :: proc(using loc := #caller_location, index, count: int) #cc_contextless { + __bounds_check_error(file_path, int(line), int(column), index, count); +} +__slice_expr_error_loc :: proc(using loc := #caller_location, low, high, max: int) #cc_contextless { + __slice_expr_error(file_path, int(line), int(column), low, high, max); +} +__substring_expr_error_loc :: proc(using loc := #caller_location, low, high: int) #cc_contextless { + __substring_expr_error(file_path, int(line), int(column), low, high); +} __mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr #cc_contextless { if data == nil do return nil; diff --git a/core/_soft_numbers.odin b/core/_soft_numbers.odin index 21673165f..7093b75f0 100644 --- a/core/_soft_numbers.odin +++ b/core/_soft_numbers.odin @@ -5,17 +5,16 @@ __multi3 :: proc(a, b: u128) -> u128 #cc_c #link_name "__multi3" { lower_mask :: u128(~u64(0) >> bits_in_dword_2); - when ODIN_ENDIAN == "bit" { - TWords :: struct #raw_union { - all: u128; - using _: struct {lo, hi: u64;}; + TWords :: struct #raw_union { + all: u128; + using _: struct { + when ODIN_ENDIAN == "big" { + lo, hi: u64; + } else { + hi, lo: u64; + } }; - } else { - TWords :: struct #raw_union { - all: u128; - using _: struct {hi, lo: u64;}; - }; - } + }; r: TWords; t: u64; @@ -63,13 +62,13 @@ __i128_quo_mod :: proc(a, b: i128, rem: ^i128) -> (quo: i128) #cc_c #link_name " b = (a~s) - s; uquo: u128; - urem := __u128_quo_mod(transmute(u128, a), transmute(u128, b), &uquo); - iquo := transmute(i128, uquo); - irem := transmute(i128, urem); + urem := __u128_quo_mod(transmute(u128)a, transmute(u128)b, &uquo); + iquo := transmute(i128)uquo; + irem := transmute(i128)urem; iquo = (iquo~s) - s; irem = (irem~s) - s; - if rem != nil { rem^ = irem; } + if rem != nil do rem^ = irem; return iquo; } @@ -78,7 +77,7 @@ __u128_quo_mod :: proc(a, b: u128, rem: ^u128) -> (quo: u128) #cc_c #link_name " alo, ahi := u64(a), u64(a>>64); blo, bhi := u64(b), u64(b>>64); if b == 0 { - if rem != nil { rem^ = 0; } + if rem != nil do rem^ = 0; return u128(alo/blo); } diff --git a/core/fmt.odin b/core/fmt.odin index 2dd87b28f..7a5e08021 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -149,8 +149,7 @@ aprintf :: proc(fmt: string, args: ...any) -> string { } -// bprint* procedures return a string that was allocated with the current context -// They must be freed accordingly +// bprint* procedures return a string using a buffer from an array bprint :: proc(buf: []u8, args: ...any) -> string { sb := StringBuffer(buf[..0..len(buf)]); return sbprint(&sb, ...args); diff --git a/core/math.odin b/core/math.odin index 9c338de77..a113bcd87 100644 --- a/core/math.odin +++ b/core/math.odin @@ -60,19 +60,19 @@ sign :: proc(x: f64) -> f64 { if x >= 0 do return +1; return -1; } copy_sign :: proc(x, y: f32) -> f32 { - ix := transmute(u32, x); - iy := transmute(u32, y); + ix := transmute(u32)x; + iy := transmute(u32)y; ix &= 0x7fff_ffff; ix |= iy & 0x8000_0000; - return transmute(f32, ix); + return transmute(f32)ix; } copy_sign :: proc(x, y: f64) -> f64 { - ix := transmute(u64, x); - iy := transmute(u64, y); + ix := transmute(u64)x; + iy := transmute(u64)y; ix &= 0x7fff_ffff_ffff_ff; ix |= iy & 0x8000_0000_0000_0000; - return transmute(f64, ix); + return transmute(f64)ix; } round :: proc(x: f32) -> f32 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); } diff --git a/core/strconv.odin b/core/strconv.odin index a4a7a43f1..4a062b8a2 100644 --- a/core/strconv.odin +++ b/core/strconv.odin @@ -226,10 +226,10 @@ generic_ftoa :: proc(buf: []u8, val: f64, fmt: u8, prec, bit_size: int) -> []u8 flt: ^FloatInfo; match bit_size { case 32: - bits = u64(transmute(u32, f32(val))); + bits = u64(transmute(u32)f32(val)); flt = &_f32_info; case 64: - bits = transmute(u64, val); + bits = transmute(u64)val; flt = &_f64_info; case: panic("strconv: invalid bit_size"); diff --git a/core/thread.odin b/core/thread.odin index b6a39cd89..82f26eef5 100644 --- a/core/thread.odin +++ b/core/thread.odin @@ -5,7 +5,7 @@ import win32 "sys/windows.odin"; Thread :: struct { using specific: OsSpecific; procedure: Proc; - data: rawptr; + data: any; user_index: int; init_context: Context; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 3f5530da4..4b5ffdc64 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -160,6 +160,7 @@ String odin_root_dir(void) { } array_init_count(&path_buf, heap_allocator(), 300); + defer (array_free(&path_buf)); len = 0; for (;;) { @@ -179,7 +180,10 @@ String odin_root_dir(void) { tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + defer (gb_temp_arena_memory_end(tmp)); + text = gb_alloc_array(string_buffer_allocator, u8, len + 1); + gb_memmove(text, &path_buf[0], len); path = make_string(text, len); @@ -194,10 +198,6 @@ String odin_root_dir(void) { global_module_path = path; global_module_path_set = true; - gb_temp_arena_memory_end(tmp); - - array_free(&path_buf); - return path; } #endif @@ -267,7 +267,7 @@ String get_fullpath_core(gbAllocator a, String path) { } -String const ODIN_VERSION = str_lit("0.6.0-dev"); +String const ODIN_VERSION = str_lit("0.6.0"); void init_build_context(void) { BuildContext *bc = &build_context; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 72ca6d7c4..8b2ab0941 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -319,14 +319,12 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) { if (is_type_integer(x) && is_type_integer(y)) { GB_ASSERT(x->kind == Type_Basic); GB_ASSERT(y->kind == Type_Basic); - if (x->Basic.size == y->Basic.size) { - continue; - } + i64 sx = type_size_of(heap_allocator(), x); + i64 sy = type_size_of(heap_allocator(), y); + if (sx == sy) continue; } - if (!are_types_identical(x, y)) { - return false; - } + if (!are_types_identical(x, y)) return false; } for (isize i = 0; i < a->result_count; i++) { Type *x = base_type(a->results->Tuple.variables[i]->type); @@ -338,9 +336,9 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) { if (is_type_integer(x) && is_type_integer(y)) { GB_ASSERT(x->kind == Type_Basic); GB_ASSERT(y->kind == Type_Basic); - if (x->Basic.size == y->Basic.size) { - continue; - } + i64 sx = type_size_of(heap_allocator(), x); + i64 sy = type_size_of(heap_allocator(), y); + if (sx == sy) continue; } if (!are_types_identical(x, y)) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4ac092b64..ae7018a8c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2121,6 +2121,10 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari type = determine_type_from_polymorphic(c, type, op); if (type == t_invalid) { success = false; + } else if (!c->context.no_polymorphic_errors) { + // NOTE(bill): The type should be determined now and thus, no need to determine the type any more + is_type_polymorphic_type = false; + // is_type_polymorphic_type = is_type_polymorphic(base_type(type)); } } @@ -3959,6 +3963,43 @@ void check_cast(Checker *c, Operand *x, Type *type) { x->type = type; } +bool check_transmute(Checker *c, AstNode *node, Operand *o, Type *t) { + if (o->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(o->expr); + error(o->expr, "Cannot transmute a constant expression: `%s`", expr_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return false; + } + + if (is_type_untyped(o->type)) { + gbString expr_str = expr_to_string(o->expr); + error(o->expr, "Cannot transmute untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return false; + } + + i64 srcz = type_size_of(c->allocator, o->type); + i64 dstz = type_size_of(c->allocator, t); + if (srcz != dstz) { + gbString expr_str = expr_to_string(o->expr); + gbString type_str = type_to_string(t); + error(o->expr, "Cannot transmute `%s` to `%s`, %lld vs %lld bytes", expr_str, type_str, srcz, dstz); + gb_string_free(type_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return false; + } + + o->mode = Addressing_Value; + o->type = t; + return true; +} + bool check_binary_vector_expr(Checker *c, Token op, Operand *x, Operand *y) { if (is_type_vector(x->type) && !is_type_vector(y->type)) { if (check_is_assignable_to(c, y, x->type)) { @@ -4870,7 +4911,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id case BuiltinProc_align_of: case BuiltinProc_offset_of: case BuiltinProc_type_info_of: - case BuiltinProc_transmute: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; default: @@ -5891,6 +5931,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } } break; + #if 0 case BuiltinProc_transmute: { Operand op = {}; check_expr_or_type(c, &op, ce->args[0]); @@ -5940,6 +5981,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id o->mode = Addressing_Value; o->type = t; } break; + #endif } return true; @@ -7814,7 +7856,18 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t Type *type = o->type; check_expr_base(c, o, tc->expr, type); if (o->mode != Addressing_Invalid) { - check_cast(c, o, type); + switch (tc->token.kind) { + case Token_transmute: + check_transmute(c, node, o, type); + break; + case Token_cast: + check_cast(c, o, type); + break; + default: + error(node, "Invalid AST: Invalid casting expression"); + o->mode = Addressing_Invalid; + break; + } } return Expr_Expr; case_end; diff --git a/src/checker.cpp b/src/checker.cpp index ec907cc1d..c243c704b 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -61,8 +61,6 @@ enum BuiltinProcId { BuiltinProc_abs, BuiltinProc_clamp, - BuiltinProc_transmute, - BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures BuiltinProc_COUNT, @@ -107,8 +105,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("abs"), 1, false, Expr_Expr}, {STR_LIT("clamp"), 3, false, Expr_Expr}, - {STR_LIT("transmute"), 2, false, Expr_Expr}, - {STR_LIT(""), 0, true, Expr_Expr}, // DIRECTIVE }; @@ -2172,33 +2168,37 @@ void check_import_entities(Checker *c, Map *file_scopes) { scope->has_been_imported = true; if (id->import_name.string == ".") { - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - Entity *e = scope->elements.entries[elem_index].value; - if (e->scope == parent_scope) { - continue; - } - - - if (!is_entity_kind_exported(e->kind)) { - continue; - } - if (id->is_import) { - if (is_entity_exported(e)) { - // TODO(bill): Should these entities be imported but cause an error when used? - bool ok = add_entity(c, parent_scope, e->identifier, e); - if (ok) { - map_set(&parent_scope->implicit, hash_entity(e), true); - } + if (parent_scope->is_global) { + error(id->import_name, "#shared_global_scope imports cannot use ."); + } else { + // NOTE(bill): Add imported entities to this file's scope + for_array(elem_index, scope->elements.entries) { + Entity *e = scope->elements.entries[elem_index].value; + if (e->scope == parent_scope) { + continue; + } + + + if (!is_entity_kind_exported(e->kind)) { + continue; + } + if (id->is_import) { + if (is_entity_exported(e)) { + // TODO(bill): Should these entities be imported but cause an error when used? + bool ok = add_entity(c, parent_scope, e->identifier, e); + if (ok) { + map_set(&parent_scope->implicit, hash_entity(e), true); + } + } + } else { + add_entity(c, parent_scope, e->identifier, e); } - } else { - add_entity(c, parent_scope, e->identifier, e); } } } else { String import_name = path_to_entity_name(id->import_name.string, id->fullpath); if (is_blank_ident(import_name)) { - error(token, "File name, %.*s, cannot be as an import name as it is not a valid identifier", LIT(id->import_name.string)); + error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); } else { GB_ASSERT(id->import_name.pos.line != 0); id->import_name.string = import_name; diff --git a/src/ir.cpp b/src/ir.cpp index a3655ae25..62603c801 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2435,52 +2435,69 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { Type *t = base_type(ir_type(s)); Type *result_type = nullptr; - if (is_type_struct(t)) { + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_string: + switch (index) { + case 0: result_type = t_u8_ptr; break; + case 1: result_type = t_int; break; + } + break; + case Basic_any: + switch (index) { + case 0: result_type = t_rawptr; break; + case 1: result_type = t_type_info_ptr; break; + } + break; + case Basic_complex64: case Basic_complex128: + { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = ft; break; + case 1: result_type = ft; break; + } + } break; + } + break; + case Type_Struct: result_type = t->Struct.fields[index]->type; - } else if (is_type_union(t)) { + break; + case Type_Union: GB_ASSERT(index == -1); return ir_emit_union_tag_value(proc, s); - } else if (is_type_tuple(t)) { + case Type_Tuple: GB_ASSERT(t->Tuple.variables.count > 0); result_type = t->Tuple.variables[index]->type; - } else if (is_type_complex(t)) { - Type *ft = base_complex_elem_type(t); - switch (index) { - case 0: result_type = ft; break; - case 1: result_type = ft; break; - } - } else if (is_type_slice(t)) { + break; + case Type_Slice: switch (index) { case 0: result_type = make_type_pointer(a, t->Slice.elem); break; case 1: result_type = t_int; break; case 2: result_type = t_int; break; } - } else if (is_type_string(t)) { - switch (index) { - case 0: result_type = t_u8_ptr; break; - case 1: result_type = t_int; break; - } - } else if (is_type_any(t)) { - switch (index) { - case 0: result_type = t_rawptr; break; - case 1: result_type = t_type_info_ptr; break; - } - } else if (is_type_dynamic_array(t)) { + break; + case Type_DynamicArray: switch (index) { case 0: result_type = make_type_pointer(a, t->DynamicArray.elem); break; case 1: result_type = t_int; break; case 2: result_type = t_int; break; case 3: result_type = t_allocator; break; } - } else if (is_type_map(t)) { + break; + + case Type_Map: { generate_map_internal_types(a, t); Type *gst = t->Map.generated_struct_type; switch (index) { case 0: result_type = gst->Struct.fields[0]->type; break; case 1: result_type = gst->Struct.fields[1]->type; break; } - } else { + } break; + + default: GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ir_type(s)), index); + break; } GB_ASSERT(result_type != nullptr); @@ -2492,6 +2509,7 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) { GB_ASSERT(sel.index.count > 0); Type *type = type_deref(ir_type(e)); + gbAllocator a = proc->module->allocator; for_array(i, sel.index) { i32 index = cast(i32)sel.index[i]; @@ -2502,21 +2520,23 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) { } type = core_type(type); - if (is_type_raw_union(type)) { type = type->Struct.fields[index]->type; - e = ir_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); + e = ir_emit_conv(proc, e, make_type_pointer(a, type)); } else if (type->kind == Type_Union) { GB_ASSERT(index == -1); type = t_type_info_ptr; e = ir_emit_struct_ep(proc, e, index); } else if (type->kind == Type_Struct) { type = type->Struct.fields[index]->type; - e = ir_emit_struct_ep(proc, e, index); + if (type->Struct.is_raw_union) { + } else { + e = ir_emit_struct_ep(proc, e, index); + } } else if (type->kind == Type_Tuple) { type = type->Tuple.variables[index]->type; e = ir_emit_struct_ep(proc, e, index); - }else if (type->kind == Type_Basic) { + } else if (type->kind == Type_Basic) { switch (type->Basic.kind) { case Basic_any: { if (index == 0) { @@ -3867,11 +3887,6 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv return ir_type_info(proc, t); } break; - case BuiltinProc_transmute: { - irValue *x = ir_build_expr(proc, ce->args[1]); - return ir_emit_transmute(proc, x, tv.type); - } - case BuiltinProc_len: { irValue *v = ir_build_expr(proc, ce->args[0]); Type *t = base_type(ir_type(v)); @@ -4694,7 +4709,13 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { case_ast_node(tc, TypeCast, expr); irValue *e = ir_build_expr(proc, tc->expr); - return ir_emit_conv(proc, e, tv.type); + switch (tc->token.kind) { + case Token_cast: + return ir_emit_conv(proc, e, tv.type); + case Token_transmute: + return ir_emit_transmute(proc, e, tv.type); + } + GB_PANIC("Invalid AST TypeCast"); case_end; case_ast_node(ue, UnaryExpr, expr); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c6fb56616..58e89dbf6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -275,7 +275,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { case Type_DynamicArray: ir_fprintf(f, "{"); ir_print_type(f, m, t->DynamicArray.elem); - ir_fprintf(f, "*, i%lld, i%lld,", word_bits, word_bits); + ir_fprintf(f, "*, i%lld, i%lld, ", word_bits, word_bits); ir_print_type(f, m, t_allocator); ir_fprintf(f, "}"); return; @@ -523,7 +523,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * if (!has_defaults) { ir_fprintf(f, "zeroinitializer"); } else { - ir_print_compound_element(f, m, empty_exact_value, type); + ir_print_exact_value(f, m, empty_exact_value, type); } break; } @@ -881,9 +881,6 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { case irInstr_Store: { Type *type = type_deref(ir_type(instr->Store.address)); ir_fprintf(f, "store "); - if (instr->Store.atomic) { - ir_fprintf(f, "atomic "); - } ir_print_type(f, m, type); ir_fprintf(f, " "); ir_print_value(f, m, instr->Store.value, type); @@ -891,29 +888,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_print_type(f, m, type); ir_fprintf(f, "* "); ir_print_value(f, m, instr->Store.address, type); - if (instr->Store.atomic) { - // TODO(bill): Do ordering - ir_fprintf(f, " unordered"); - ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type)); - } ir_fprintf(f, "\n"); } break; case irInstr_Load: { Type *type = instr->Load.type; ir_fprintf(f, "%%%d = load ", value->index); - // if (is_type_atomic(type)) { - // ir_fprintf(f, "atomic "); - // } ir_print_type(f, m, type); ir_fprintf(f, ", "); ir_print_type(f, m, type); ir_fprintf(f, "* "); ir_print_value(f, m, instr->Load.address, type); - // if (is_type_atomic(type)) { - // TODO(bill): Do ordering - // ir_fprintf(f, " unordered"); - // } ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type)); } break; @@ -1409,7 +1394,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { if (param_index > 0) ir_fprintf(f, ", "); ir_print_type(f, m, t_context_ptr); - ir_fprintf(f, " noalias nonnull"); + ir_fprintf(f, " noalias nonnull "); ir_print_value(f, m, call->context_ptr, t_context_ptr); } ir_fprintf(f, ")\n"); @@ -1568,26 +1553,27 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { #endif case irInstr_DebugDeclare: { - /* irInstrDebugDeclare *dd = &instr->DebugDeclare; + irInstrDebugDeclare *dd = &instr->DebugDeclare; Type *vt = ir_type(dd->value); irDebugInfo *di = dd->debug_info; Entity *e = dd->entity; String name = e->token.string; TokenPos pos = e->token.pos; // gb_printf("debug_declare %.*s\n", LIT(dd->entity->token.string)); + ir_fprintf(f, "; "); ir_fprintf(f, "call void @llvm.dbg.declare("); ir_fprintf(f, "metadata "); ir_print_type(f, m, vt); ir_fprintf(f, " "); ir_print_value(f, m, dd->value, vt); ir_fprintf(f, ", metadata !DILocalVariable(name: \""); - ir_print_escape_string(f, name, false); + ir_print_escape_string(f, name, false, false); ir_fprintf(f, "\", scope: !%d, line: %td)", di->id, pos.line); ir_fprintf(f, ", metadata !DIExpression()"); ir_fprintf(f, ")"); ir_fprintf(f, ", !dbg !DILocation(line: %td, column: %td, scope: !%d)", pos.line, pos.column, di->id); - ir_fprintf(f, "\n"); */ + ir_fprintf(f, "\n"); } break; } } diff --git a/src/parser.cpp b/src/parser.cpp index b7209058e..9ef0759ef 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2850,6 +2850,13 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) { Token close = expect_token(f, Token_CloseParen); return ast_type_cast(f, token, type, parse_unary_expr(f, lhs)); } break; + case Token_transmute: { + Token token = expect_token(f, Token_transmute); + Token open = expect_token_after(f, Token_OpenParen, "transmute"); + AstNode *type = parse_type(f); + Token close = expect_token(f, Token_CloseParen); + return ast_type_cast(f, token, type, parse_unary_expr(f, lhs)); + } break; } AstNode *operand = parse_operand(f, lhs); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 68ecf6178..cd55edac5 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -114,6 +114,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_static, "static"), \ TOKEN_KIND(Token_dynamic, "dynamic"), \ TOKEN_KIND(Token_cast, "cast"), \ + TOKEN_KIND(Token_transmute, "transmute"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_context, "context"), \ TOKEN_KIND(Token_push_context, "push_context"), \ diff --git a/src/types.cpp b/src/types.cpp index f738dd50f..86b673796 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2141,7 +2141,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) { i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) { t = base_type(t); - if (t->kind == Type_Struct && !t->Struct.is_raw_union) { + if (t->kind == Type_Struct) { type_set_offsets(allocator, t); if (gb_is_between(index, 0, t->Struct.fields.count-1)) { return t->Struct.offsets[index]; @@ -2155,7 +2155,7 @@ i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) { if (t->Basic.kind == Basic_string) { switch (index) { case 0: return 0; // data - case 1: return build_context.word_size; // count + case 1: return build_context.word_size; // len } } else if (t->Basic.kind == Basic_any) { switch (index) { @@ -2166,16 +2166,21 @@ i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) { } else if (t->kind == Type_Slice) { switch (index) { case 0: return 0; // data - case 1: return 1*build_context.word_size; // count - case 2: return 2*build_context.word_size; // capacity + case 1: return 1*build_context.word_size; // len + case 2: return 2*build_context.word_size; // cap } } else if (t->kind == Type_DynamicArray) { switch (index) { case 0: return 0; // data - case 1: return 1*build_context.word_size; // count - case 2: return 2*build_context.word_size; // capacity + case 1: return 1*build_context.word_size; // len + case 2: return 2*build_context.word_size; // cap case 3: return 3*build_context.word_size; // allocator } + } else if (t->kind == Type_Union) { + i64 s = type_size_of(allocator, t); + switch (index) { + case -1: return align_formula(t->Union.variant_block_size, build_context.word_size); // __type_info + } } return 0; }