Browse Source

Merge branch 'odin-lang:master' into master

avanspector 7 months ago
parent
commit
be7799459b
92 changed files with 2711 additions and 1190 deletions
  1. 2 0
      base/builtin/builtin.odin
  2. 4 2
      base/intrinsics/intrinsics.odin
  3. 60 3
      base/runtime/core_builtin.odin
  4. 34 11
      base/runtime/dynamic_map_internal.odin
  5. 2 2
      build_odin.sh
  6. 230 148
      core/encoding/base32/base32.odin
  7. 227 0
      core/encoding/base32/base32_test.odin
  8. 2 2
      core/encoding/json/unmarshal.odin
  9. 1 1
      core/image/general.odin
  10. 1 129
      core/image/png/helpers.odin
  11. 7 3
      core/io/util.odin
  12. 66 18
      core/math/linalg/general.odin
  13. 66 18
      core/math/linalg/glsl/linalg_glsl.odin
  14. 66 18
      core/math/linalg/hlsl/linalg_hlsl.odin
  15. 0 24
      core/math/rand/rand.odin
  16. 0 23
      core/mem/allocators.odin
  17. 1 8
      core/mem/mem.odin
  18. 2 1
      core/mem/virtual/arena.odin
  19. 1 1
      core/os/os2/file.odin
  20. 27 54
      core/os/os2/file_linux.odin
  21. 29 20
      core/os/os2/file_posix.odin
  22. 37 16
      core/os/os2/file_windows.odin
  23. 1 1
      core/os/os2/heap_linux.odin
  24. 0 2
      core/os/os2/path_linux.odin
  25. 2 2
      core/os/os2/pipe_linux.odin
  26. 2 2
      core/os/os2/pipe_posix.odin
  27. 10 1
      core/os/os2/process.odin
  28. 11 36
      core/os/os2/process_linux.odin
  29. 2 0
      core/prof/spall/doc.odin
  30. 59 37
      core/sys/linux/bits.odin
  31. 2 2
      core/sys/linux/helpers.odin
  32. 25 15
      core/sys/linux/sys.odin
  33. 166 66
      core/sys/linux/types.odin
  34. 21 1
      core/sys/windows/kernel32.odin
  35. 26 2
      core/sys/windows/types.odin
  36. 1 0
      core/sys/windows/user32.odin
  37. 153 141
      core/time/timezone/tz_windows.odin
  38. 1 0
      examples/demo/demo.odin
  39. 12 6
      src/build_settings.cpp
  40. 48 50
      src/check_decl.cpp
  41. 53 10
      src/check_expr.cpp
  42. 8 1
      src/check_type.cpp
  43. 51 1
      src/checker.cpp
  44. 2 0
      src/checker.hpp
  45. 1 0
      src/entity.cpp
  46. 0 2
      src/llvm_backend.cpp
  47. 8 1
      src/llvm_backend.hpp
  48. 11 5
      src/llvm_backend_expr.cpp
  49. 17 1
      src/llvm_backend_general.cpp
  50. 5 1
      src/llvm_backend_proc.cpp
  51. 52 29
      src/llvm_backend_stmt.cpp
  52. 20 7
      src/llvm_backend_utility.cpp
  53. 3 3
      src/main.cpp
  54. 67 3
      src/parser.cpp
  55. 2 0
      src/parser.hpp
  56. 1 0
      tests/core/encoding/cbor/test_core_cbor.odin
  57. 1 0
      tests/core/fmt/test_core_fmt.odin
  58. 1 0
      tests/core/hash/test_core_hash.odin
  59. 1 0
      tests/core/hash/test_vectors_xxhash.odin
  60. 1 0
      tests/core/image/test_core_image.odin
  61. 1 0
      tests/core/net/test_core_net.odin
  62. 99 1
      tests/core/runtime/test_core_runtime.odin
  63. 1 0
      tests/core/slice/test_core_slice.odin
  64. 1 0
      tests/issues/run.bat
  65. 1 0
      tests/issues/run.sh
  66. 198 0
      tests/issues/test_issue_4584.odin
  67. 1 0
      tests/issues/test_issue_829.odin
  68. 3 11
      vendor/box2d/box2d.odin
  69. 5 0
      vendor/box2d/build_box2d.sh
  70. 3 1
      vendor/box2d/wasm.Makefile
  71. 58 13
      vendor/cgltf/cgltf.odin
  72. BIN
      vendor/cgltf/lib/cgltf.lib
  73. BIN
      vendor/cgltf/lib/cgltf_wasm.o
  74. 338 200
      vendor/cgltf/src/cgltf.h
  75. 55 12
      vendor/cgltf/src/cgltf_write.h
  76. 2 2
      vendor/libc/include/math.h
  77. 2 2
      vendor/libc/math.odin
  78. 1 0
      vendor/libc/stdio.odin
  79. 5 0
      vendor/raylib/raygui.odin
  80. 5 0
      vendor/raylib/raylib.odin
  81. 1 1
      vendor/raylib/raymath.odin
  82. 6 1
      vendor/raylib/rlgl/rlgl.odin
  83. BIN
      vendor/raylib/wasm/libraygui.a
  84. BIN
      vendor/raylib/wasm/libraylib.a
  85. 34 12
      vendor/vulkan/_gen/create_vulkan_odin_wrapper.py
  86. 56 0
      vendor/vulkan/_gen/vulkan_xcb.h
  87. 56 0
      vendor/vulkan/_gen/vulkan_xlib.h
  88. 6 0
      vendor/vulkan/core.odin
  89. 4 0
      vendor/vulkan/enums.odin
  90. 16 0
      vendor/vulkan/procedures.odin
  91. 37 4
      vendor/vulkan/structs.odin
  92. 4 0
      vendor/x11/xlib/xlib.odin

+ 2 - 0
base/builtin/builtin.odin

@@ -1,6 +1,8 @@
 // This is purely for documentation
 package builtin
 
+import "base:runtime"
+
 nil   :: nil
 false :: 0!=0
 true  :: 0==0

+ 4 - 2
base/intrinsics/intrinsics.odin

@@ -2,6 +2,8 @@
 #+build ignore
 package intrinsics
 
+import "base:runtime"
+
 // Package-Related
 is_package_imported :: proc(package_name: string) -> bool ---
 
@@ -72,7 +74,7 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..=
 prefetch_write_data        :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
 
 // Compiler Hints
-expect :: proc(val, expected_val: T) -> T ---
+expect :: proc(val, expected_val: $T) -> T ---
 
 // Linux and Darwin Only
 syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
@@ -219,7 +221,7 @@ type_map_cell_info :: proc($T: typeid)           -> ^runtime.Map_Cell_Info ---
 type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
 type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
 
-type_has_shared_fields :: proc($U, $V: typeid) -> bool typeid where type_is_struct(U), type_is_struct(V) ---
+type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
 
 constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
 

+ 60 - 3
base/runtime/core_builtin.odin

@@ -826,10 +826,12 @@ _resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem,
 		return nil
 	}
 
+	if should_zero && a.len < length {
+		num_reused := min(a.cap, length) - a.len
+		intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], num_reused*size_of_elem)
+	}
+
 	if length <= a.cap {
-		if should_zero && a.len < length {
-			intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], (length-a.len)*size_of_elem)
-		}
 		a.len = max(length, 0)
 		return nil
 	}
@@ -936,6 +938,32 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
 	return
 }
 
+/*
+Retrieves a pointer to the key and value for a possibly just inserted entry into the map.
+
+If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`.
+Otherwise the existing entry is left untouched and pointers to its key and value are returned.
+
+If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned.
+
+If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`.
+
+WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key.
+If that is not the case you will corrupt the map.
+*/
+@(builtin, require_results)
+map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) {
+	key := key
+	zero: V
+
+	_key_ptr, _value_ptr: rawptr
+	_key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc)
+
+	key_ptr   = (^K)(_key_ptr)
+	value_ptr = (^V)(_value_ptr)
+	return
+}
+
 
 @builtin
 card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
@@ -964,6 +992,24 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc :=
 	}
 }
 
+// Evaluates the condition and aborts the program iff the condition is
+// false.  This routine ignores `ODIN_DISABLE_ASSERT`, and will always
+// execute.
+@builtin
+ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
+	if !condition {
+		@(cold)
+		internal :: proc(message: string, loc: Source_Code_Location) {
+			p := context.assertion_failure_proc
+			if p == nil {
+				p = default_assertion_failure_proc
+			}
+			p("unsatisfied ensure", message, loc)
+		}
+		internal(message, loc)
+	}
+}
+
 @builtin
 panic :: proc(message: string, loc := #caller_location) -> ! {
 	p := context.assertion_failure_proc
@@ -999,6 +1045,17 @@ assert_contextless :: proc "contextless" (condition: bool, message := #caller_ex
 	}
 }
 
+@builtin
+ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
+	if !condition {
+		@(cold)
+		internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
+			default_assertion_contextless_failure_proc("unsatisfied ensure", message, loc)
+		}
+		internal(message, loc)
+	}
+}
+
 @builtin
 panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
 	default_assertion_contextless_failure_proc("panic", message, loc)

+ 34 - 11
base/runtime/dynamic_map_internal.odin

@@ -158,21 +158,21 @@ map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T
 	} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
 		// Likely case, N is a power of two because T is a power of two.
 
+		// Unique case, no need to index data here since only one element.
+		when N == 1 {
+			return &cells[index].data[0]
+		}
+
 		// Compute the integer log 2 of N, this is the shift amount to index the
 		// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
 		// constant, hence this approach. We only need to check up to N = 64.
-		SHIFT :: 1 when N < 2  else
-		         2 when N < 4  else
-		         3 when N < 8  else
-		         4 when N < 16 else
-		         5 when N < 32 else 6
+		SHIFT :: 1 when N == 2  else
+		         2 when N == 4  else
+		         3 when N == 8  else
+		         4 when N == 16 else
+		         5 when N == 32 else 6
 		#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
-		// Unique case, no need to index data here since only one element.
-		when N == 1 {
-			return &cells[index >> SHIFT].data[0]
-		} else {
-			return &cells[index >> SHIFT].data[index & (N - 1)]
-		}
+		return &cells[index >> SHIFT].data[index & (N - 1)]
 	} else {
 		// Least likely (and worst case), we pay for a division operation but we
 		// assume the compiler does not actually generate a division. N will be in the
@@ -941,6 +941,29 @@ __dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
 	return nil, rawptr(result)
 }
 
+__dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key: rawptr, zero: rawptr, loc := #caller_location) -> (key_ptr: rawptr, value_ptr: rawptr, just_inserted: bool, err: Allocator_Error) {
+	hash := info.key_hasher(key, map_seed(m^))
+
+	if key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
+		return
+	}
+
+	has_grown: bool
+	if err, has_grown = __dynamic_map_check_grow(m, info, loc); err != nil {
+		return
+	} else if has_grown {
+		hash = info.key_hasher(key, map_seed(m^))
+	}
+
+	value_ptr = rawptr(map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(zero)))
+	assert(value_ptr != nil)
+	key_ptr = rawptr(map_cell_index_dynamic(map_data(m^), info.ks, map_desired_position(m^, hash)))
+
+	m.len += 1
+	just_inserted = true
+	return
+}
+
 
 // IMPORTANT: USED WITHIN THE COMPILER
 @(private)

+ 2 - 2
build_odin.sh

@@ -110,8 +110,8 @@ Linux)
 	LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
 	;;
 OpenBSD)
-	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
-	LDFLAGS="$LDFLAGS -liconv"
+	CXXFLAGS="$CXXFLAGS -I/usr/local/include $($LLVM_CONFIG --cxxflags --ldflags)"
+	LDFLAGS="$LDFLAGS -L/usr/local/lib -liconv"
 	LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
 	;;
 Haiku)

+ 230 - 148
core/encoding/base32/base32.odin

@@ -1,148 +1,230 @@
-package encoding_base32
-
-// @note(zh): Encoding utility for Base32
-// A secondary param can be used to supply a custom alphabet to
-// @link(encode) and a matching decoding table to @link(decode).
-// If none is supplied it just uses the standard Base32 alphabet.
-// Incase your specific version does not use padding, you may
-// truncate it from the encoded output.
-
-ENC_TABLE := [32]byte {
-	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
-	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
-	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
-	'Y', 'Z', '2', '3', '4', '5', '6', '7',
-}
-
-PADDING :: '='
-
-DEC_TABLE := [?]u8 {
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0, 26, 27, 28, 29, 30, 31,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
-	 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-}
-
-encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
-	out_length := (len(data) + 4) / 5 * 8
-	out := make([]byte, out_length)
-	_encode(out, data)
-	return string(out)
-}
-
-@private
-_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
-	out := out
-	data := data
-
-	for len(data) > 0 {
-		carry: byte
-		switch len(data) {
-		case:
-			out[7] = ENC_TABLE[data[4] & 0x1f]
-			carry = data[4] >> 5
-			fallthrough
-		case 4:
-			out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
-			out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
-			carry = data[3] >> 7
-			fallthrough
-		case 3:
-			out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
-			carry = (data[2] >> 4) & 0x1f
-			fallthrough
-		case 2:
-			out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
-			out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
-			carry = (data[1] >> 6) & 0x1f
-			fallthrough
-		case 1:
-			out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
-			out[0] = ENC_TABLE[data[0] >> 3]
-		}
-
-		if len(data) < 5 {
-			out[7] = byte(PADDING)
-			if len(data) < 4 {
-				out[6] = byte(PADDING)
-				out[5] = byte(PADDING)
-				if len(data) < 3 {
-					out[4] = byte(PADDING)
-					if len(data) < 2 {
-						out[3] = byte(PADDING)
-						out[2] = byte(PADDING)
-					}
-				}
-			}
-			break
-		}
-		data = data[5:]
-		out = out[8:]
-	}
-}
-
-decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
-	if len(data) == 0 {
-		return nil
-	}
-
-	outi := 0
-	data := data
-
-	out := make([]byte, len(data) / 8 * 5, allocator)
-	end := false
-	for len(data) > 0 && !end {
-		dbuf : [8]byte
-		dlen := 8
-
-		for j := 0; j < 8; {
-			if len(data) == 0 {
-				dlen, end = j, true
-				break
-			}
-			input := data[0]
-			data = data[1:]
-			if input == byte(PADDING) && j >= 2 && len(data) < 8 {
-				assert(!(len(data) + j < 8 - 1), "Corrupted input")
-				for k := 0; k < 8-1-j; k +=1 {
-					assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
-				}
-				dlen, end = j, true
-				assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
-				break
-			}
-			dbuf[j] = DEC_TABLE[input]
-			assert(dbuf[j] != 0xff, "Corrupted input")
-			j += 1
-		}
-
-		switch dlen {
-		case 8:
-			out[outi + 4] = dbuf[6] << 5 | dbuf[7]
-			fallthrough
-		case 7:
-			out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
-			fallthrough
-		case 5:
-			out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
-			fallthrough
-		case 4:
-			out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
-			fallthrough
-		case 2:
-			out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
-		}
-		outi += 5
-	}
-	return out
-}
+// Base32 encoding/decoding implementation as specified in RFC 4648.
+// [[ More; https://www.rfc-editor.org/rfc/rfc4648.html ]]
+package encoding_base32
+
+// @note(zh): Encoding utility for Base32
+// A secondary param can be used to supply a custom alphabet to
+// @link(encode) and a matching decoding table to @link(decode).
+// If none is supplied it just uses the standard Base32 alphabet.
+// In case your specific version does not use padding, you may
+// truncate it from the encoded output.
+
+// Error represents errors that can occur during base32 decoding operations.
+// As per RFC 4648:
+// - Section 3.3: Invalid character handling
+// - Section 3.2: Padding requirements
+// - Section 6: Base32 encoding specifics (including block size requirements)
+Error :: enum {
+	None,
+	Invalid_Character, // Input contains characters outside the specified alphabet
+	Invalid_Length,    // Input length is not valid for base32 (must be a multiple of 8 with proper padding)
+	Malformed_Input,   // Input has improper structure (wrong padding position or incomplete groups)
+}
+
+Validate_Proc :: #type proc(c: byte) -> bool
+
+@private
+_validate_default :: proc(c: byte) -> bool {
+	return (c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')
+}
+
+@(rodata)
+ENC_TABLE := [32]byte {
+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+	'Y', 'Z', '2', '3', '4', '5', '6', '7',
+}
+
+PADDING :: '='
+
+@(rodata)
+DEC_TABLE := [256]u8 {
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0, 26, 27, 28, 29, 30, 31,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
+	 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+}
+
+encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
+	out_length := (len(data) + 4) / 5 * 8
+	out := make([]byte, out_length, allocator)
+	_encode(out, data, ENC_TBL)
+	return string(out[:])
+}
+
+@private
+_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
+	out := out
+	data := data
+
+	for len(data) > 0 {
+		carry: byte
+		switch len(data) {
+		case:
+			out[7] = ENC_TBL[data[4] & 0x1f]
+			carry = data[4] >> 5
+			fallthrough
+		case 4:
+			out[6] = ENC_TBL[carry | (data[3] << 3) & 0x1f]
+			out[5] = ENC_TBL[(data[3] >> 2) & 0x1f]
+			carry = data[3] >> 7
+			fallthrough
+		case 3:
+			out[4] = ENC_TBL[carry | (data[2] << 1) & 0x1f]
+			carry = (data[2] >> 4) & 0x1f
+			fallthrough
+		case 2:
+			out[3] = ENC_TBL[carry | (data[1] << 4) & 0x1f]
+			out[2] = ENC_TBL[(data[1] >> 1) & 0x1f]
+			carry = (data[1] >> 6) & 0x1f
+			fallthrough
+		case 1:
+			out[1] = ENC_TBL[carry | (data[0] << 2) & 0x1f]
+			out[0] = ENC_TBL[data[0] >> 3]
+		}
+
+		if len(data) < 5 {
+			out[7] = byte(PADDING)
+			if len(data) < 4 {
+				out[6] = byte(PADDING)
+				out[5] = byte(PADDING)
+				if len(data) < 3 {
+					out[4] = byte(PADDING)
+					if len(data) < 2 {
+						out[3] = byte(PADDING)
+						out[2] = byte(PADDING)
+					}
+				}
+			}
+			break
+		}
+		data = data[5:]
+		out = out[8:]
+	}
+}
+
+@(optimization_mode="favor_size")
+decode :: proc(
+  data: string,
+  DEC_TBL := DEC_TABLE,
+  validate: Validate_Proc = _validate_default,
+  allocator := context.allocator) -> (out: []byte, err: Error) {
+	if len(data) == 0 {
+		return nil, .None
+	}
+
+	// Check minimum length requirement first
+	if len(data) < 2 {
+		return nil, .Invalid_Length
+	}
+
+	// Validate characters using provided validation function
+	for i := 0; i < len(data); i += 1 {
+		c := data[i]
+		if c == byte(PADDING) {
+			break
+		}
+		if !validate(c) {
+			return nil, .Invalid_Character
+		}
+	}
+
+	// Validate padding and length
+	data_len := len(data)
+	padding_count := 0
+	for i := data_len - 1; i >= 0; i -= 1 {
+		if data[i] != byte(PADDING) {
+			break
+		}
+		padding_count += 1
+	}
+
+	// Check for proper padding and length combinations
+	if padding_count > 0 {
+		// Verify no padding in the middle
+		for i := 0; i < data_len - padding_count; i += 1 {
+			if data[i] == byte(PADDING) {
+				return nil, .Malformed_Input
+			}
+		}
+
+		content_len := data_len - padding_count
+		mod8 := content_len % 8
+		required_padding: int
+		switch mod8 {
+		case 2: required_padding = 6 // 2 chars need 6 padding chars
+		case 4: required_padding = 4 // 4 chars need 4 padding chars
+		case 5: required_padding = 3 // 5 chars need 3 padding chars
+		case 7: required_padding = 1 // 7 chars need 1 padding char
+		case: required_padding = 0
+		}
+
+		if required_padding > 0 {
+			if padding_count != required_padding {
+				return nil, .Malformed_Input
+			}
+		} else if mod8 != 0 {
+			return nil, .Malformed_Input
+		}
+	} else {
+		// No padding - must be multiple of 8
+		if data_len % 8 != 0 {
+			return nil, .Malformed_Input
+		}
+	}
+
+	// Calculate decoded length: 5 bytes for every 8 input chars
+	input_chars := data_len - padding_count
+	out_len := input_chars * 5 / 8
+	out = make([]byte, out_len, allocator)
+	defer if err != .None {
+		delete(out)
+	}
+
+	// Process input in 8-byte blocks
+	outi := 0
+	for i := 0; i < input_chars; i += 8 {
+		buf: [8]byte
+		block_size := min(8, input_chars - i)
+
+		// Decode block
+		for j := 0; j < block_size; j += 1 {
+			buf[j] = DEC_TBL[data[i + j]]
+		}
+
+		// Convert to output bytes based on block size
+		bytes_to_write := block_size * 5 / 8
+		switch block_size {
+		case 8:
+			out[outi + 4] = (buf[6] << 5) | buf[7]
+			fallthrough
+		case 7:
+			out[outi + 3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3)
+			fallthrough
+		case 5:
+			out[outi + 2] = (buf[3] << 4) | (buf[4] >> 1)
+			fallthrough
+		case 4:
+			out[outi + 1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4)
+			fallthrough
+		case 2:
+			out[outi] = (buf[0] << 3) | (buf[1] >> 2)
+		}
+		outi += bytes_to_write
+	}
+
+	return
+}

+ 227 - 0
core/encoding/base32/base32_test.odin

@@ -0,0 +1,227 @@
+package encoding_base32
+
+import "core:testing"
+import "core:bytes"
+
+@(test)
+test_base32_decode_valid :: proc(t: ^testing.T) {
+	// RFC 4648 Section 10 - Test vectors
+	cases := [?]struct {
+		input, expected: string,
+	}{
+		{"", ""},
+		{"MY======", "f"},
+		{"MZXQ====", "fo"},
+		{"MZXW6===", "foo"},
+		{"MZXW6YQ=", "foob"},
+		{"MZXW6YTB", "fooba"},
+		{"MZXW6YTBOI======", "foobar"},
+	}
+
+	for c in cases {
+		output, err := decode(c.input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.None)
+		expected := transmute([]u8)c.expected
+		if output != nil {
+			testing.expect(t, bytes.equal(output, expected))
+		} else {
+			testing.expect(t, len(c.expected) == 0)
+		}
+	}
+}
+
+@(test)
+test_base32_encode :: proc(t: ^testing.T) {
+	// RFC 4648 Section 10 - Test vectors
+	cases := [?]struct {
+		input, expected: string,
+	}{
+		{"", ""},
+		{"f", "MY======"},
+		{"fo", "MZXQ===="},
+		{"foo", "MZXW6==="},
+		{"foob", "MZXW6YQ="},
+		{"fooba", "MZXW6YTB"},
+		{"foobar", "MZXW6YTBOI======"},
+	}
+
+	for c in cases {
+		output := encode(transmute([]byte)c.input)
+		defer delete(output)
+		testing.expect(t, output == c.expected)
+	}
+}
+
+@(test)
+test_base32_decode_invalid :: proc(t: ^testing.T) {
+	// Section 3.3 - Non-alphabet characters
+	{
+		// Characters outside alphabet
+		input := "MZ1W6YTB" // '1' not in alphabet (A-Z, 2-7)
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Invalid_Character)
+	}
+	{
+		// Lowercase not allowed
+		input := "mzxq===="
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Invalid_Character)
+	}
+
+	// Section 3.2 - Padding requirements
+	{
+		// Padding must only be at end
+		input := "MZ=Q===="
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Malformed_Input)
+	}
+	{
+		// Missing padding
+		input := "MZXQ" // Should be MZXQ====
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Malformed_Input)
+	}
+	{
+		// Incorrect padding length
+		input := "MZXQ=" // Needs 4 padding chars
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Malformed_Input)
+	}
+	{
+		// Too much padding
+		input := "MY=========" // Extra padding chars
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Malformed_Input)
+	}
+
+	// Section 6 - Base32 block size requirements
+	{
+		// Single character (invalid block)
+		input := "M"
+		output, err := decode(input)
+		if output != nil {
+			defer delete(output)
+		}
+		testing.expect_value(t, err, Error.Invalid_Length)
+	}
+}
+
+@(test)
+test_base32_roundtrip :: proc(t: ^testing.T) {
+	cases := [?]string{
+		"",
+		"f",
+		"fo",
+		"foo",
+		"foob",
+		"fooba",
+		"foobar",
+	}
+
+	for input in cases {
+		encoded := encode(transmute([]byte)input)
+		defer delete(encoded)
+		decoded, err := decode(encoded)
+		if decoded != nil {
+			defer delete(decoded)
+		}
+		testing.expect_value(t, err, Error.None)
+		testing.expect(t, bytes.equal(decoded, transmute([]byte)input))
+	}
+}
+
+@(test)
+test_base32_custom_alphabet :: proc(t: ^testing.T) {
+	custom_enc_table := [32]byte{
+		'0', '1', '2', '3', '4', '5', '6', '7',
+		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+	}
+
+	custom_dec_table: [256]u8
+	for i := 0; i < len(custom_enc_table); i += 1 {
+		custom_dec_table[custom_enc_table[i]] = u8(i)
+	}
+
+	/*
+	custom_dec_table := [256]u8{
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x00-0x0f
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x10-0x1f
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x20-0x2f
+		0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0,  0,  0,  0,  0, // 0x30-0x3f ('0'-'9')
+		0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f ('A'-'O')
+	 25, 26, 27, 28, 29, 30, 31,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x50-0x5f ('P'-'V')
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x60-0x6f
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x70-0x7f
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x80-0x8f
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0x90-0x9f
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0xa0-0xaf
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0xb0-0xbf
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0xc0-0xcf
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0xd0-0xdf
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0xe0-0xef
+		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0xf0-0xff
+	}
+	*/
+
+	custom_validate :: proc(c: byte) -> bool {
+		return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'V') || c == byte(PADDING)
+	}
+
+	cases := [?]struct {
+		input: string,
+		enc_expected: string,
+	}{
+		{"f", "CO======"},
+		{"fo", "CPNG===="},
+		{"foo", "CPNMU==="},
+	}
+
+	for c in cases {
+		// Test encoding
+		encoded := encode(transmute([]byte)c.input, custom_enc_table)
+		defer delete(encoded)
+		testing.expect(t, encoded == c.enc_expected)
+
+		// Test decoding
+		decoded, err := decode(encoded, custom_dec_table, custom_validate)
+		defer if decoded != nil {
+			delete(decoded)
+		}
+
+		testing.expect_value(t, err, Error.None)
+		testing.expect(t, bytes.equal(decoded, transmute([]byte)c.input))
+	}
+
+	// Test invalid character detection
+	{
+		input := "WXY=====" // Contains chars not in our alphabet
+		output, err := decode(input, custom_dec_table, custom_validate)
+		if output != nil {
+			delete(output)
+		}
+		testing.expect_value(t, err, Error.Invalid_Character)
+	}
+}

+ 2 - 2
core/encoding/json/unmarshal.odin

@@ -439,7 +439,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 			use_field_idx := -1
 			
 			for field, field_idx in fields {
-				tag_value := string(reflect.struct_tag_get(field.tag, "json"))
+				tag_value := reflect.struct_tag_get(field.tag, "json")
 				json_name, _ := json_name_from_tag_value(tag_value)
 				if key == json_name {
 					use_field_idx = field_idx
@@ -470,7 +470,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 						}
 					}
 
-					if field.name == key {
+					if field.name == key || (field.tag != "" && reflect.struct_tag_get(field.tag, "json") == key) {
 						offset = field.offset
 						type = field.type
 						found = true

+ 1 - 1
core/image/general.odin

@@ -146,7 +146,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
 	case s[6:10] == "JFIF", s[6:10] == "Exif":
 		return .JPEG
 	case s[:3] == "\xff\xd8\xff":
-		switch s[4] {
+		switch s[3] {
 		case 0xdb, 0xee, 0xe1, 0xe0:
 			return .JPEG
 		}

+ 1 - 129
core/image/png/helpers.odin

@@ -396,132 +396,4 @@ exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
 	General helper functions
 */
 
-compute_buffer_size :: image.compute_buffer_size
-
-/*
-	PNG save helpers
-*/
-
-when false {
-
-	make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
-
-		data: []u8
-		if v, ok := c.([]u8); ok {
-			data = v
-		} else {
-			data = mem.any_to_bytes(c)
-		}
-
-		res.header.length = u32be(len(data))
-		res.header.type   = t
-		res.data   = data
-
-		// CRC the type
-		crc    := hash.crc32(mem.any_to_bytes(res.header.type))
-		// Extend the CRC with the data
-		res.crc = u32be(hash.crc32(data, crc))
-		return
-	}
-
-	write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
-		c := chunk
-		// Write length + type
-		os.write_ptr(fd, &c.header, 8)
-		// Write data
-		os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length))
-		// Write CRC32
-		os.write_ptr(fd, &c.crc, 4)
-	}
-
-	write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
-		profiler.timed_proc()
-		using image
-		using os
-		flags: int = O_WRONLY|O_CREATE|O_TRUNC
-
-		if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
-			return .Invalid_Image_Dimensions
-		}
-
-		mode: int = 0
-		when ODIN_OS == .Linux || ODIN_OS == .Darwin {
-			// NOTE(justasd): 644 (owner read, write; group read; others read)
-			mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
-		}
-
-		fd, fderr := open(filename, flags, mode)
-		if fderr != nil {
-			return .Cannot_Open_File
-		}
-		defer close(fd)
-
-		magic := Signature
-
-		write_ptr(fd, &magic, 8)
-
-		ihdr := IHDR{
-			width              = u32be(width),
-			height             = u32be(height),
-			bit_depth          = depth,
-			compression_method = 0,
-			filter_method      = 0,
-			interlace_method   = .None,
-		}
-
-		switch channels {
-		case 1: ihdr.color_type = Color_Type{}
-		case 2: ihdr.color_type = Color_Type{.Alpha}
-		case 3: ihdr.color_type = Color_Type{.Color}
-		case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
-		case:// Unhandled
-			return .Unknown_Color_Type
-		}
-		h := make_chunk(ihdr, .IHDR)
-		write_chunk(fd, h)
-
-		bytes_needed := width * height * int(channels) + height
-		filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator)
-		defer delete(filter_bytes)
-
-		i := 0; j := 0
-		// Add a filter byte 0 per pixel row
-		for y := 0; y < height; y += 1 {
-			filter_bytes[j] = 0; j += 1
-			for x := 0; x < width; x += 1 {
-				for z := 0; z < channels; z += 1 {
-					filter_bytes[j+z] = image.pixels[i+z]
-				}
-				i += channels; j += channels
-			}
-		}
-		assert(j == bytes_needed)
-
-		a: []u8 = filter_bytes[:]
-
-		out_buf: ^[dynamic]u8
-		defer free(out_buf)
-
-		ctx := zlib.ZLIB_Context{
-			in_buf  = &a,
-			out_buf = out_buf,
-		}
-		err = zlib.write_zlib_stream_from_memory(&ctx)
-
-		b: []u8
-		if err == nil {
-			b = ctx.out_buf[:]
-		} else {
-			return err
-		}
-
-		idat := make_chunk(b, .IDAT)
-
-		write_chunk(fd, idat)
-
-		iend := make_chunk([]u8{}, .IEND)
-		write_chunk(fd, iend)
-
-		return nil
-	}
-}
+compute_buffer_size :: image.compute_buffer_size

+ 7 - 3
core/io/util.odin

@@ -132,9 +132,13 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
 			buf: [2]byte
 			s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
 			switch len(s) {
-			case 0: write_string(w, "00", &n) or_return
-			case 1: write_byte(w, '0',    &n) or_return
-			case 2: write_string(w, s,    &n) or_return
+			case 0: 
+				write_string(w, "00", &n) or_return
+			case 1: 
+				write_byte(w, '0',    &n) or_return
+				fallthrough
+			case 2: 
+				write_string(w, s,    &n) or_return
 			}
 		} else {
 			write_rune(w, r, &n) or_return

+ 66 - 18
core/math/linalg/general.odin

@@ -417,6 +417,13 @@ adjugate :: proc{
 	matrix4x4_adjugate,
 }
 
+cofactor :: proc{
+	matrix1x1_cofactor,
+	matrix2x2_cofactor,
+	matrix3x3_cofactor,
+	matrix4x4_cofactor,
+}
+
 inverse_transpose :: proc{
 	matrix1x1_inverse_transpose,
 	matrix2x2_inverse_transpose,
@@ -479,9 +486,9 @@ matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) #
 }
 @(require_results)
 matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) #no_bounds_check {
-	a := adjugate(m)
+	c := cofactor(m)
 	for i in 0..<4 {
-		det += m[0, i] * a[0, i]
+		det += m[0, i] * c[0, i]
 	}
 	return
 }
@@ -497,6 +504,47 @@ matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bo
 
 @(require_results)
 matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
+	y[0, 0] = +x[1, 1]
+	y[0, 1] = -x[0, 1]
+	y[1, 0] = -x[1, 0]
+	y[1, 1] = +x[0, 0]
+	return
+}
+
+@(require_results)
+matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
+	y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+	y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+	y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+	y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+	y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+	y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+	y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+	y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+	y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+	return
+}
+
+@(require_results)
+matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
+	for i in 0..<4 {
+		for j in 0..<4 {
+			sign: T = 1 if (i + j) % 2 == 0 else -1
+			y[i, j] = sign * matrix_minor(x, j, i)
+		}
+	}
+	return
+}
+
+
+@(require_results)
+matrix1x1_cofactor :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
+	y = x
+	return
+}
+
+@(require_results)
+matrix2x2_cofactor :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
 	y[0, 0] = +x[1, 1]
 	y[0, 1] = -x[1, 0]
 	y[1, 0] = -x[0, 1]
@@ -505,7 +553,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bo
 }
 
 @(require_results)
-matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
+matrix3x3_cofactor :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
 	y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
 	y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
 	y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -520,7 +568,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bo
 
 
 @(require_results)
-matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
+matrix4x4_cofactor :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
 	for i in 0..<4 {
 		for j in 0..<4 {
 			sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -556,19 +604,19 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
 
 @(require_results)
 matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d := determinant(x)
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[i, j] / d
+				y[i, j] = c[i, j] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[i, j] * id
+				y[i, j] = c[i, j] * id
 			}
 		}
 	}
@@ -577,22 +625,22 @@ matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
 
 @(require_results)
 matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d: T
 	for i in 0..<4 {
-		d += x[0, i] * a[0, i]
+		d += x[0, i] * c[0, i]
 	}
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[i, j] / d
+				y[i, j] = c[i, j] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[i, j] * id
+				y[i, j] = c[i, j] * id
 			}
 		}
 	}
@@ -625,19 +673,19 @@ matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bou
 
 @(require_results)
 matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d := determinant(x)
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[j, i] / d
+				y[i, j] = c[j, i] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[j, i] * id
+				y[i, j] = c[j, i] * id
 			}
 		}
 	}
@@ -646,22 +694,22 @@ matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
 
 @(require_results)
 matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d: T
 	for i in 0..<4 {
-		d += x[0, i] * a[0, i]
+		d += x[0, i] * c[0, i]
 	}
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[j, i] / d
+				y[i, j] = c[j, i] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[j, i] * id
+				y[i, j] = c[j, i] * id
 			}
 		}
 	}

+ 66 - 18
core/math/linalg/glsl/linalg_glsl.odin

@@ -1882,6 +1882,13 @@ adjugate :: proc{
 	adjugate_matrix4x4,
 }
 
+cofactor :: proc{
+	cofactor_matrix1x1,
+	cofactor_matrix2x2,
+	cofactor_matrix3x3,
+	cofactor_matrix4x4,
+}
+
 inverse_transpose :: proc{
 	inverse_transpose_matrix1x1,
 	inverse_transpose_matrix2x2,
@@ -1944,9 +1951,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
 }
 @(require_results)
 determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
-	a := adjugate(m)
+	c := cofactor(m)
 	#no_bounds_check for i in 0..<4 {
-		det += m[0, i] * a[0, i]
+		det += m[0, i] * c[0, i]
 	}
 	return
 }
@@ -1962,6 +1969,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
 
 @(require_results)
 adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
+	y[0, 0] = +x[1, 1]
+	y[0, 1] = -x[0, 1]
+	y[1, 0] = -x[1, 0]
+	y[1, 1] = +x[0, 0]
+	return
+}
+
+@(require_results)
+adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+	y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+	y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+	y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+	y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+	y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+	y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+	y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+	y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+	y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+	return
+}
+
+@(require_results)
+adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+	for i in 0..<4 {
+		for j in 0..<4 {
+			sign: T = 1 if (i + j) % 2 == 0 else -1
+			y[i, j] = sign * matrix_minor(x, j, i)
+		}
+	}
+	return
+}
+
+
+@(require_results)
+cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
+	y = x
+	return
+}
+
+@(require_results)
+cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
 	y[0, 0] = +x[1, 1]
 	y[0, 1] = -x[1, 0]
 	y[1, 0] = -x[0, 1]
@@ -1970,7 +2018,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
 }
 
 @(require_results)
-adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
 	y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
 	y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
 	y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1985,7 +2033,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
 
 
 @(require_results)
-adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
 	for i in 0..<4 {
 		for j in 0..<4 {
 			sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -2021,19 +2069,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
 
 @(require_results)
 inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d := determinant(x)
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[i, j] / d
+				y[i, j] = c[i, j] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[i, j] * id
+				y[i, j] = c[i, j] * id
 			}
 		}
 	}
@@ -2042,22 +2090,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
 
 @(require_results)
 inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d: T
 	for i in 0..<4 {
-		d += x[0, i] * a[0, i]
+		d += x[0, i] * c[0, i]
 	}
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[i, j] / d
+				y[i, j] = c[i, j] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[i, j] * id
+				y[i, j] = c[i, j] * id
 			}
 		}
 	}
@@ -2090,19 +2138,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
 
 @(require_results)
 inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d := determinant(x)
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[j, i] / d
+				y[i, j] = c[j, i] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[j, i] * id
+				y[i, j] = c[j, i] * id
 			}
 		}
 	}
@@ -2111,22 +2159,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
 
 @(require_results)
 inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d: T
 	for i in 0..<4 {
-		d += x[0, i] * a[0, i]
+		d += x[0, i] * c[0, i]
 	}
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[j, i] / d
+				y[i, j] = c[j, i] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[j, i] * id
+				y[i, j] = c[j, i] * id
 			}
 		}
 	}

+ 66 - 18
core/math/linalg/hlsl/linalg_hlsl.odin

@@ -1514,6 +1514,13 @@ adjugate :: proc{
 	adjugate_matrix4x4,
 }
 
+cofactor :: proc{
+	cofactor_matrix1x1,
+	cofactor_matrix2x2,
+	cofactor_matrix3x3,
+	cofactor_matrix4x4,
+}
+
 inverse_transpose :: proc{
 	inverse_transpose_matrix1x1,
 	inverse_transpose_matrix2x2,
@@ -1568,9 +1575,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
 }
 @(require_results)
 determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
-	a := adjugate(m)
+	c := cofactor(m)
 	#no_bounds_check for i in 0..<4 {
-		det += m[0, i] * a[0, i]
+		det += m[0, i] * c[0, i]
 	}
 	return
 }
@@ -1586,6 +1593,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
 
 @(require_results)
 adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
+	y[0, 0] = +x[1, 1]
+	y[0, 1] = -x[0, 1]
+	y[1, 0] = -x[1, 0]
+	y[1, 1] = +x[0, 0]
+	return
+}
+
+@(require_results)
+adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+	y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+	y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+	y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+	y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+	y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+	y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+	y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+	y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+	y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+	return
+}
+
+@(require_results)
+adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+	for i in 0..<4 {
+		for j in 0..<4 {
+			sign: T = 1 if (i + j) % 2 == 0 else -1
+			y[i, j] = sign * matrix_minor(x, j, i)
+		}
+	}
+	return
+}
+
+
+@(require_results)
+cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
+	y = x
+	return
+}
+
+@(require_results)
+cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
 	y[0, 0] = +x[1, 1]
 	y[0, 1] = -x[1, 0]
 	y[1, 0] = -x[0, 1]
@@ -1594,7 +1642,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
 }
 
 @(require_results)
-adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
 	y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
 	y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
 	y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1609,7 +1657,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
 
 
 @(require_results)
-adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
 	for i in 0..<4 {
 		for j in 0..<4 {
 			sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -1645,19 +1693,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
 
 @(require_results)
 inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d := determinant(x)
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[i, j] / d
+				y[i, j] = c[i, j] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[i, j] * id
+				y[i, j] = c[i, j] * id
 			}
 		}
 	}
@@ -1666,22 +1714,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
 
 @(require_results)
 inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d: T
 	for i in 0..<4 {
-		d += x[0, i] * a[0, i]
+		d += x[0, i] * c[0, i]
 	}
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[i, j] / d
+				y[i, j] = c[i, j] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[i, j] * id
+				y[i, j] = c[i, j] * id
 			}
 		}
 	}
@@ -1714,19 +1762,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
 
 @(require_results)
 inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d := determinant(x)
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[j, i] / d
+				y[i, j] = c[j, i] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<3 {
 			for j in 0..<3 {
-				y[i, j] = a[j, i] * id
+				y[i, j] = c[j, i] * id
 			}
 		}
 	}
@@ -1735,22 +1783,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
 
 @(require_results)
 inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
-	a := adjugate(x)
+	c := cofactor(x)
 	d: T
 	for i in 0..<4 {
-		d += x[0, i] * a[0, i]
+		d += x[0, i] * c[0, i]
 	}
 	when intrinsics.type_is_integer(T) {
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[j, i] / d
+				y[i, j] = c[j, i] / d
 			}
 		}
 	} else {
 		id := 1/d
 		for i in 0..<4 {
 			for j in 0..<4 {
-				y[i, j] = a[j, i] * id
+				y[i, j] = c[j, i] * id
 			}
 		}
 	}

+ 0 - 24
core/math/rand/rand.odin

@@ -29,30 +29,6 @@ Reset the seed used by the context.random_generator.
 Inputs:
 - seed: The seed value
 
-Example:
-	import "core:math/rand"
-	import "core:fmt"
-
-	set_global_seed_example :: proc() {
-		rand.set_global_seed(1)
-		fmt.println(rand.uint64())
-	}
-
-Possible Output:
-
-	10
-*/
-@(deprecated="Prefer `rand.reset`")
-set_global_seed :: proc(seed: u64) {
-	runtime.random_generator_reset_u64(context.random_generator, seed)
-}
-
-/*
-Reset the seed used by the context.random_generator.
-
-Inputs:
-- seed: The seed value
-
 Example:
 	import "core:math/rand"
 	import "core:fmt"

+ 0 - 23
core/mem/allocators.odin

@@ -140,14 +140,6 @@ arena_init :: proc(a: ^Arena, data: []byte) {
 	a.temp_count = 0
 }
 
-@(deprecated="prefer 'mem.arena_init'")
-init_arena :: proc(a: ^Arena, data: []byte) {
-	a.data       = data
-	a.offset     = 0
-	a.peak_used  = 0
-	a.temp_count = 0
-}
-
 /*
 Allocate memory from an arena.
 
@@ -786,14 +778,6 @@ stack_init :: proc(s: ^Stack, data: []byte) {
 	s.peak_used   = 0
 }
 
-@(deprecated="prefer 'mem.stack_init'")
-init_stack :: proc(s: ^Stack, data: []byte) {
-	s.data        = data
-	s.prev_offset = 0
-	s.curr_offset = 0
-	s.peak_used   = 0
-}
-
 /*
 Allocate memory from stack.
 
@@ -1162,13 +1146,6 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
 	s.peak_used = 0
 }
 
-@(deprecated="prefer 'small_stack_init'")
-init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
-	s.data      = data
-	s.offset    = 0
-	s.peak_used = 0
-}
-
 /*
 Small stack allocator.
 

+ 1 - 8
core/mem/mem.odin

@@ -685,11 +685,4 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he
 		}
 	}
 	return int(padding)
-}
-
-@(require_results, deprecated="prefer 'slice.clone'")
-clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
-	new_slice, _ = make(T, len(slice), allocator, loc)
-	runtime.copy(new_slice, slice)
-	return new_slice
-}
+}

+ 2 - 1
core/mem/virtual/arena.odin

@@ -204,8 +204,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
 		}
 		// Zero the first block's memory
 		if arena.curr_block != nil {
-			mem.zero(arena.curr_block.base, int(arena.curr_block.used))
+			curr_block_used := int(arena.curr_block.used)
 			arena.curr_block.used = 0
+			mem.zero(arena.curr_block.base, curr_block_used)
 		}
 		arena.total_used = 0
 	case .Static, .Buffer:

+ 1 - 1
core/os/os2/file.odin

@@ -115,7 +115,7 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
 
 @(require_results)
 new_file :: proc(handle: uintptr, name: string) -> ^File {
-	file, err := _new_file(handle, name)
+	file, err := _new_file(handle, name, file_allocator())
 	if err != nil {
 		panic(error_string(err))
 	}

+ 27 - 54
core/os/os2/file_linux.odin

@@ -39,37 +39,23 @@ _stderr := File{
 
 @init
 _standard_stream_init :: proc() {
-	@static stdin_impl := File_Impl {
-		name = "/proc/self/fd/0",
-		fd = 0,
-	}
-
-	@static stdout_impl := File_Impl {
-		name = "/proc/self/fd/1",
-		fd = 1,
-	}
-
-	@static stderr_impl := File_Impl {
-		name = "/proc/self/fd/2",
-		fd = 2,
+	new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
+		impl.file.impl = impl
+		impl.fd = linux.Fd(fd)
+		impl.allocator = runtime.nil_allocator()
+		impl.name = name
+		impl.file.stream = {
+			data = impl,
+			procedure = _file_stream_proc,
+		}
+		impl.file.fstat = _fstat
+		return &impl.file
 	}
 
-	stdin_impl.allocator  = file_allocator()
-	stdout_impl.allocator = file_allocator()
-	stderr_impl.allocator = file_allocator()
-
-	_stdin.impl  = &stdin_impl
-	_stdout.impl = &stdout_impl
-	_stderr.impl = &stderr_impl
-
-	// cannot define these initially because cyclic reference
-	_stdin.stream.data  = &stdin_impl
-	_stdout.stream.data = &stdout_impl
-	_stderr.stream.data = &stderr_impl
-
-	stdin  = &_stdin
-	stdout = &_stdout
-	stderr = &_stderr
+	@(static) files: [3]File_Impl
+	stdin  = new_std(&files[0], 0, "/proc/self/fd/0")
+	stdout = new_std(&files[1], 1, "/proc/self/fd/1")
+	stderr = new_std(&files[2], 2, "/proc/self/fd/2")
 }
 
 _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -80,6 +66,9 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
 	// terminal would be incredibly rare. This has no effect on files while
 	// allowing us to open serial devices.
 	sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
+	when size_of(rawptr) == 4 {
+		sys_flags += {.LARGEFILE}
+	}
 	switch flags & (O_RDONLY|O_WRONLY|O_RDWR) {
 	case O_RDONLY:
 	case O_WRONLY: sys_flags += {.WRONLY}
@@ -97,18 +86,18 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
 		return nil, _get_platform_error(errno)
 	}
 
-	return _new_file(uintptr(fd), name)
+	return _new_file(uintptr(fd), name, file_allocator())
 }
 
-_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
-	impl := new(File_Impl, file_allocator()) or_return
+_new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
+	impl := new(File_Impl, allocator) or_return
 	defer if err != nil {
-		free(impl, file_allocator())
+		free(impl, allocator)
 	}
 	impl.file.impl = impl
 	impl.fd = linux.Fd(fd)
-	impl.allocator = file_allocator()
-	impl.name = _get_full_path(impl.fd, file_allocator()) or_return
+	impl.allocator = allocator
+	impl.name = _get_full_path(impl.fd, impl.allocator) or_return
 	impl.file.stream = {
 		data = impl,
 		procedure = _file_stream_proc,
@@ -272,28 +261,12 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
 }
 
 _remove :: proc(name: string) -> Error {
-	is_dir_fd :: proc(fd: linux.Fd) -> bool {
-		s: linux.Stat
-		if linux.fstat(fd, &s) != .NONE {
-			return false
-		}
-		return linux.S_ISDIR(s.mode)
-	}
-
 	TEMP_ALLOCATOR_GUARD()
 	name_cstr := temp_cstring(name) or_return
 
-	fd, errno := linux.open(name_cstr, {.NOFOLLOW})
-	#partial switch (errno) {
-	case .ELOOP:
-		/* symlink */
-	case .NONE:
-		defer linux.close(fd)
-		if is_dir_fd(fd) {
-			return _get_platform_error(linux.rmdir(name_cstr))
-		}
-	case:
-		return _get_platform_error(errno)
+	if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS + {.NOFOLLOW}); errno == .NONE {
+		linux.close(fd)
+		return _get_platform_error(linux.rmdir(name_cstr))
 	}
 
 	return _get_platform_error(linux.unlink(name_cstr))

+ 29 - 20
core/os/os2/file_posix.odin

@@ -21,23 +21,29 @@ File_Impl :: struct {
 	name:  string,
 	cname: cstring,
 	fd:    posix.FD,
+	allocator: runtime.Allocator,
 }
 
 @(init)
 init_std_files :: proc() {
-	// NOTE: is this (paths) also the case on non darwin?
-
-	stdin = __new_file(posix.STDIN_FILENO)
-	(^File_Impl)(stdin.impl).name  = "/dev/stdin"
-	(^File_Impl)(stdin.impl).cname = "/dev/stdin"
-
-	stdout = __new_file(posix.STDIN_FILENO)
-	(^File_Impl)(stdout.impl).name  = "/dev/stdout"
-	(^File_Impl)(stdout.impl).cname = "/dev/stdout"
+	new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
+		impl.file.impl = impl
+		impl.fd = fd
+		impl.allocator = runtime.nil_allocator()
+		impl.cname = name
+		impl.name  = string(name)
+		impl.file.stream = {
+			data = impl,
+			procedure = _file_stream_proc,
+		}
+		impl.file.fstat = _fstat
+		return &impl.file
+	}
 
-	stderr = __new_file(posix.STDIN_FILENO)
-	(^File_Impl)(stderr.impl).name  = "/dev/stderr"
-	(^File_Impl)(stderr.impl).cname = "/dev/stderr"
+	@(static) files: [3]File_Impl
+	stdin  = new_std(&files[0], posix.STDIN_FILENO,  "/dev/stdin")
+	stdout = new_std(&files[1], posix.STDOUT_FILENO, "/dev/stdout")
+	stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr")
 }
 
 _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -72,10 +78,10 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
 		return
 	}
 
-	return _new_file(uintptr(fd), name)
+	return _new_file(uintptr(fd), name, file_allocator())
 }
 
-_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
 	if name == "" {
 		err = .Invalid_Path
 		return
@@ -84,10 +90,10 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
 		return
 	}
 
-	crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
+	crname := _posix_absolute_path(posix.FD(handle), name, allocator) or_return
 	rname  := string(crname)
 
-	f = __new_file(posix.FD(handle))
+	f = __new_file(posix.FD(handle), allocator)
 	impl := (^File_Impl)(f.impl)
 	impl.name  = rname
 	impl.cname = crname
@@ -95,10 +101,11 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
 	return f, nil
 }
 
-__new_file :: proc(handle: posix.FD) -> ^File {
-	impl := new(File_Impl, file_allocator())
+__new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
+	impl := new(File_Impl, allocator)
 	impl.file.impl = impl
 	impl.fd = posix.FD(handle)
+	impl.allocator = allocator
 	impl.file.stream = {
 		data = impl,
 		procedure = _file_stream_proc,
@@ -114,8 +121,10 @@ _close :: proc(f: ^File_Impl) -> (err: Error) {
 		err = _get_platform_error()
 	}
 
-	delete(f.cname, file_allocator())
-	free(f, file_allocator())
+	allocator := f.allocator
+
+	delete(f.cname, allocator)
+	free(f, allocator)
 	return
 }
 

+ 37 - 16
core/os/os2/file_windows.odin

@@ -44,17 +44,38 @@ File_Impl :: struct {
 
 @(init)
 init_std_files :: proc() {
-	stdin  = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)),  "<stdin>")
-	stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "<stdout>")
-	stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)),  "<stderr>")
-}
-@(fini)
-fini_std_files :: proc() {
-	_destroy((^File_Impl)(stdin.impl))
-	_destroy((^File_Impl)(stdout.impl))
-	_destroy((^File_Impl)(stderr.impl))
-}
+	new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
+		impl.file.impl = impl
+
+		impl.allocator = runtime.nil_allocator()
+		impl.fd = win32.GetStdHandle(code)
+		impl.name = name
+		impl.wname = nil
+
+		handle := _handle(&impl.file)
+		kind := File_Impl_Kind.File
+		if m: u32; win32.GetConsoleMode(handle, &m) {
+			kind = .Console
+		}
+		if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
+			kind = .Pipe
+		}
+		impl.kind = kind
 
+		impl.file.stream = {
+			data = impl,
+			procedure = _file_stream_proc,
+		}
+		impl.file.fstat = _fstat
+
+		return &impl.file
+	}
+
+	@(static) files: [3]File_Impl
+	stdin  = new_std(&files[0], win32.STD_INPUT_HANDLE,  "<stdin>")
+	stdout = new_std(&files[1], win32.STD_OUTPUT_HANDLE, "<stdout>")
+	stderr = new_std(&files[2], win32.STD_ERROR_HANDLE,  "<stderr>")
+}
 
 _handle :: proc(f: ^File) -> win32.HANDLE {
 	return win32.HANDLE(_fd(f))
@@ -132,21 +153,21 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
 _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
 	flags := flags if flags != nil else {.Read}
 	handle := _open_internal(name, flags, perm) or_return
-	return _new_file(handle, name)
+	return _new_file(handle, name, file_allocator())
 }
 
-_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
 	if handle == INVALID_HANDLE {
 		return
 	}
-	impl := new(File_Impl, file_allocator()) or_return
+	impl := new(File_Impl, allocator) or_return
 	defer if err != nil {
-		free(impl, file_allocator())
+		free(impl, allocator)
 	}
 
 	impl.file.impl = impl
 
-	impl.allocator = file_allocator()
+	impl.allocator = allocator
 	impl.fd = rawptr(handle)
 	impl.name = clone_string(name, impl.allocator) or_return
 	impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
@@ -180,7 +201,7 @@ _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Rea
 }
 
 _new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
-	f, err = _new_file(handle, name)
+	f, err = _new_file(handle, name, file_allocator())
 	if f != nil && err == nil {
 		impl := (^File_Impl)(f.impl)
 		impl.r_buf = make([]byte, buffer_size, file_allocator())

+ 1 - 1
core/os/os2/heap_linux.odin

@@ -415,7 +415,7 @@ _region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_l
 	back_idx := -1
 	idx: u16
 	infinite: for {
-		for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
+		for i := 0; i < int(region_iter.hdr.free_list_len); i += 1 {
 			idx = region_iter.hdr.free_list[i]
 			if _get_block_count(region_iter.memory[idx]) >= new_block_count {
 				break infinite

+ 0 - 2
core/os/os2/path_linux.odin

@@ -77,8 +77,6 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
 }
 
 _remove_all :: proc(path: string) -> Error {
-	DT_DIR :: 4
-
 	remove_all_dir :: proc(dfd: linux.Fd) -> Error {
 		n := 64
 		buf := make([]u8, n)

+ 2 - 2
core/os/os2/pipe_linux.odin

@@ -10,8 +10,8 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 		return nil, nil,_get_platform_error(errno)
 	}
 
-	r = _new_file(uintptr(fds[0])) or_return
-	w = _new_file(uintptr(fds[1])) or_return
+	r = _new_file(uintptr(fds[0]), "", file_allocator()) or_return
+	w = _new_file(uintptr(fds[1]), "", file_allocator()) or_return
 
 	return
 }

+ 2 - 2
core/os/os2/pipe_posix.odin

@@ -21,7 +21,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 		return
 	}
 
-	r = __new_file(fds[0])
+	r = __new_file(fds[0], file_allocator())
 	ri := (^File_Impl)(r.impl)
 
 	rname := strings.builder_make(file_allocator())
@@ -31,7 +31,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 	ri.name  = strings.to_string(rname)
 	ri.cname = strings.to_cstring(&rname)
 
-	w = __new_file(fds[1])
+	w = __new_file(fds[1], file_allocator())
 	wi := (^File_Impl)(w.impl)
 	
 	wname := strings.builder_make(file_allocator())

+ 10 - 1
core/os/os2/process.odin

@@ -290,12 +290,21 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
 	return _process_open(pid, flags)
 }
 
+
+/*
+OS-specific process attributes.
+*/
+Process_Attributes :: struct {
+	sys_attr: _Sys_Process_Attributes,
+}
+
 /*
 	The description of how a process should be created.
 */
 Process_Desc :: struct {
 	// OS-specific attributes.
-	sys_attr: _Sys_Process_Attributes,
+	sys_attr: Process_Attributes,
+
 	// The working directory of the process. If the string has length 0, the
 	// working directory is assumed to be the current working directory of the
 	// current process.

+ 11 - 36
core/os/os2/process_linux.odin

@@ -384,14 +384,6 @@ _Sys_Process_Attributes :: struct {}
 
 @(private="package")
 _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
-	has_executable_permissions :: proc(fd: linux.Fd) -> bool {
-		backing: [48]u8
-		b := strings.builder_from_bytes(backing[:])
-		strings.write_string(&b, "/proc/self/fd/")
-		strings.write_int(&b, int(fd))
-		return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
-	}
-
 	TEMP_ALLOCATOR_GUARD()
 
 	if len(desc.command) == 0 {
@@ -411,7 +403,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 	}
 
 	// search PATH if just a plain name is provided
-	exe_fd: linux.Fd
+	exe_path: cstring
 	executable_name := desc.command[0]
 	if strings.index_byte(executable_name, '/') < 0 {
 		path_env := get_env("PATH", temp_allocator())
@@ -426,16 +418,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 			strings.write_byte(&exe_builder, '/')
 			strings.write_string(&exe_builder, executable_name)
 
-			exe_path := strings.to_cstring(&exe_builder)
-			if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
-				continue
-			}
-			if !has_executable_permissions(exe_fd) {
-				linux.close(exe_fd)
-				continue
+			exe_path = strings.to_cstring(&exe_builder)
+			if linux.access(exe_path, linux.X_OK) == .NONE {
+				found = true
+				break
 			}
-			found = true
-			break
 		}
 		if !found {
 			// check in cwd to match windows behavior
@@ -443,29 +430,18 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 			strings.write_string(&exe_builder, "./")
 			strings.write_string(&exe_builder, executable_name)
 
-			exe_path := strings.to_cstring(&exe_builder)
-			if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
+			exe_path = strings.to_cstring(&exe_builder)
+			if linux.access(exe_path, linux.X_OK) != .NONE {
 				return process, .Not_Exist
 			}
-			if !has_executable_permissions(exe_fd) {
-				linux.close(exe_fd)
-				return process, .Permission_Denied
-			}
 		}
 	} else {
-		exe_path := temp_cstring(executable_name) or_return
-		if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
-			return process, _get_platform_error(errno)
-		}
-		if !has_executable_permissions(exe_fd) {
-			linux.close(exe_fd)
-			return process, .Permission_Denied
+		exe_path = temp_cstring(executable_name) or_return
+		if linux.access(exe_path, linux.X_OK) != .NONE {
+			return process, .Not_Exist
 		}
 	}
 
-	// At this point, we have an executable.
-	defer linux.close(exe_fd)
-
 	// args and environment need to be a list of cstrings
 	// that are terminated by a nil pointer.
 	cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
@@ -492,7 +468,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 	}
 	defer linux.close(child_pipe_fds[READ])
 
-
 	// TODO: This is the traditional textbook implementation with fork.
 	//       A more efficient implementation with vfork:
 	//
@@ -573,7 +548,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 			write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
 		}
 
-		errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
+		errno = linux.execveat(dir_fd, exe_path, &cargs[0], env)
 		assert(errno != nil)
 		write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
 	}

+ 2 - 0
core/prof/spall/doc.odin

@@ -18,6 +18,8 @@ Example:
 		defer spall.context_destroy(&spall_ctx)
 
 		buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
+		defer delete(buffer_backing)
+
 		spall_buffer = spall.buffer_create(buffer_backing, u32(sync.current_thread_id()))
 		defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
 

+ 59 - 37
core/sys/linux/bits.odin

@@ -152,43 +152,65 @@ Errno :: enum i32 {
 	RDONLY flag is not present, because it has the value of 0, i.e. it is the
 	default, unless WRONLY or RDWR is specified.
 */
-Open_Flags_Bits :: enum {
-	WRONLY    = 0,
-	RDWR      = 1,
-	CREAT     = 6,
-	EXCL      = 7,
-	NOCTTY    = 8,
-	TRUNC     = 9,
-	APPEND    = 10,
-	NONBLOCK  = 11,
-	DSYNC     = 12,
-	ASYNC     = 13,
-	DIRECT    = 14,
-	LARGEFILE = 15,
-	DIRECTORY = 16,
-	NOFOLLOW  = 17,
-	NOATIME   = 18,
-	CLOEXEC   = 19,
-	PATH      = 21,
-}
-// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
-#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
-#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
-#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
-#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
-#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
-#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
-#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
-#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
-#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
-#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
-#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
-#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
-#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
-#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
-#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
-#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
-#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
+when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
+	Open_Flags_Bits :: enum {
+		WRONLY    = 0,
+		RDWR      = 1,
+		CREAT     = 6,
+		EXCL      = 7,
+		NOCTTY    = 8,
+		TRUNC     = 9,
+		APPEND    = 10,
+		NONBLOCK  = 11,
+		DSYNC     = 12,
+		ASYNC     = 13,
+		DIRECT    = 14,
+		LARGEFILE = 15,
+		DIRECTORY = 16,
+		NOFOLLOW  = 17,
+		NOATIME   = 18,
+		CLOEXEC   = 19,
+		PATH      = 21,
+	}
+	// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+	#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
+	#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
+	#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
+	#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
+	#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
+	#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
+	#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
+	#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
+	#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
+	#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
+	#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
+	#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+	#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+	#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
+	#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
+	#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
+	#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
+} else {
+	Open_Flags_Bits :: enum {
+		WRONLY    = 0,
+		RDWR      = 1,
+		CREAT     = 6,
+		EXCL      = 7,
+		NOCTTY    = 8,
+		TRUNC     = 9,
+		APPEND    = 10,
+		NONBLOCK  = 11,
+		DSYNC     = 12,
+		ASYNC     = 13,
+		DIRECTORY = 14,
+		NOFOLLOW  = 15,
+		DIRECT    = 16,
+		LARGEFILE = 17,
+		NOATIME   = 18,
+		CLOEXEC   = 19,
+		PATH      = 21,
+	}
+}
 
 /*
 	Bits for FD_Flags bitset

+ 2 - 2
core/sys/linux/helpers.odin

@@ -138,8 +138,8 @@ errno_unwrap :: proc {errno_unwrap2, errno_unwrap3}
 when size_of(int) == 4 {
 	// xxx64 system calls take some parameters as pairs of ulongs rather than a single pointer
 	@(private)
-	compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (hi: uint, lo: uint) {
-		no_sign := uint(a)
+	compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (lo: uint, hi: uint) {
+		no_sign := u64(a)
 		hi = uint(no_sign >> 32)
 		lo = uint(no_sign & 0xffff_ffff)
 		return

+ 25 - 15
core/sys/linux/sys.odin

@@ -151,7 +151,8 @@ lseek :: proc "contextless" (fd: Fd, off: i64, whence: Seek_Whence) -> (i64, Err
 		return errno_unwrap(ret, i64)
 	} else {
 		result: i64 = ---
-		ret := syscall(SYS__llseek, fd, compat64_arg_pair(off), &result, whence)
+		lo, hi := compat64_arg_pair(off)
+		ret := syscall(SYS__llseek, fd, hi, lo, &result, whence)
 		return result, Errno(-ret)
 	}
 }
@@ -251,7 +252,11 @@ ioctl :: proc "contextless" (fd: Fd, request: u32, arg: uintptr) -> (uintptr) {
 	Available since Linux 2.2.
 */
 pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
-	ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+	when ODIN_ARCH == .arm32 {
+		ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
+	} else {
+		ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+	}
 	return errno_unwrap(ret, int)
 }
 
@@ -261,7 +266,11 @@ pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
 	Available since Linux 2.2.
 */
 pwrite :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
-	ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+	when ODIN_ARCH == .arm32 {
+		ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
+	} else {
+		ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+	}
 	return errno_unwrap(ret, int)
 }
 
@@ -1127,7 +1136,10 @@ fdatasync :: proc "contextless" (fd: Fd) -> (Errno) {
 	On 32-bit architectures available since Linux 2.4.
 */
 truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
-	when size_of(int) == 4 {
+	when ODIN_ARCH == .arm32 {
+		ret := syscall(SYS_truncate64, cast(rawptr) name, 0, compat64_arg_pair(length))
+		return Errno(-ret)
+	} else when size_of(int) == 4 {
 		ret := syscall(SYS_truncate64, cast(rawptr) name, compat64_arg_pair(length))
 		return Errno(-ret)
 	} else {
@@ -1141,7 +1153,10 @@ truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
 	On 32-bit architectures available since 2.4.
 */
 ftruncate :: proc "contextless" (fd: Fd, length: i64) -> (Errno) {
-	when size_of(int) == 4 {
+	when ODIN_ARCH == .arm32 {
+		ret := syscall(SYS_ftruncate64, fd, 0, compat64_arg_pair(length))
+		return Errno(-ret)
+	} else when size_of(int) == 4 {
 		ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
 		return Errno(-ret)
 	} else {
@@ -1952,10 +1967,10 @@ sigaltstack :: proc "contextless" (stack: ^Sig_Stack, old_stack: ^Sig_Stack) ->
 */
 mknod :: proc "contextless" (name: cstring, mode: Mode, dev: Dev) -> (Errno) {
 	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
-		ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, dev)
+		ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
 		return Errno(-ret)
 	} else {
-		ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, dev)
+		ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
 		return Errno(-ret)
 	}
 }
@@ -2586,7 +2601,7 @@ mkdirat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode) -> (Errno)
 	Available since Linux 2.6.16.
 */
 mknodat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode, dev: Dev) -> (Errno) {
-	ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, dev)
+	ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
 	return Errno(-ret)
 }
 
@@ -2684,13 +2699,8 @@ faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) ->
 	Available since Linux 2.6.16.
 */
 ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (i32, Errno) {
-	when size_of(int) == 8 {
-		ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
-		return errno_unwrap(ret, i32)
-	} else {
-		ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
-		return errno_unwrap(ret, i32)
-	}
+	ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
+	return errno_unwrap(ret, i32)
 }
 
 // TODO(flysand): unshare

+ 166 - 66
core/sys/linux/types.odin

@@ -3,7 +3,7 @@ package linux
 /*
 	Type for storage device handle.
 */
-Dev :: distinct int
+Dev :: distinct u64
 
 /*
 	Type for 32-bit User IDs.
@@ -153,6 +153,7 @@ when ODIN_ARCH == .amd64 {
 		uid:        Uid,
 		gid:        Gid,
 		rdev:       Dev,
+		_:          [4]u8,
 		size:       i64,
 		blksize:    uint,
 		blocks:     u64,
@@ -516,79 +517,79 @@ Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]
 Sig_Set :: [_SIGSET_NWORDS]uint
 
 @private SI_MAX_SIZE       :: 128
-@private SI_ARCH_PREAMBLE  :: 4 * size_of(i32)
+@private SI_ARCH_PREAMBLE  :: 4 * size_of(i32) when size_of(rawptr) == 8 else 3 * size_of(i32)
 @private SI_PAD_SIZE       :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
 
 Sig_Handler_Fn :: #type proc "c" (sig: Signal)
 Sig_Restore_Fn :: #type proc "c" () -> !
 
-Sig_Info :: struct #packed {
-	signo: Signal,
-	errno: Errno,
-	code: i32,
-	_pad0: i32,
-	using _union: struct #raw_union {
-		_pad1: [SI_PAD_SIZE]u8,
-		using _kill: struct {
-			pid: Pid, /* sender's pid */
-			uid: Uid, /* sender's uid */
-		},
-		using _timer: struct {
-			timerid: i32,   /* timer id */
-			overrun: i32,   /* overrun count */
-			value: Sig_Val, /* timer value */
-		},
-		/* POSIX.1b signals */
-		using _rt: struct {
-			_pid0: Pid, /* sender's pid */
-			_uid0: Uid, /* sender's uid */
-		},
-		/* SIGCHLD */
-		using _sigchld: struct {
-			_pid1: Pid,  /* which child */
-			_uid1: Uid,  /* sender's uid */
-			status: i32, /* exit code */
-			utime: uint,
-			stime: uint, //clock_t
-		},
-		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
-		using _sigfault: struct {
-			addr: rawptr, /* faulting insn/memory ref. */
-			using _: struct #raw_union {
-				trapno: i32,   /* Trap number that caused signal */
-				addr_lsb: i16, /* LSB of the reported address */
-				using _addr_bnd: struct {
-					_pad2: u64,
-					lower: rawptr, /* lower bound during fault */
-					upper: rawptr, /* upper bound during fault */
-				},
-				using _addr_pkey: struct {
-					_pad3: u64,
-					pkey: u32, /* protection key on PTE that faulted */
-				},
-				using _perf: struct {
-					perf_data: u64,
-					perf_type: u32,
-					perf_flags: u32,
+when size_of(rawptr) == 8 {
+	Sig_Info :: struct #packed {
+		signo: Signal,
+		errno: Errno,
+		code: i32,
+		_pad0: i32,
+		using _union: struct #raw_union {
+			_pad1: [SI_PAD_SIZE]u8,
+			using _kill: struct {
+				pid: Pid, /* sender's pid */
+				uid: Uid, /* sender's uid */
+			},
+			using _timer: struct {
+				timerid: i32,   /* timer id */
+				overrun: i32,   /* overrun count */
+				value: Sig_Val, /* timer value */
+			},
+			/* POSIX.1b signals */
+			using _rt: struct {
+				_pid0: Pid, /* sender's pid */
+				_uid0: Uid, /* sender's uid */
+			},
+			/* SIGCHLD */
+			using _sigchld: struct {
+				_pid1: Pid,  /* which child */
+				_uid1: Uid,  /* sender's uid */
+				status: i32, /* exit code */
+				utime: uint,
+				stime: uint, //clock_t
+			},
+			/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+			using _sigfault: struct {
+				addr: rawptr, /* faulting insn/memory ref. */
+				using _: struct #raw_union {
+					trapno: i32,   /* Trap number that caused signal */
+					addr_lsb: i16, /* LSB of the reported address */
+					using _addr_bnd: struct {
+						_pad2: u64,
+						lower: rawptr, /* lower bound during fault */
+						upper: rawptr, /* upper bound during fault */
+					},
+					using _addr_pkey: struct {
+						_pad3: u64,
+						pkey: u32, /* protection key on PTE that faulted */
+					},
+					using _perf: struct {
+						perf_data: u64,
+						perf_type: u32,
+						perf_flags: u32,
+					},
 				},
 			},
+			/* SIGPOLL */
+			using _sigpoll: struct {
+				band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
+				fd: Fd,
+			},
+			/* SIGSYS */
+			using _sigsys: struct {
+				call_addr: rawptr, /* calling user insn */
+				syscall: i32,      /* triggering system call number */
+				arch: u32,         /* AUDIT_ARCH_* of syscall */
+			},
 		},
-		/* SIGPOLL */
-		using _sigpoll: struct {
-			band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
-			fd: Fd,
-		},
-		/* SIGSYS */
-		using _sigsys: struct {
-			call_addr: rawptr, /* calling user insn */
-			syscall: i32,      /* triggering system call number */
-			arch: u32,         /* AUDIT_ARCH_* of syscall */
-		},
-	},
-}
+	}
 
-#assert(size_of(Sig_Info) == 128)
-when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	#assert(size_of(Sig_Info) == 128)
 	#assert(offset_of(Sig_Info, signo)      == 0x00)
 	#assert(offset_of(Sig_Info, errno)      == 0x04)
 	#assert(offset_of(Sig_Info, code)       == 0x08)
@@ -615,7 +616,96 @@ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 	#assert(offset_of(Sig_Info, syscall)    == 0x18)
 	#assert(offset_of(Sig_Info, arch)       == 0x1C)
 } else {
-	// TODO
+	Sig_Info :: struct {
+		signo: Signal,
+		errno: Errno,
+		code: i32,
+		using _union: struct #raw_union {
+			_pad1: [SI_PAD_SIZE]u8,
+			using _kill: struct {
+				pid: Pid, /* sender's pid */
+				uid: Uid, /* sender's uid */
+			},
+			using _timer: struct {
+				timerid: i32,   /* timer id */
+				overrun: i32,   /* overrun count */
+				value: Sig_Val, /* timer value */
+			},
+			/* POSIX.1b signals */
+			using _rt: struct {
+				_pid0: Pid, /* sender's pid */
+				_uid0: Uid, /* sender's uid */
+			},
+			/* SIGCHLD */
+			using _sigchld: struct {
+				_pid1: Pid,  /* which child */
+				_uid1: Uid,  /* sender's uid */
+				status: i32, /* exit code */
+				utime: uint,
+				stime: uint, //clock_t
+			},
+			/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+			using _sigfault: struct {
+				addr: rawptr, /* faulting insn/memory ref. */
+				using _: struct #raw_union {
+					trapno: i32,   /* Trap number that caused signal */
+					addr_lsb: i16, /* LSB of the reported address */
+					using _addr_bnd: struct {
+						_pad2: u32,
+						lower: rawptr, /* lower bound during fault */
+						upper: rawptr, /* upper bound during fault */
+					},
+					using _addr_pkey: struct {
+						_pad3: u32,
+						pkey: u32, /* protection key on PTE that faulted */
+					},
+					using _perf: struct {
+						perf_data: u32,
+						perf_type: u32,
+						perf_flags: u32,
+					},
+				},
+			},
+			/* SIGPOLL */
+			using _sigpoll: struct {
+				band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
+				fd: Fd,
+			},
+			/* SIGSYS */
+			using _sigsys: struct {
+				call_addr: rawptr, /* calling user insn */
+				syscall: i32,      /* triggering system call number */
+				arch: u32,         /* AUDIT_ARCH_* of syscall */
+			},
+		},
+	}
+
+	#assert(size_of(Sig_Info) == 128)
+	#assert(offset_of(Sig_Info, signo)      == 0x00)
+	#assert(offset_of(Sig_Info, errno)      == 0x04)
+	#assert(offset_of(Sig_Info, code)       == 0x08)
+	#assert(offset_of(Sig_Info, pid)        == 0x0c)
+	#assert(offset_of(Sig_Info, uid)        == 0x10)
+	#assert(offset_of(Sig_Info, timerid)    == 0x0c)
+	#assert(offset_of(Sig_Info, overrun)    == 0x10)
+	#assert(offset_of(Sig_Info, value)      == 0x14)
+	#assert(offset_of(Sig_Info, status)     == 0x14)
+	#assert(offset_of(Sig_Info, utime)      == 0x18)
+	#assert(offset_of(Sig_Info, stime)      == 0x1c)
+	#assert(offset_of(Sig_Info, addr)       == 0x0c)
+	#assert(offset_of(Sig_Info, addr_lsb)   == 0x10)
+	#assert(offset_of(Sig_Info, trapno)     == 0x10)
+	#assert(offset_of(Sig_Info, lower)      == 0x14)
+	#assert(offset_of(Sig_Info, upper)      == 0x18)
+	#assert(offset_of(Sig_Info, pkey)       == 0x14)
+	#assert(offset_of(Sig_Info, perf_data)  == 0x10)
+	#assert(offset_of(Sig_Info, perf_type)  == 0x14)
+	#assert(offset_of(Sig_Info, perf_flags) == 0x18)
+	#assert(offset_of(Sig_Info, band)       == 0x0c)
+	#assert(offset_of(Sig_Info, fd)         == 0x10)
+	#assert(offset_of(Sig_Info, call_addr)  == 0x0c)
+	#assert(offset_of(Sig_Info, syscall)    == 0x10)
+	#assert(offset_of(Sig_Info, arch)       == 0x14)
 }
 
 SIGEV_MAX_SIZE :: 64
@@ -684,6 +774,14 @@ Address_Family :: distinct Protocol_Family
 */
 Socket_Msg :: bit_set[Socket_Msg_Bits; i32]
 
+/*
+	Struct representing a generic socket address.
+*/
+Sock_Addr :: struct #packed {
+	sa_family: Address_Family,
+	sa_data:   [14]u8,
+}
+
 /*
 	Struct representing IPv4 socket address.
 */
@@ -691,6 +789,7 @@ Sock_Addr_In :: struct #packed {
 	sin_family: Address_Family,
 	sin_port:   u16be,
 	sin_addr:   [4]u8,
+	sin_zero:   [size_of(Sock_Addr) - size_of(Address_Family) - size_of(u16be) - size_of([4]u8)]u8,
 }
 
 /*
@@ -720,6 +819,7 @@ Sock_Addr_Any :: struct #raw_union {
 		family: Address_Family,
 		port:   u16be,
 	},
+	using generic: Sock_Addr,
 	using ipv4: Sock_Addr_In,
 	using ipv6: Sock_Addr_In6,
 	using uds: Sock_Addr_Un,

+ 21 - 1
core/sys/windows/kernel32.odin

@@ -20,6 +20,15 @@ COMMON_LVB_GRID_RVERTICAL  :: WORD(0x1000)
 COMMON_LVB_REVERSE_VIDEO   :: WORD(0x4000)
 COMMON_LVB_UNDERSCORE      :: WORD(0x8000)
 COMMON_LVB_SBCSDBCS        :: WORD(0x0300)
+EV_BREAK                   :: DWORD(0x0040)
+EV_CTS                     :: DWORD(0x0008)
+EV_DSR                     :: DWORD(0x0010)
+EV_ERR                     :: DWORD(0x0080)
+EV_RING                    :: DWORD(0x0100)
+EV_RLSD                    :: DWORD(0x0020)
+EV_RXCHAR                  :: DWORD(0x0001)
+EV_RXFLAG                  :: DWORD(0x0002)
+EV_TXEMPTY                 :: DWORD(0x0004)
 
 @(default_calling_convention="system")
 foreign kernel32 {
@@ -109,7 +118,9 @@ foreign kernel32 {
 	ClearCommError :: proc(hFile: HANDLE, lpErrors: ^Com_Error, lpStat: ^COMSTAT) -> BOOL ---
 	GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
 	SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
-	GetCommPorts :: proc(lpPortNumbers: PULONG, uPortNumbersCount: ULONG, puPortNumbersFound: PULONG) -> ULONG ---
+	SetCommMask :: proc(handle: HANDLE, dwEvtMap: DWORD) -> BOOL ---
+	GetCommMask :: proc(handle: HANDLE, lpEvtMask: LPDWORD) -> BOOL ---
+	WaitCommEvent :: proc(handle: HANDLE, lpEvtMask: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL ---
 	GetCommandLineW :: proc() -> LPCWSTR ---
 	GetTempPathW :: proc(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD ---
 	GetCurrentProcess :: proc() -> HANDLE ---
@@ -239,6 +250,10 @@ foreign kernel32 {
 		hThread: HANDLE,
 		lpContext: LPCONTEXT,
 	) -> BOOL ---
+	SetThreadContext :: proc(
+		hThread: HANDLE,
+		lpContext: LPCONTEXT,
+	) -> BOOL ---
 	CreateProcessW :: proc(
 		lpApplicationName: LPCWSTR,
 		lpCommandLine: LPWSTR,
@@ -1068,6 +1083,11 @@ foreign one_core {
 		PageProtection: ULONG,
 		PreferredNode: ULONG,
 	) -> PVOID ---
+	GetCommPorts :: proc(
+		lpPortNumbers: PULONG,
+		uPortNumbersCount: ULONG,
+		puPortNumbersFound: PULONG,
+	) -> ULONG ---
 }
 
 

+ 26 - 2
core/sys/windows/types.odin

@@ -143,6 +143,7 @@ LPWSAPROTOCOL_INFO :: ^WSAPROTOCOL_INFO
 LPSTR :: ^CHAR
 LPWSTR :: ^WCHAR
 OLECHAR :: WCHAR
+BSTR :: ^OLECHAR
 LPOLESTR :: ^OLECHAR
 LPCOLESTR :: LPCSTR
 LPFILETIME :: ^FILETIME
@@ -2694,11 +2695,23 @@ EXCEPTION_MAXIMUM_PARAMETERS :: 15
 
 EXCEPTION_DATATYPE_MISALIGNMENT     :: 0x80000002
 EXCEPTION_BREAKPOINT                :: 0x80000003
+EXCEPTION_SINGLE_STEP               :: 0x80000004
 EXCEPTION_ACCESS_VIOLATION          :: 0xC0000005
+EXCEPTION_IN_PAGE_ERROR             :: 0xC0000006
 EXCEPTION_ILLEGAL_INSTRUCTION       :: 0xC000001D
+EXCEPTION_NONCONTINUABLE_EXCEPTION  :: 0xC0000025
+EXCEPTION_INVALID_DISPOSITION       :: 0xC0000026
 EXCEPTION_ARRAY_BOUNDS_EXCEEDED     :: 0xC000008C
+EXCEPTION_FLT_DENORMAL_OPERAND      :: 0xC000008D
+EXCEPTION_FLT_DIVIDE_BY_ZERO        :: 0xC000008E
+EXCEPTION_FLT_INEXACT_RESULT        :: 0xC000008F
+EXCEPTION_FLT_INVALID_OPERATION     :: 0xC0000090
+EXCEPTION_FLT_OVERFLOW              :: 0xC0000091
+EXCEPTION_FLT_STACK_CHECK           :: 0xC0000092
+EXCEPTION_FLT_UNDERFLOW             :: 0xC0000093
 EXCEPTION_INT_DIVIDE_BY_ZERO        :: 0xC0000094
 EXCEPTION_INT_OVERFLOW              :: 0xC0000095
+EXCEPTION_PRIV_INSTRUCTION          :: 0xC0000096
 EXCEPTION_STACK_OVERFLOW            :: 0xC00000FD
 STATUS_PRIVILEGED_INSTRUCTION       :: 0xC0000096
 
@@ -3415,8 +3428,6 @@ TIME_ZONE_INFORMATION :: struct {
 	DaylightBias: LONG,
 }
 
-
-@(private="file")
 IMAGE_DOS_HEADER :: struct {
 	e_magic:    WORD,
 	e_cblp:     WORD,
@@ -3534,6 +3545,19 @@ IMAGE_EXPORT_DIRECTORY :: struct {
 	AddressOfNameOrdinals: DWORD, // RVA from base of image
 }
 
+IMAGE_DEBUG_DIRECTORY :: struct {
+	Characteristics:  DWORD,
+	TimeDateStamp:    DWORD,
+	MajorVersion:     WORD,
+	MinorVersion:     WORD,
+	Type:             DWORD,
+	SizeOfData:       DWORD,
+	AddressOfRawData: DWORD,
+	PointerToRawData: DWORD,
+}
+
+IMAGE_DEBUG_TYPE_CODEVIEW :: 2
+
 SICHINTF :: DWORD
 SHCONTF :: DWORD
 SFGAOF :: ULONG

+ 1 - 0
core/sys/windows/user32.odin

@@ -51,6 +51,7 @@ foreign user32 {
 	IsWindowVisible :: proc(hwnd: HWND) -> BOOL ---
 	IsWindowEnabled :: proc(hwnd: HWND) -> BOOL ---
 	IsIconic :: proc(hwnd: HWND) -> BOOL ---
+	IsZoomed :: proc(hwnd: HWND) -> BOOL ---
 	BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
 	GetTopWindow :: proc(hWnd: HWND) -> HWND ---
 	SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---

+ 153 - 141
core/time/timezone/tz_windows.odin

@@ -11,146 +11,147 @@ TZ_Abbrev :: struct {
 	dst: string,
 }
 
-tz_abbrevs := map[string]TZ_Abbrev {
-	"Egypt Standard Time"             = {"EET", "EEST"},    // Africa/Cairo
-	"Morocco Standard Time"           = {"+00", "+01"},     // Africa/Casablanca
-	"South Africa Standard Time"      = {"SAST", "SAST"},   // Africa/Johannesburg
-	"South Sudan Standard Time"       = {"CAT", "CAT"},     // Africa/Juba
-	"Sudan Standard Time"             = {"CAT", "CAT"},     // Africa/Khartoum
-	"W. Central Africa Standard Time" = {"WAT", "WAT"},     // Africa/Lagos
-	"E. Africa Standard Time"         = {"EAT", "EAT"},     // Africa/Nairobi
-	"Sao Tome Standard Time"          = {"GMT", "GMT"},     // Africa/Sao_Tome
-	"Libya Standard Time"             = {"EET", "EET"},     // Africa/Tripoli
-	"Namibia Standard Time"           = {"CAT", "CAT"},     // Africa/Windhoek
-	"Aleutian Standard Time"          = {"HST", "HDT"},     // America/Adak
-	"Alaskan Standard Time"           = {"AKST", "AKDT"},   // America/Anchorage
-	"Tocantins Standard Time"         = {"-03", "-03"},     // America/Araguaina
-	"Paraguay Standard Time"          = {"-04", "-03"},     // America/Asuncion
-	"Bahia Standard Time"             = {"-03", "-03"},     // America/Bahia
-	"SA Pacific Standard Time"        = {"-05", "-05"},     // America/Bogota
-	"Argentina Standard Time"         = {"-03", "-03"},     // America/Buenos_Aires
-	"Eastern Standard Time (Mexico)"  = {"EST", "EST"},     // America/Cancun
-	"Venezuela Standard Time"         = {"-04", "-04"},     // America/Caracas
-	"SA Eastern Standard Time"        = {"-03", "-03"},     // America/Cayenne
-	"Central Standard Time"           = {"CST", "CDT"},     // America/Chicago
-	"Central Brazilian Standard Time" = {"-04", "-04"},     // America/Cuiaba
-	"Mountain Standard Time"          = {"MST", "MDT"},     // America/Denver
-	"Greenland Standard Time"         = {"-03", "-02"},     // America/Godthab
-	"Turks And Caicos Standard Time"  = {"EST", "EDT"},     // America/Grand_Turk
-	"Central America Standard Time"   = {"CST", "CST"},     // America/Guatemala
-	"Atlantic Standard Time"          = {"AST", "ADT"},     // America/Halifax
-	"Cuba Standard Time"              = {"CST", "CDT"},     // America/Havana
-	"US Eastern Standard Time"        = {"EST", "EDT"},     // America/Indianapolis
-	"SA Western Standard Time"        = {"-04", "-04"},     // America/La_Paz
-	"Pacific Standard Time"           = {"PST", "PDT"},     // America/Los_Angeles
-	"Mountain Standard Time (Mexico)" = {"MST", "MST"},     // America/Mazatlan
-	"Central Standard Time (Mexico)"  = {"CST", "CST"},     // America/Mexico_City
-	"Saint Pierre Standard Time"      = {"-03", "-02"},     // America/Miquelon
-	"Montevideo Standard Time"        = {"-03", "-03"},     // America/Montevideo
-	"Eastern Standard Time"           = {"EST", "EDT"},     // America/New_York
-	"US Mountain Standard Time"       = {"MST", "MST"},     // America/Phoenix
-	"Haiti Standard Time"             = {"EST", "EDT"},     // America/Port-au-Prince
-	"Magallanes Standard Time"        = {"-03", "-03"},     // America/Punta_Arenas
-	"Canada Central Standard Time"    = {"CST", "CST"},     // America/Regina
-	"Pacific SA Standard Time"        = {"-04", "-03"},     // America/Santiago
-	"E. South America Standard Time"  = {"-03", "-03"},     // America/Sao_Paulo
-	"Newfoundland Standard Time"      = {"NST", "NDT"},     // America/St_Johns
-	"Pacific Standard Time (Mexico)"  = {"PST", "PDT"},     // America/Tijuana
-	"Yukon Standard Time"             = {"MST", "MST"},     // America/Whitehorse
-	"Central Asia Standard Time"      = {"+06", "+06"},     // Asia/Almaty
-	"Jordan Standard Time"            = {"+03", "+03"},     // Asia/Amman
-	"Arabic Standard Time"            = {"+03", "+03"},     // Asia/Baghdad
-	"Azerbaijan Standard Time"        = {"+04", "+04"},     // Asia/Baku
-	"SE Asia Standard Time"           = {"+07", "+07"},     // Asia/Bangkok
-	"Altai Standard Time"             = {"+07", "+07"},     // Asia/Barnaul
-	"Middle East Standard Time"       = {"EET", "EEST"},    // Asia/Beirut
-	"India Standard Time"             = {"IST", "IST"},     // Asia/Calcutta
-	"Transbaikal Standard Time"       = {"+09", "+09"},     // Asia/Chita
-	"Sri Lanka Standard Time"         = {"+0530", "+0530"}, // Asia/Colombo
-	"Syria Standard Time"             = {"+03", "+03"},     // Asia/Damascus
-	"Bangladesh Standard Time"        = {"+06", "+06"},     // Asia/Dhaka
-	"Arabian Standard Time"           = {"+04", "+04"},     // Asia/Dubai
-	"West Bank Standard Time"         = {"EET", "EEST"},    // Asia/Hebron
-	"W. Mongolia Standard Time"       = {"+07", "+07"},     // Asia/Hovd
-	"North Asia East Standard Time"   = {"+08", "+08"},     // Asia/Irkutsk
-	"Israel Standard Time"            = {"IST", "IDT"},     // Asia/Jerusalem
-	"Afghanistan Standard Time"       = {"+0430", "+0430"}, // Asia/Kabul
-	"Russia Time Zone 11"             = {"+12", "+12"},     // Asia/Kamchatka
-	"Pakistan Standard Time"          = {"PKT", "PKT"},     // Asia/Karachi
-	"Nepal Standard Time"             = {"+0545", "+0545"}, // Asia/Katmandu
-	"North Asia Standard Time"        = {"+07", "+07"},     // Asia/Krasnoyarsk
-	"Magadan Standard Time"           = {"+11", "+11"},     // Asia/Magadan
-	"N. Central Asia Standard Time"   = {"+07", "+07"},     // Asia/Novosibirsk
-	"Omsk Standard Time"              = {"+06", "+06"},     // Asia/Omsk
-	"North Korea Standard Time"       = {"KST", "KST"},     // Asia/Pyongyang
-	"Qyzylorda Standard Time"         = {"+05", "+05"},     // Asia/Qyzylorda
-	"Myanmar Standard Time"           = {"+0630", "+0630"}, // Asia/Rangoon
-	"Arab Standard Time"              = {"+03", "+03"},     // Asia/Riyadh
-	"Sakhalin Standard Time"          = {"+11", "+11"},     // Asia/Sakhalin
-	"Korea Standard Time"             = {"KST", "KST"},     // Asia/Seoul
-	"China Standard Time"             = {"CST", "CST"},     // Asia/Shanghai
-	"Singapore Standard Time"         = {"+08", "+08"},     // Asia/Singapore
-	"Russia Time Zone 10"             = {"+11", "+11"},     // Asia/Srednekolymsk
-	"Taipei Standard Time"            = {"CST", "CST"},     // Asia/Taipei
-	"West Asia Standard Time"         = {"+05", "+05"},     // Asia/Tashkent
-	"Georgian Standard Time"          = {"+04", "+04"},     // Asia/Tbilisi
-	"Iran Standard Time"              = {"+0330", "+0330"}, // Asia/Tehran
-	"Tokyo Standard Time"             = {"JST", "JST"},     // Asia/Tokyo
-	"Tomsk Standard Time"             = {"+07", "+07"},     // Asia/Tomsk
-	"Ulaanbaatar Standard Time"       = {"+08", "+08"},     // Asia/Ulaanbaatar
-	"Vladivostok Standard Time"       = {"+10", "+10"},     // Asia/Vladivostok
-	"Yakutsk Standard Time"           = {"+09", "+09"},     // Asia/Yakutsk
-	"Ekaterinburg Standard Time"      = {"+05", "+05"},     // Asia/Yekaterinburg
-	"Caucasus Standard Time"          = {"+04", "+04"},     // Asia/Yerevan
-	"Azores Standard Time"            = {"-01", "+00"},     // Atlantic/Azores
-	"Cape Verde Standard Time"        = {"-01", "-01"},     // Atlantic/Cape_Verde
-	"Greenwich Standard Time"         = {"GMT", "GMT"},     // Atlantic/Reykjavik
-	"Cen. Australia Standard Time"    = {"ACST", "ACDT"},   // Australia/Adelaide
-	"E. Australia Standard Time"      = {"AEST", "AEST"},   // Australia/Brisbane
-	"AUS Central Standard Time"       = {"ACST", "ACST"},   // Australia/Darwin
-	"Aus Central W. Standard Time"    = {"+0845", "+0845"}, // Australia/Eucla
-	"Tasmania Standard Time"          = {"AEST", "AEDT"},   // Australia/Hobart
-	"Lord Howe Standard Time"         = {"+1030", "+11"},   // Australia/Lord_Howe
-	"W. Australia Standard Time"      = {"AWST", "AWST"},   // Australia/Perth
-	"AUS Eastern Standard Time"       = {"AEST", "AEDT"},   // Australia/Sydney
-	"UTC-11"                          = {"-11", "-11"},     // Etc/GMT+11
-	"Dateline Standard Time"          = {"-12", "-12"},     // Etc/GMT+12
-	"UTC-02"                          = {"-02", "-02"},     // Etc/GMT+2
-	"UTC-08"                          = {"-08", "-08"},     // Etc/GMT+8
-	"UTC-09"                          = {"-09", "-09"},     // Etc/GMT+9
-	"UTC+12"                          = {"+12", "+12"},     // Etc/GMT-12
-	"UTC+13"                          = {"+13", "+13"},     // Etc/GMT-13
-	"UTC"                             = {"UTC", "UTC"},     // Etc/UTC
-	"Astrakhan Standard Time"         = {"+04", "+04"},     // Europe/Astrakhan
-	"W. Europe Standard Time"         = {"CET", "CEST"},    // Europe/Berlin
-	"GTB Standard Time"               = {"EET", "EEST"},    // Europe/Bucharest
-	"Central Europe Standard Time"    = {"CET", "CEST"},    // Europe/Budapest
-	"E. Europe Standard Time"         = {"EET", "EEST"},    // Europe/Chisinau
-	"Turkey Standard Time"            = {"+03", "+03"},     // Europe/Istanbul
-	"Kaliningrad Standard Time"       = {"EET", "EET"},     // Europe/Kaliningrad
-	"FLE Standard Time"               = {"EET", "EEST"},    // Europe/Kiev
-	"GMT Standard Time"               = {"GMT", "BST"},     // Europe/London
-	"Belarus Standard Time"           = {"+03", "+03"},     // Europe/Minsk
-	"Russian Standard Time"           = {"MSK", "MSK"},     // Europe/Moscow
-	"Romance Standard Time"           = {"CET", "CEST"},    // Europe/Paris
-	"Russia Time Zone 3"              = {"+04", "+04"},     // Europe/Samara
-	"Saratov Standard Time"           = {"+04", "+04"},     // Europe/Saratov
-	"Volgograd Standard Time"         = {"MSK", "MSK"},     // Europe/Volgograd
-	"Central European Standard Time"  = {"CET", "CEST"},    // Europe/Warsaw
-	"Mauritius Standard Time"         = {"+04", "+04"},     // Indian/Mauritius
-	"Samoa Standard Time"             = {"+13", "+13"},     // Pacific/Apia
-	"New Zealand Standard Time"       = {"NZST", "NZDT"},   // Pacific/Auckland
-	"Bougainville Standard Time"      = {"+11", "+11"},     // Pacific/Bougainville
-	"Chatham Islands Standard Time"   = {"+1245", "+1345"}, // Pacific/Chatham
-	"Easter Island Standard Time"     = {"-06", "-05"},     // Pacific/Easter
-	"Fiji Standard Time"              = {"+12", "+12"},     // Pacific/Fiji
-	"Central Pacific Standard Time"   = {"+11", "+11"},     // Pacific/Guadalcanal
-	"Hawaiian Standard Time"          = {"HST", "HST"},     // Pacific/Honolulu
-	"Line Islands Standard Time"      = {"+14", "+14"},     // Pacific/Kiritimati
-	"Marquesas Standard Time"         = {"-0930", "-0930"}, // Pacific/Marquesas
-	"Norfolk Standard Time"           = {"+11", "+12"},     // Pacific/Norfolk
-	"West Pacific Standard Time"      = {"+10", "+10"},     // Pacific/Port_Moresby
-	"Tonga Standard Time"             = {"+13", "+13"},     // Pacific/Tongatapu
+@(rodata)
+tz_abbrevs := [?]struct{key: string, value: TZ_Abbrev}{
+	{"Egypt Standard Time",             {"EET", "EEST"}},    // Africa/Cairo
+	{"Morocco Standard Time",           {"+00", "+01"}},     // Africa/Casablanca
+	{"South Africa Standard Time",      {"SAST", "SAST"}},   // Africa/Johannesburg
+	{"South Sudan Standard Time",       {"CAT", "CAT"}},     // Africa/Juba
+	{"Sudan Standard Time",             {"CAT", "CAT"}},     // Africa/Khartoum
+	{"W. Central Africa Standard Time", {"WAT", "WAT"}},     // Africa/Lagos
+	{"E. Africa Standard Time",         {"EAT", "EAT"}},     // Africa/Nairobi
+	{"Sao Tome Standard Time",          {"GMT", "GMT"}},     // Africa/Sao_Tome
+	{"Libya Standard Time",             {"EET", "EET"}},     // Africa/Tripoli
+	{"Namibia Standard Time",           {"CAT", "CAT"}},     // Africa/Windhoek
+	{"Aleutian Standard Time",          {"HST", "HDT"}},     // America/Adak
+	{"Alaskan Standard Time",           {"AKST", "AKDT"}},   // America/Anchorage
+	{"Tocantins Standard Time",         {"-03", "-03"}},     // America/Araguaina
+	{"Paraguay Standard Time",          {"-04", "-03"}},     // America/Asuncion
+	{"Bahia Standard Time",             {"-03", "-03"}},     // America/Bahia
+	{"SA Pacific Standard Time",        {"-05", "-05"}},     // America/Bogota
+	{"Argentina Standard Time",         {"-03", "-03"}},     // America/Buenos_Aires
+	{"Eastern Standard Time (Mexico)",  {"EST", "EST"}},     // America/Cancun
+	{"Venezuela Standard Time",         {"-04", "-04"}},     // America/Caracas
+	{"SA Eastern Standard Time",        {"-03", "-03"}},     // America/Cayenne
+	{"Central Standard Time",           {"CST", "CDT"}},     // America/Chicago
+	{"Central Brazilian Standard Time", {"-04", "-04"}},     // America/Cuiaba
+	{"Mountain Standard Time",          {"MST", "MDT"}},     // America/Denver
+	{"Greenland Standard Time",         {"-03", "-02"}},     // America/Godthab
+	{"Turks And Caicos Standard Time",  {"EST", "EDT"}},     // America/Grand_Turk
+	{"Central America Standard Time",   {"CST", "CST"}},     // America/Guatemala
+	{"Atlantic Standard Time",          {"AST", "ADT"}},     // America/Halifax
+	{"Cuba Standard Time",              {"CST", "CDT"}},     // America/Havana
+	{"US Eastern Standard Time",        {"EST", "EDT"}},     // America/Indianapolis
+	{"SA Western Standard Time",        {"-04", "-04"}},     // America/La_Paz
+	{"Pacific Standard Time",           {"PST", "PDT"}},     // America/Los_Angeles
+	{"Mountain Standard Time (Mexico)", {"MST", "MST"}},     // America/Mazatlan
+	{"Central Standard Time (Mexico)",  {"CST", "CST"}},     // America/Mexico_City
+	{"Saint Pierre Standard Time",      {"-03", "-02"}},     // America/Miquelon
+	{"Montevideo Standard Time",        {"-03", "-03"}},     // America/Montevideo
+	{"Eastern Standard Time",           {"EST", "EDT"}},     // America/New_York
+	{"US Mountain Standard Time",       {"MST", "MST"}},     // America/Phoenix
+	{"Haiti Standard Time",             {"EST", "EDT"}},     // America/Port-au-Prince
+	{"Magallanes Standard Time",        {"-03", "-03"}},     // America/Punta_Arenas
+	{"Canada Central Standard Time",    {"CST", "CST"}},     // America/Regina
+	{"Pacific SA Standard Time",        {"-04", "-03"}},     // America/Santiago
+	{"E. South America Standard Time",  {"-03", "-03"}},     // America/Sao_Paulo
+	{"Newfoundland Standard Time",      {"NST", "NDT"}},     // America/St_Johns
+	{"Pacific Standard Time (Mexico)",  {"PST", "PDT"}},     // America/Tijuana
+	{"Yukon Standard Time",             {"MST", "MST"}},     // America/Whitehorse
+	{"Central Asia Standard Time",      {"+06", "+06"}},     // Asia/Almaty
+	{"Jordan Standard Time",            {"+03", "+03"}},     // Asia/Amman
+	{"Arabic Standard Time",            {"+03", "+03"}},     // Asia/Baghdad
+	{"Azerbaijan Standard Time",        {"+04", "+04"}},     // Asia/Baku
+	{"SE Asia Standard Time",           {"+07", "+07"}},     // Asia/Bangkok
+	{"Altai Standard Time",             {"+07", "+07"}},     // Asia/Barnaul
+	{"Middle East Standard Time",       {"EET", "EEST"}},    // Asia/Beirut
+	{"India Standard Time",             {"IST", "IST"}},     // Asia/Calcutta
+	{"Transbaikal Standard Time",       {"+09", "+09"}},     // Asia/Chita
+	{"Sri Lanka Standard Time",         {"+0530", "+0530"}}, // Asia/Colombo
+	{"Syria Standard Time",             {"+03", "+03"}},     // Asia/Damascus
+	{"Bangladesh Standard Time",        {"+06", "+06"}},     // Asia/Dhaka
+	{"Arabian Standard Time",           {"+04", "+04"}},     // Asia/Dubai
+	{"West Bank Standard Time",         {"EET", "EEST"}},    // Asia/Hebron
+	{"W. Mongolia Standard Time",       {"+07", "+07"}},     // Asia/Hovd
+	{"North Asia East Standard Time",   {"+08", "+08"}},     // Asia/Irkutsk
+	{"Israel Standard Time",            {"IST", "IDT"}},     // Asia/Jerusalem
+	{"Afghanistan Standard Time",       {"+0430", "+0430"}}, // Asia/Kabul
+	{"Russia Time Zone 11",             {"+12", "+12"}},     // Asia/Kamchatka
+	{"Pakistan Standard Time",          {"PKT", "PKT"}},     // Asia/Karachi
+	{"Nepal Standard Time",             {"+0545", "+0545"}}, // Asia/Katmandu
+	{"North Asia Standard Time",        {"+07", "+07"}},     // Asia/Krasnoyarsk
+	{"Magadan Standard Time",           {"+11", "+11"}},     // Asia/Magadan
+	{"N. Central Asia Standard Time",   {"+07", "+07"}},     // Asia/Novosibirsk
+	{"Omsk Standard Time",              {"+06", "+06"}},     // Asia/Omsk
+	{"North Korea Standard Time",       {"KST", "KST"}},     // Asia/Pyongyang
+	{"Qyzylorda Standard Time",         {"+05", "+05"}},     // Asia/Qyzylorda
+	{"Myanmar Standard Time",           {"+0630", "+0630"}}, // Asia/Rangoon
+	{"Arab Standard Time",              {"+03", "+03"}},     // Asia/Riyadh
+	{"Sakhalin Standard Time",          {"+11", "+11"}},     // Asia/Sakhalin
+	{"Korea Standard Time",             {"KST", "KST"}},     // Asia/Seoul
+	{"China Standard Time",             {"CST", "CST"}},     // Asia/Shanghai
+	{"Singapore Standard Time",         {"+08", "+08"}},     // Asia/Singapore
+	{"Russia Time Zone 10",             {"+11", "+11"}},     // Asia/Srednekolymsk
+	{"Taipei Standard Time",            {"CST", "CST"}},     // Asia/Taipei
+	{"West Asia Standard Time",         {"+05", "+05"}},     // Asia/Tashkent
+	{"Georgian Standard Time",          {"+04", "+04"}},     // Asia/Tbilisi
+	{"Iran Standard Time",              {"+0330", "+0330"}}, // Asia/Tehran
+	{"Tokyo Standard Time",             {"JST", "JST"}},     // Asia/Tokyo
+	{"Tomsk Standard Time",             {"+07", "+07"}},     // Asia/Tomsk
+	{"Ulaanbaatar Standard Time",       {"+08", "+08"}},     // Asia/Ulaanbaatar
+	{"Vladivostok Standard Time",       {"+10", "+10"}},     // Asia/Vladivostok
+	{"Yakutsk Standard Time",           {"+09", "+09"}},     // Asia/Yakutsk
+	{"Ekaterinburg Standard Time",      {"+05", "+05"}},     // Asia/Yekaterinburg
+	{"Caucasus Standard Time",          {"+04", "+04"}},     // Asia/Yerevan
+	{"Azores Standard Time",            {"-01", "+00"}},     // Atlantic/Azores
+	{"Cape Verde Standard Time",        {"-01", "-01"}},     // Atlantic/Cape_Verde
+	{"Greenwich Standard Time",         {"GMT", "GMT"}},     // Atlantic/Reykjavik
+	{"Cen. Australia Standard Time",    {"ACST", "ACDT"}},   // Australia/Adelaide
+	{"E. Australia Standard Time",      {"AEST", "AEST"}},   // Australia/Brisbane
+	{"AUS Central Standard Time",       {"ACST", "ACST"}},   // Australia/Darwin
+	{"Aus Central W. Standard Time",    {"+0845", "+0845"}}, // Australia/Eucla
+	{"Tasmania Standard Time",          {"AEST", "AEDT"}},   // Australia/Hobart
+	{"Lord Howe Standard Time",         {"+1030", "+11"}},   // Australia/Lord_Howe
+	{"W. Australia Standard Time",      {"AWST", "AWST"}},   // Australia/Perth
+	{"AUS Eastern Standard Time",       {"AEST", "AEDT"}},   // Australia/Sydney
+	{"UTC-11",                          {"-11", "-11"}},     // Etc/GMT+11
+	{"Dateline Standard Time",          {"-12", "-12"}},     // Etc/GMT+12
+	{"UTC-02",                          {"-02", "-02"}},     // Etc/GMT+2
+	{"UTC-08",                          {"-08", "-08"}},     // Etc/GMT+8
+	{"UTC-09",                          {"-09", "-09"}},     // Etc/GMT+9
+	{"UTC+12",                          {"+12", "+12"}},     // Etc/GMT-12
+	{"UTC+13",                          {"+13", "+13"}},     // Etc/GMT-13
+	{"UTC",                             {"UTC", "UTC"}},     // Etc/UTC
+	{"Astrakhan Standard Time",         {"+04", "+04"}},     // Europe/Astrakhan
+	{"W. Europe Standard Time",         {"CET", "CEST"}},    // Europe/Berlin
+	{"GTB Standard Time",               {"EET", "EEST"}},    // Europe/Bucharest
+	{"Central Europe Standard Time",    {"CET", "CEST"}},    // Europe/Budapest
+	{"E. Europe Standard Time",         {"EET", "EEST"}},    // Europe/Chisinau
+	{"Turkey Standard Time",            {"+03", "+03"}},     // Europe/Istanbul
+	{"Kaliningrad Standard Time",       {"EET", "EET"}},     // Europe/Kaliningrad
+	{"FLE Standard Time",               {"EET", "EEST"}},    // Europe/Kiev
+	{"GMT Standard Time",               {"GMT", "BST"}},     // Europe/London
+	{"Belarus Standard Time",           {"+03", "+03"}},     // Europe/Minsk
+	{"Russian Standard Time",           {"MSK", "MSK"}},     // Europe/Moscow
+	{"Romance Standard Time",           {"CET", "CEST"}},    // Europe/Paris
+	{"Russia Time Zone 3",              {"+04", "+04"}},     // Europe/Samara
+	{"Saratov Standard Time",           {"+04", "+04"}},     // Europe/Saratov
+	{"Volgograd Standard Time",         {"MSK", "MSK"}},     // Europe/Volgograd
+	{"Central European Standard Time",  {"CET", "CEST"}},    // Europe/Warsaw
+	{"Mauritius Standard Time",         {"+04", "+04"}},     // Indian/Mauritius
+	{"Samoa Standard Time",             {"+13", "+13"}},     // Pacific/Apia
+	{"New Zealand Standard Time",       {"NZST", "NZDT"}},   // Pacific/Auckland
+	{"Bougainville Standard Time",      {"+11", "+11"}},     // Pacific/Bougainville
+	{"Chatham Islands Standard Time",   {"+1245", "+1345"}}, // Pacific/Chatham
+	{"Easter Island Standard Time",     {"-06", "-05"}},     // Pacific/Easter
+	{"Fiji Standard Time",              {"+12", "+12"}},     // Pacific/Fiji
+	{"Central Pacific Standard Time",   {"+11", "+11"}},     // Pacific/Guadalcanal
+	{"Hawaiian Standard Time",          {"HST", "HST"}},     // Pacific/Honolulu
+	{"Line Islands Standard Time",      {"+14", "+14"}},     // Pacific/Kiritimati
+	{"Marquesas Standard Time",         {"-0930", "-0930"}}, // Pacific/Marquesas
+	{"Norfolk Standard Time",           {"+11", "+12"}},     // Pacific/Norfolk
+	{"West Pacific Standard Time",      {"+10", "+10"}},     // Pacific/Port_Moresby
+	{"Tonga Standard Time",             {"+13", "+13"}},     // Pacific/Tongatapu
 }
 
 iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) {
@@ -269,7 +270,18 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re
 	defer delete(wintz_name, allocator)
 	defer delete(iana_name, allocator)
 
-	abbrevs := tz_abbrevs[wintz_name] or_return
+	abbrevs: TZ_Abbrev
+	abbrevs_ok: bool
+	for pair in tz_abbrevs {
+		if pair.key == wintz_name {
+			abbrevs = pair.value
+			abbrevs_ok = true
+			break
+		}
+	}
+	if !abbrevs_ok {
+		return
+	}
 	if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std {
 		return nil, true
 	}

+ 1 - 0
examples/demo/demo.odin

@@ -1,4 +1,5 @@
 #+vet !using-stmt !using-param
+#+feature dynamic-literals
 package main
 
 import "core:fmt"

+ 12 - 6
src/build_settings.cpp

@@ -324,6 +324,18 @@ u64 get_vet_flag_from_name(String const &name) {
 	return VetFlag_NONE;
 }
 
+enum OptInFeatureFlags : u64 {
+	OptInFeatureFlag_NONE            = 0,
+	OptInFeatureFlag_DynamicLiterals = 1u<<0,
+};
+
+u64 get_feature_flag_from_name(String const &name) {
+	if (name == "dynamic-literals") {
+		return OptInFeatureFlag_DynamicLiterals;
+	}
+	return OptInFeatureFlag_NONE;
+}
+
 
 enum SanitizerFlags : u32 {
 	SanitizerFlag_NONE = 0,
@@ -429,7 +441,6 @@ struct BuildContext {
 	bool   ignore_unknown_attributes;
 	bool   no_bounds_check;
 	bool   no_type_assert;
-	bool   no_dynamic_literals;
 	bool   no_output_files;
 	bool   no_crt;
 	bool   no_rpath;
@@ -1855,11 +1866,6 @@ gb_internal bool init_build_paths(String init_filename) {
 		produces_output_file = true;
 	}
 
-	if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR ||
-	    build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
-		bc->no_dynamic_literals = true;
-	}
-
 	if (!produces_output_file) {
 		// Command doesn't produce output files. We're done.
 		return true;

+ 48 - 50
src/check_decl.cpp

@@ -94,12 +94,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
 				return nullptr;
 			}
 			if (e2->state.load() != EntityState_Resolved) {
-				gbString str = type_to_string(t);
-				defer (gb_string_free(str));
-				error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
-				e->type = t_invalid;
+				e->type = t;
 				return nullptr;
 			}
+			gbString str = type_to_string(t);
+			defer (gb_string_free(str));
+			error(operand->expr, "Invalid use of a non-specialized polymorphic type '%s' in %.*s", str, LIT(context_name));
+			e->type = t_invalid;
+			return nullptr;
 		} else if (is_type_empty_union(t)) {
 			gbString str = type_to_string(t);
 			defer (gb_string_free(str));
@@ -971,6 +973,43 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
 	}
 }
 
+gb_internal void check_foreign_procedure(CheckerContext *ctx, Entity *e, DeclInfo *d) {
+	GB_ASSERT(e != nullptr);
+	GB_ASSERT(e->kind == Entity_Procedure);
+	String name = e->Procedure.link_name;
+
+	mutex_lock(&ctx->info->foreign_mutex);
+
+	auto *fp = &ctx->info->foreigns;
+	StringHashKey key = string_hash_string(name);
+	Entity **found = string_map_get(fp, key);
+	if (found && e != *found) {
+		Entity *f = *found;
+		TokenPos pos = f->token.pos;
+		Type *this_type = base_type(e->type);
+		Type *other_type = base_type(f->type);
+		if (is_type_proc(this_type) && is_type_proc(other_type)) {
+			if (!are_signatures_similar_enough(this_type, other_type)) {
+				error(d->proc_lit,
+				      "Redeclaration of foreign procedure '%.*s' with different type signatures\n"
+				      "\tat %s",
+				      LIT(name), token_pos_to_string(pos));
+			}
+		} else if (!signature_parameter_similar_enough(this_type, other_type)) {
+			error(d->proc_lit,
+			      "Foreign entity '%.*s' previously declared elsewhere with a different type\n"
+			      "\tat %s",
+			      LIT(name), token_pos_to_string(pos));
+		}
+	} else if (name == "main") {
+		error(d->proc_lit, "The link name 'main' is reserved for internal use");
+	} else {
+		string_map_set(fp, key, e);
+	}
+
+	mutex_unlock(&ctx->info->foreign_mutex);
+}
+
 gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	GB_ASSERT(e->type == nullptr);
 	if (d->proc_lit->kind != Ast_ProcLit) {
@@ -1307,57 +1346,16 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 			name = e->Procedure.link_name;
 		}
 		Entity *foreign_library = init_entity_foreign_library(ctx, e);
-		
-		if (is_arch_wasm() && foreign_library != nullptr) {
-			String module_name = str_lit("env");
-			GB_ASSERT (foreign_library->kind == Entity_LibraryName);
-			if (foreign_library->LibraryName.paths.count != 1) {
-				error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
-				      LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
-			}
-
-			if (foreign_library->LibraryName.paths.count >= 1) {
-				module_name = foreign_library->LibraryName.paths[0];
-			}
-
-			if (!string_ends_with(module_name, str_lit(".o"))) {
-				name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
-			}
-		}
-
 		e->Procedure.is_foreign = true;
 		e->Procedure.link_name = name;
+		e->Procedure.foreign_library = foreign_library;
 
-		mutex_lock(&ctx->info->foreign_mutex);
-
-		auto *fp = &ctx->info->foreigns;
-		StringHashKey key = string_hash_string(name);
-		Entity **found = string_map_get(fp, key);
-		if (found && e != *found) {
-			Entity *f = *found;
-			TokenPos pos = f->token.pos;
-			Type *this_type = base_type(e->type);
-			Type *other_type = base_type(f->type);
-			if (is_type_proc(this_type) && is_type_proc(other_type)) {
-				if (!are_signatures_similar_enough(this_type, other_type)) {
-					error(d->proc_lit,
-					      "Redeclaration of foreign procedure '%.*s' with different type signatures\n"
-					      "\tat %s",
-					      LIT(name), token_pos_to_string(pos));
-				}
-			} else if (!signature_parameter_similar_enough(this_type, other_type)) {
-				error(d->proc_lit,
-				      "Foreign entity '%.*s' previously declared elsewhere with a different type\n"
-				      "\tat %s",
-				      LIT(name), token_pos_to_string(pos));
-			}
-		} else if (name == "main") {
-			error(d->proc_lit, "The link name 'main' is reserved for internal use");
+		if (is_arch_wasm() && foreign_library != nullptr) {
+			// NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later
+			mpsc_enqueue(&ctx->info->foreign_decls_to_check, e);
 		} else {
-			string_map_set(fp, key, e);
+			check_foreign_procedure(ctx, e, d);
 		}
-
-		mutex_unlock(&ctx->info->foreign_mutex);
 	} else {
 		String name = e->token.string;
 		if (e->Procedure.link_name.len > 0) {

+ 53 - 10
src/check_expr.cpp

@@ -3672,6 +3672,13 @@ gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x
 			}
 		}
 	}
+	if (is_type_simd_vector(x->type) && !is_type_simd_vector(y->type)) {
+		if (check_is_assignable_to(c, y, x->type)) {
+			if (check_binary_op(c, x, op)) {
+				return true;
+			}
+		}
+	}
 	return false;
 }
 
@@ -4556,6 +4563,19 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
 		break;
 	}
 	
+	case Type_SimdVector: {
+		Type *elem = base_array_type(t);
+		if (check_is_assignable_to(c, operand, elem)) {
+			operand->mode = Addressing_Value;
+		} else {
+			operand->mode = Addressing_Invalid;
+			convert_untyped_error(c, operand, target_type);
+			return;
+		}
+
+		break;
+	}
+	
 	case Type_Matrix: {
 		Type *elem = base_array_type(t);
 		if (check_is_assignable_to(c, operand, elem)) {
@@ -8725,6 +8745,18 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
 		error(node, "#caller_expression may only be used as a default argument parameter");
 		o->type = t_string;
 		o->mode = Addressing_Value;
+	} else if (name == "branch_location") {
+		if (!c->in_defer) {
+			error(node, "#branch_location may only be used within a 'defer' statement");
+		} else if (c->curr_proc_decl) {
+			Entity *e = c->curr_proc_decl->entity;
+			if (e != nullptr) {
+				GB_ASSERT(e->kind == Entity_Procedure);
+				e->Procedure.uses_branch_location = true;
+			}
+		}
+		o->type = t_source_code_location;
+		o->mode = Addressing_Value;
 	} else {
 		if (name == "location") {
 			init_core_source_code_location(c->checker);
@@ -9339,6 +9371,23 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
 	return false;
 }
 
+gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
+	if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
+		ERROR_BLOCK();
+		error(node, "Compound literals of dynamic types are disabled by default");
+		error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
+		error_line("\tWarning: Please understand that dynamic literals will implicitly allocate using the current 'context.allocator' in that scope\n");
+		if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+			error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+		} else if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+			error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+		}
+		return false;
+	}
+
+	return cl->elems.count > 0;
+}
+
 gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
 	ExprKind kind = Expr_Expr;
 	ast_node(cl, CompoundLit, node);
@@ -9539,11 +9588,6 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 			elem_type = t->DynamicArray.elem;
 			context_name = str_lit("dynamic array literal");
 			is_constant = false;
-
-			if (!build_context.no_dynamic_literals) {
-				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");
@@ -9718,8 +9762,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 
 
 		if (t->kind == Type_DynamicArray) {
-			if (build_context.no_dynamic_literals && cl->elems.count) {
-				error(node, "Compound literals of dynamic types have been disabled");
+			if (check_for_dynamic_literals(c, node, cl)) {
+				add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+				add_package_dependency(c, "runtime", "__dynamic_array_append");
 			}
 		}
 
@@ -10108,9 +10153,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 			}
 		}
 
-		if (build_context.no_dynamic_literals && cl->elems.count) {
-			error(node, "Compound literals of dynamic types have been disabled");
-		} else {
+		if (check_for_dynamic_literals(c, node, cl)) {
 			add_map_reserve_dependencies(c);
 			add_map_set_dependencies(c);
 		}

+ 8 - 1
src/check_type.cpp

@@ -2440,8 +2440,12 @@ gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc
 	bool success = true;
 	isize specialization_count = 0;
 	Type *params  = check_get_params(c, c->scope, pt->params, &variadic, &variadic_index, &success, &specialization_count, operands);
-	Type *results = check_get_results(c, c->scope, pt->results);
 
+	bool no_poly_return = c->disallow_polymorphic_return_types;
+	c->disallow_polymorphic_return_types = c->scope == c->polymorphic_scope;
+	// NOTE(zen3ger): if the parapoly scope is the current proc's scope, then the return types shall not declare new poly vars
+	Type *results = check_get_results(c, c->scope, pt->results);
+	c->disallow_polymorphic_return_types = no_poly_return;
 
 	isize param_count = 0;
 	isize result_count = 0;
@@ -3383,6 +3387,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
 		}
 		Type *t = alloc_type_generic(ctx->scope, 0, token.string, specific);
 		if (ctx->allow_polymorphic_types) {
+			if (ctx->disallow_polymorphic_return_types) {
+				error(ident, "Undeclared polymorphic parameter '%.*s' in return type", LIT(token.string));
+			}
 			Scope *ps = ctx->polymorphic_scope;
 			Scope *s = ctx->scope;
 			Scope *entity_scope = s;

+ 51 - 1
src/checker.cpp

@@ -542,6 +542,23 @@ gb_internal u64 check_vet_flags(Ast *node) {
 	return ast_file_vet_flags(file);
 }
 
+gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
+	AstFile *file = c->file;
+	if (file == nullptr &&
+	    c->curr_proc_decl &&
+	    c->curr_proc_decl->proc_lit) {
+		file = c->curr_proc_decl->proc_lit->file();
+	}
+	if (file == nullptr) {
+		file = node->file();
+	}
+	if (file != nullptr && file->feature_flags_set) {
+		return file->feature_flags;
+	}
+	return 0;
+}
+
+
 enum VettedEntityKind {
 	VettedEntity_Invalid,
 
@@ -1164,7 +1181,6 @@ gb_internal void init_universal(void) {
 	add_global_bool_constant("ODIN_NO_BOUNDS_CHECK",            build_context.no_bounds_check);
 	add_global_bool_constant("ODIN_NO_TYPE_ASSERT",             build_context.no_type_assert);
 	add_global_bool_constant("ODIN_DEFAULT_TO_PANIC_ALLOCATOR", bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR);
-	add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS",        bc->no_dynamic_literals);
 	add_global_bool_constant("ODIN_NO_CRT",                     bc->no_crt);
 	add_global_bool_constant("ODIN_USE_SEPARATE_MODULES",       bc->use_separate_modules);
 	add_global_bool_constant("ODIN_TEST",                       bc->command_kind == Command_test);
@@ -1356,6 +1372,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
 	mpsc_init(&i->required_global_variable_queue, a); // 1<<10);
 	mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10);
 	mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10);
+	mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
 	mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
 
 	string_map_init(&i->load_directory_cache);
@@ -1382,6 +1399,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
 	mpsc_destroy(&i->required_global_variable_queue);
 	mpsc_destroy(&i->required_foreign_imports_through_force_queue);
 	mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
+	mpsc_destroy(&i->foreign_decls_to_check);
 
 	map_destroy(&i->objc_msgSend_types);
 	string_map_destroy(&i->load_file_cache);
@@ -5094,6 +5112,38 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) {
 
 		e->LibraryName.paths = fl->fullpaths;
 	}
+
+	for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_decls_to_check, &e); /**/) {
+		GB_ASSERT(e != nullptr);
+		if (e->kind != Entity_Procedure) {
+			continue;
+		}
+		if (!is_arch_wasm()) {
+			continue;
+		}
+		Entity *foreign_library = e->Procedure.foreign_library;
+		GB_ASSERT(foreign_library != nullptr);
+
+		String name = e->Procedure.link_name;
+
+		String module_name = str_lit("env");
+		GB_ASSERT (foreign_library->kind == Entity_LibraryName);
+		if (foreign_library->LibraryName.paths.count != 1) {
+			error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
+			      LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
+		}
+
+		if (foreign_library->LibraryName.paths.count >= 1) {
+			module_name = foreign_library->LibraryName.paths[0];
+		}
+
+		if (!string_ends_with(module_name, str_lit(".o"))) {
+			name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
+		}
+		e->Procedure.link_name = name;
+
+		check_foreign_procedure(&ctx, e, e->decl_info);
+	}
 }
 
 gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {

+ 2 - 0
src/checker.hpp

@@ -461,6 +461,7 @@ struct CheckerInfo {
 	MPSCQueue<Entity *> required_global_variable_queue;
 	MPSCQueue<Entity *> required_foreign_imports_through_force_queue;
 	MPSCQueue<Entity *> foreign_imports_to_check_fullpaths;
+	MPSCQueue<Entity *> foreign_decls_to_check;
 
 	MPSCQueue<Ast *> intrinsics_entry_point_usage;
 
@@ -521,6 +522,7 @@ struct CheckerContext {
 	bool       in_enum_type;
 	bool       collect_delayed_decls;
 	bool       allow_polymorphic_types;
+	bool       disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
 	bool       no_polymorphic_errors;
 	bool       hide_polymorphic_errors;
 	bool       in_polymorphic_specialization;

+ 1 - 0
src/entity.cpp

@@ -256,6 +256,7 @@ struct Entity {
 			bool    entry_point_only           : 1;
 			bool    has_instrumentation        : 1;
 			bool    is_memcpy_like             : 1;
+			bool    uses_branch_location       : 1;
 		} Procedure;
 		struct {
 			Array<Entity *> entities;

+ 0 - 2
src/llvm_backend.cpp

@@ -1096,8 +1096,6 @@ gb_internal void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_
 }
 
 gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
-	GB_ASSERT(!build_context.no_dynamic_literals);
-
 	TEMPORARY_ALLOCATOR_GUARD();
 
 	String proc_name = {};

+ 8 - 1
src/llvm_backend.hpp

@@ -359,6 +359,10 @@ struct lbProcedure {
 	bool             in_multi_assignment;
 	Array<LLVMValueRef> raw_input_parameters;
 
+	bool             uses_branch_location;
+	TokenPos         branch_location_pos;
+	TokenPos         curr_token_pos;
+
 	Array<lbVariadicReuseSlices> variadic_reuses;
 	lbAddr variadic_reuse_base_array_ptr;
 
@@ -444,7 +448,8 @@ gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isiz
 
 gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
 gb_internal lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
-gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, TokenPos pos);
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node);
 gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
 gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
 gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none);
@@ -742,3 +747,5 @@ gb_global char const *llvm_linkage_strings[] = {
 };
 
 #define ODIN_METADATA_IS_PACKED str_lit("odin-is-packed")
+#define ODIN_METADATA_MIN_ALIGN str_lit("odin-min-align")
+#define ODIN_METADATA_MAX_ALIGN str_lit("odin-max-align")

+ 11 - 5
src/llvm_backend_expr.cpp

@@ -3502,7 +3502,13 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
 
 	case_ast_node(bd, BasicDirective, expr);
 		TokenPos pos = bd->token.pos;
-		GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string));
+		String name = bd->name.string;
+		if (name == "branch_location") {
+			GB_ASSERT(p->uses_branch_location);
+			String proc_name = p->entity->token.string;
+			return lb_emit_source_code_location_as_global(p, proc_name, p->branch_location_pos);
+		}
+		GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(name));
 	case_end;
 
 	case_ast_node(i, Implicit, expr);
@@ -3668,7 +3674,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
 
 		lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
 		lb_start_block(p, else_);
-		lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
+		lb_emit_defer_stmts(p, lbDeferExit_Branch, block, expr);
 		lb_emit_jump(p, block);
 		lb_start_block(p, then);
 
@@ -4807,7 +4813,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 		if (cl->elems.count == 0) {
 			break;
 		}
-		GB_ASSERT(!build_context.no_dynamic_literals);
+		GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
 
 		lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
 		gb_unused(err);
@@ -4896,7 +4902,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 		if (cl->elems.count == 0) {
 			break;
 		}
-		GB_ASSERT(!build_context.no_dynamic_literals);
+		GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
 
 		Type *et = bt->DynamicArray.elem;
 		lbValue size  = lb_const_int(p->module, t_int, type_size_of(et));
@@ -5493,7 +5499,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 
 		lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
 		lb_start_block(p, else_);
-		lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
+		lb_emit_defer_stmts(p, lbDeferExit_Branch, block, expr);
 		lb_emit_jump(p, block);
 		lb_start_block(p, then);
 

+ 17 - 1
src/llvm_backend_general.cpp

@@ -734,6 +734,17 @@ gb_internal LLVMValueRef OdinLLVMBuildLoad(lbProcedure *p, LLVMTypeRef type, LLV
 		if (is_packed != 0) {
 			LLVMSetAlignment(result, 1);
 		}
+		u64 align = LLVMGetAlignment(result);
+		u64 align_min = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_MIN_ALIGN);
+		u64 align_max = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_MAX_ALIGN);
+		if (align_min != 0 && align < align_min) {
+			align = align_min;
+		}
+		if (align_max != 0 && align > align_max) {
+			align = align_max;
+		}
+		GB_ASSERT(align <= UINT_MAX);
+		LLVMSetAlignment(result, (unsigned int)align);
 	}
 
 	return result;
@@ -2121,6 +2132,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 			}
 			
 			i64 prev_offset = 0;
+			bool requires_packing = type->Struct.is_packed;
 			for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
 				Entity *field = type->Struct.fields[field_index];
 				i64 offset = type->Struct.offsets[field_index];
@@ -2141,6 +2153,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 					field_type = t_rawptr;
 				}
 
+				// max_field_align might misalign items in a way that requires packing
+				// so check the alignment of all fields to see if packing is required.
+				requires_packing = requires_packing || ((offset % type_align_of(field_type)) != 0);
+
 				array_add(&fields, lb_type(m, field_type));
 
 				prev_offset = offset + type_size_of(field->type);
@@ -2155,7 +2171,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				GB_ASSERT(fields[i] != nullptr);
 			}
 			
-			LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, type->Struct.is_packed);
+			LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing);
 			map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
 			map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);			
 			#if 0

+ 5 - 1
src/llvm_backend_proc.cpp

@@ -125,6 +125,10 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
 	// map_init(&p->selector_addr,    0);
 	// map_init(&p->tuple_fix_map,    0);
 
+	if (p->entity != nullptr && p->entity->Procedure.uses_branch_location) {
+		p->uses_branch_location = true;
+	}
+
 	if (p->is_foreign) {
 		lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
 	}
@@ -757,7 +761,7 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
 	if (p->type->Proc.result_count == 0) {
 		instr = LLVMGetLastInstruction(p->curr_block->block);
 		if (!lb_is_instr_terminating(instr)) {
-			lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+			lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, p->body);
 			lb_set_debug_position_to_procedure_end(p);
 			LLVMBuildRetVoid(p->builder);
 		}

+ 52 - 29
src/llvm_backend_stmt.cpp

@@ -208,8 +208,8 @@ gb_internal void lb_open_scope(lbProcedure *p, Scope *s) {
 
 }
 
-gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, bool pop_stack=true) {
-	lb_emit_defer_stmts(p, kind, block);
+gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node, bool pop_stack=true) {
+	lb_emit_defer_stmts(p, kind, block, node);
 	GB_ASSERT(p->scope_index > 0);
 
 	// NOTE(bill): Remove `context`s made in that scope
@@ -721,7 +721,7 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
 
 		lb_build_stmt(p, rs->body);
 
-		lb_close_scope(p, lbDeferExit_Default, nullptr);
+		lb_close_scope(p, lbDeferExit_Default, nullptr, node->left);
 		lb_pop_target_list(p);
 
 		if (check != nullptr) {
@@ -854,7 +854,7 @@ gb_internal void lb_build_range_tuple(lbProcedure *p, AstRangeStmt *rs, Scope *s
 
 	lb_build_stmt(p, rs->body);
 
-	lb_close_scope(p, lbDeferExit_Default, nullptr);
+	lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
 	lb_pop_target_list(p);
 	lb_emit_jump(p, loop);
 	lb_start_block(p, done);
@@ -976,7 +976,7 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
 
 	lb_build_stmt(p, rs->body);
 
-	lb_close_scope(p, lbDeferExit_Default, nullptr);
+	lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
 	lb_pop_target_list(p);
 	lb_emit_jump(p, loop);
 	lb_start_block(p, done);
@@ -1192,7 +1192,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
 
 	lb_build_stmt(p, rs->body);
 
-	lb_close_scope(p, lbDeferExit_Default, nullptr);
+	lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
 	lb_pop_target_list(p);
 	lb_emit_jump(p, loop);
 	lb_start_block(p, done);
@@ -1363,7 +1363,7 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
 	}
 
 
-	lb_close_scope(p, lbDeferExit_Default, nullptr);
+	lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
 }
 
 gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_found_) {
@@ -1433,6 +1433,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
 	ast_node(body, BlockStmt, ss->body);
 
 	isize case_count = body->stmts.count;
+	Ast *default_clause = nullptr;
 	Slice<Ast *> default_stmts = {};
 	lbBlock *default_fall = nullptr;
 	lbBlock *default_block = nullptr;
@@ -1482,6 +1483,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
 
 		if (cc->list.count == 0) {
 			// default case
+			default_clause = clause;
 			default_stmts = cc->stmts;
 			default_fall  = fall;
 			if (switch_instr == nullptr) {
@@ -1552,7 +1554,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
 		lb_push_target_list(p, ss->label, done, nullptr, fall);
 		lb_open_scope(p, body->scope);
 		lb_build_stmt_list(p, cc->stmts);
-		lb_close_scope(p, lbDeferExit_Default, body);
+		lb_close_scope(p, lbDeferExit_Default, body, clause);
 		lb_pop_target_list(p);
 
 		lb_emit_jump(p, done);
@@ -1570,13 +1572,13 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
 		lb_push_target_list(p, ss->label, done, nullptr, default_fall);
 		lb_open_scope(p, default_block->scope);
 		lb_build_stmt_list(p, default_stmts);
-		lb_close_scope(p, lbDeferExit_Default, default_block);
+		lb_close_scope(p, lbDeferExit_Default, default_block, default_clause);
 		lb_pop_target_list(p);
 	}
 
 	lb_emit_jump(p, done);
 	lb_start_block(p, done);
-	lb_close_scope(p, lbDeferExit_Default, done);
+	lb_close_scope(p, lbDeferExit_Default, done, ss->body);
 }
 
 gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value, bool is_default_case) {
@@ -1627,7 +1629,7 @@ gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBl
 
 	lb_push_target_list(p, label, done, nullptr, nullptr);
 	lb_build_stmt_list(p, cc->stmts);
-	lb_close_scope(p, lbDeferExit_Default, body);
+	lb_close_scope(p, lbDeferExit_Default, body, clause);
 	lb_pop_target_list(p);
 
 	lb_emit_jump(p, done);
@@ -1835,7 +1837,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
 
 	lb_emit_jump(p, done);
 	lb_start_block(p, done);
-	lb_close_scope(p, lbDeferExit_Default, done);
+	lb_close_scope(p, lbDeferExit_Default, done, ss->body);
 }
 
 
@@ -1959,7 +1961,7 @@ gb_internal void lb_build_assignment(lbProcedure *p, Array<lbAddr> &lvals, Slice
 	p->in_multi_assignment = prev_in_assignment;
 }
 
-gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
+gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res, TokenPos pos) {
 	lbFunctionType *ft = lb_get_function_type(p->module, p->type);
 	bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
 	bool split_returns = ft->multiple_return_original_type != nullptr;
@@ -1982,7 +1984,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
 			LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value);
 		}
 
-		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
 
 		// Check for terminator in the defer stmts
 		LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
@@ -2012,7 +2014,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
 			ret_val = OdinLLVMBuildTransmute(p, ret_val, ret_type);
 		}
 
-		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
 
 		// Check for terminator in the defer stmts
 		LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
@@ -2021,7 +2023,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
 		}
 	}
 }
-gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
+gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results, TokenPos pos) {
 	lb_ensure_abi_function_type(p->module, p);
 
 	isize return_count = p->type->Proc.result_count;
@@ -2029,7 +2031,7 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
 	if (return_count == 0) {
 		// No return values
 
-		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
 		
 		// Check for terminator in the defer stmts
 		LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
@@ -2138,11 +2140,11 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
 				GB_ASSERT(result_values.count-1 == result_eps.count);
 				lb_addr_store(p, p->return_ptr, result_values[result_values.count-1]);
 
-				lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+				lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
 				LLVMBuildRetVoid(p->builder);
 				return;
 			} else {
-				return lb_build_return_stmt_internal(p, result_values[result_values.count-1]);
+				return lb_build_return_stmt_internal(p, result_values[result_values.count-1], pos);
 			}
 
 		} else {
@@ -2169,7 +2171,7 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
 			}
 
 			if (return_by_pointer) {
-				lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+				lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
 				LLVMBuildRetVoid(p->builder);
 				return;
 			}
@@ -2177,13 +2179,13 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
 			res = lb_emit_load(p, res);
 		}
 	}
-	lb_build_return_stmt_internal(p, res);
+	lb_build_return_stmt_internal(p, res, pos);
 }
 
 gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 	ast_node(is, IfStmt, node);
 	lb_open_scope(p, is->scope); // Scope #1
-	defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
+	defer (lb_close_scope(p, lbDeferExit_Default, nullptr, node));
 
 	lbBlock *then = lb_create_block(p, "if.then");
 	lbBlock *done = lb_create_block(p, "if.done");
@@ -2234,7 +2236,7 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 
 				lb_open_scope(p, scope_of_node(is->else_stmt));
 				lb_build_stmt(p, is->else_stmt);
-				lb_close_scope(p, lbDeferExit_Default, nullptr);
+				lb_close_scope(p, lbDeferExit_Default, nullptr, is->else_stmt);
 			}
 			lb_emit_jump(p, done);
 
@@ -2251,7 +2253,7 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 
 			lb_open_scope(p, scope_of_node(is->else_stmt));
 			lb_build_stmt(p, is->else_stmt);
-			lb_close_scope(p, lbDeferExit_Default, nullptr);
+			lb_close_scope(p, lbDeferExit_Default, nullptr, is->else_stmt);
 
 			lb_emit_jump(p, done);
 		}
@@ -2322,7 +2324,7 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
 	}
 
 	lb_start_block(p, done);
-	lb_close_scope(p, lbDeferExit_Default, nullptr);
+	lb_close_scope(p, lbDeferExit_Default, nullptr, node);
 }
 
 gb_internal void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
@@ -2588,7 +2590,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
 
 		lb_open_scope(p, bs->scope);
 		lb_build_stmt_list(p, bs->stmts);
-		lb_close_scope(p, lbDeferExit_Default, nullptr);
+		lb_close_scope(p, lbDeferExit_Default, nullptr, node);
 
 		if (done != nullptr) {
 			lb_emit_jump(p, done);
@@ -2702,7 +2704,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
 	case_end;
 
 	case_ast_node(rs, ReturnStmt, node);
-		lb_build_return_stmt(p, rs->results);
+		lb_build_return_stmt(p, rs->results, ast_token(node).pos);
 	case_end;
 
 	case_ast_node(is, IfStmt, node);
@@ -2755,7 +2757,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
 			}
 		}
 		if (block != nullptr) {
-			lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
+			lb_emit_defer_stmts(p, lbDeferExit_Branch, block, node);
 		}
 		lb_emit_jump(p, block);
 		lb_start_block(p, lb_create_block(p, "unreachable"));
@@ -2795,7 +2797,13 @@ gb_internal void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
 	}
 }
 
-gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, TokenPos pos) {
+	TokenPos prev_token_pos = p->branch_location_pos;
+	if (p->uses_branch_location) {
+		p->branch_location_pos = pos;
+	}
+	defer (p->branch_location_pos = prev_token_pos);
+
 	isize count = p->defer_stmts.count;
 	isize i = count;
 	while (i --> 0) {
@@ -2822,6 +2830,21 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo
 	}
 }
 
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node) {
+	TokenPos pos = {};
+	if (node) {
+		if (node->kind == Ast_BlockStmt) {
+			pos = ast_end_token(node).pos;
+		} else if (node->kind == Ast_CaseClause) {
+			pos = ast_end_token(node).pos;
+		} else {
+			pos = ast_token(node).pos;
+		}
+	}
+	return lb_emit_defer_stmts(p, kind, block, pos);
+}
+
+
 gb_internal void lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt) {
 	Type *pt = base_type(p->type);
 	GB_ASSERT(pt->kind == Type_Proc);

+ 20 - 7
src/llvm_backend_utility.cpp

@@ -476,8 +476,8 @@ gb_internal lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, Ty
 	}
 }
 
-gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
-gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res);
+gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results, TokenPos pos);
+gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res, TokenPos pos);
 
 gb_internal lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
 	lbValue lhs = {};
@@ -506,10 +506,10 @@ gb_internal lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue con
 			lbValue found = map_must_get(&p->module->values, end_entity);
 			lb_emit_store(p, found, rhs);
 
-			lb_build_return_stmt(p, {});
+			lb_build_return_stmt(p, {}, ast_token(arg).pos);
 		} else {
 			GB_ASSERT(tuple->variables.count == 1);
-			lb_build_return_stmt_internal(p, rhs);
+			lb_build_return_stmt_internal(p, rhs, ast_token(arg).pos);
 		}
 	}
 	lb_start_block(p, continue_block);
@@ -1200,9 +1200,22 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 	lbValue gep = lb_emit_struct_ep_internal(p, s, index, result_type);
 
 	Type *bt = base_type(t);
-	if (bt->kind == Type_Struct && bt->Struct.is_packed) {
-		lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED, 1);
-		GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED) == 1);
+	if (bt->kind == Type_Struct) {
+		if (bt->Struct.is_packed) {
+			lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED, 1);
+			GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED) == 1);
+		}
+		u64 align_max = bt->Struct.custom_max_field_align;
+		u64 align_min = bt->Struct.custom_min_field_align;
+		GB_ASSERT(align_min == 0 || align_max == 0 || align_min <= align_max);
+		if (align_max) {
+			lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MAX_ALIGN, align_max);
+			GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MAX_ALIGN) == align_max);
+		}
+		if (align_min) {
+			lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MIN_ALIGN, align_min);
+			GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MIN_ALIGN) == align_min);
+		}
 	}
 
 	return gep;

+ 3 - 3
src/main.cpp

@@ -1192,7 +1192,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
 							build_context.no_type_assert = true;
 							break;
 						case BuildFlag_NoDynamicLiterals:
-							build_context.no_dynamic_literals = true;
+							gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n");
 							break;
 						case BuildFlag_NoCRT:
 							build_context.no_crt = true;
@@ -2120,7 +2120,7 @@ gb_internal void export_dependencies(Checker *c) {
 		for_array(i, files) {
 			AstFile *file = files[i];
 			gb_fprintf(&f, "\t\t\"%.*s\"", LIT(file->fullpath));
-			if (i+1 == files.count) {
+			if (i+1 < files.count) {
 				gb_fprintf(&f, ",");
 			}
 			gb_fprintf(&f, "\n");
@@ -2133,7 +2133,7 @@ gb_internal void export_dependencies(Checker *c) {
 		for_array(i, load_files) {
 			LoadFileCache *cache = load_files[i];
 			gb_fprintf(&f, "\t\t\"%.*s\"", LIT(cache->path));
-			if (i+1 == load_files.count) {
+			if (i+1 < load_files.count) {
 				gb_fprintf(&f, ",");
 			}
 			gb_fprintf(&f, "\n");

+ 67 - 3
src/parser.cpp

@@ -6265,10 +6265,16 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
 			syntax_error(token_for_pos, "Invalid vet flag name: %.*s", LIT(p));
 			error_line("\tExpected one of the following\n");
 			error_line("\tunused\n");
+			error_line("\tunused-variables\n");
+			error_line("\tunused-imports\n");
+			error_line("\tunused-procedures\n");
 			error_line("\tshadowing\n");
 			error_line("\tusing-stmt\n");
 			error_line("\tusing-param\n");
+			error_line("\tstyle\n");
 			error_line("\textra\n");
+			error_line("\tcast\n");
+			error_line("\ttabs\n");
 			return build_context.vet_flags;
 		}
 	}
@@ -6286,6 +6292,63 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
 	return vet_flags &~ vet_not_flags;
 }
 
+gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
+	String const prefix = str_lit("feature");
+	GB_ASSERT(string_starts_with(s, prefix));
+	s = string_trim_whitespace(substring(s, prefix.len, s.len));
+
+	if (s.len == 0) {
+		return OptInFeatureFlag_NONE;
+	}
+
+	u64 feature_flags = 0;
+	u64 feature_not_flags = 0;
+
+	while (s.len > 0) {
+		String p = string_trim_whitespace(vet_tag_get_token(s, &s));
+		if (p.len == 0) {
+			break;
+		}
+
+		bool is_notted = false;
+		if (p[0] == '!') {
+			is_notted = true;
+			p = substring(p, 1, p.len);
+			if (p.len == 0) {
+				syntax_error(token_for_pos, "Expected a feature flag name after '!'");
+				return OptInFeatureFlag_NONE;
+			}
+		}
+
+		u64 flag = get_feature_flag_from_name(p);
+		if (flag != OptInFeatureFlag_NONE) {
+			if (is_notted) {
+				feature_not_flags |= flag;
+			} else {
+				feature_flags     |= flag;
+			}
+		} else {
+			ERROR_BLOCK();
+			syntax_error(token_for_pos, "Invalid feature flag name: %.*s", LIT(p));
+			error_line("\tExpected one of the following\n");
+			error_line("\tdynamic-literals\n");
+			return OptInFeatureFlag_NONE;
+		}
+	}
+
+	if (feature_flags == 0 && feature_not_flags == 0) {
+		return OptInFeatureFlag_NONE;
+	}
+	if (feature_flags == 0 && feature_not_flags != 0) {
+		return OptInFeatureFlag_NONE &~ feature_not_flags;
+	}
+	if (feature_flags != 0 && feature_not_flags == 0) {
+		return feature_flags;
+	}
+	GB_ASSERT(feature_flags != 0 && feature_not_flags != 0);
+	return feature_flags &~ feature_not_flags;
+}
+
 gb_internal String dir_from_path(String path) {
 	String base_dir = path;
 	for (isize i = path.len-1; i >= 0; i--) {
@@ -6399,6 +6462,9 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f)
 		} else if (command == "file") {
 			f->flags |= AstFile_IsPrivateFile;
 		}
+	} else if (string_starts_with(lc, str_lit("feature"))) {
+		f->feature_flags |= parse_feature_tag(tok, lc);
+		f->feature_flags_set = true;
 	} else if (lc == "lazy") {
 		if (build_context.ignore_lazy) {
 			// Ignore
@@ -6493,9 +6559,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
 	}
 	f->package_name = package_name.string;
 
-	// TODO: Shouldn't single file only matter for build tags? no-instrumentation for example
-	// should be respected even when in single file mode.
-	if (!f->pkg->is_single_file) {
+	{
 		if (docs != nullptr && docs->list.count > 0) {
 			for (Token const &tok : docs->list) {
 				GB_ASSERT(tok.kind == Token_Comment);

+ 2 - 0
src/parser.hpp

@@ -108,7 +108,9 @@ struct AstFile {
 	String       package_name;
 
 	u64          vet_flags;
+	u64          feature_flags;
 	bool         vet_flags_set;
+	bool         feature_flags_set;
 
 	// >= 0: In Expression
 	// <  0: In Control Clause

+ 1 - 0
tests/core/encoding/cbor/test_core_cbor.odin

@@ -1,3 +1,4 @@
+#+feature dynamic-literals
 package test_encoding_cbor
 
 import "base:intrinsics"

+ 1 - 0
tests/core/fmt/test_core_fmt.odin

@@ -1,3 +1,4 @@
+#+feature dynamic-literals
 package test_core_fmt
 
 import "base:runtime"

+ 1 - 0
tests/core/hash/test_core_hash.odin

@@ -1,3 +1,4 @@
+#+feature dynamic-literals
 package test_core_hash
 
 import "core:hash/xxhash"

+ 1 - 0
tests/core/hash/test_vectors_xxhash.odin

@@ -1,4 +1,5 @@
 // Hash Test Vectors
+#+feature dynamic-literals
 package test_core_hash
 
 XXHASH_Test_Vectors :: struct #packed {

+ 1 - 0
tests/core/image/test_core_image.odin

@@ -7,6 +7,7 @@
 
 	A test suite for PNG, TGA, NetPBM, QOI and BMP.
 */
+#+feature dynamic-literals
 package test_core_image
 
 import "core:testing"

+ 1 - 0
tests/core/net/test_core_net.odin

@@ -12,6 +12,7 @@
 */
 #+build !netbsd
 #+build !openbsd
+#+feature dynamic-literals
 package test_core_net
 
 import "core:testing"

+ 99 - 1
tests/core/runtime/test_core_runtime.odin

@@ -1,3 +1,4 @@
+#+feature dynamic-literals
 package test_core_runtime
 
 import "base:intrinsics"
@@ -63,4 +64,101 @@ test_init_cap_map_dynarray :: proc(t: ^testing.T) {
         defer delete(d2)
         testing.expect(t, cap(d2) == 0)
         testing.expect(t, d2.allocator.procedure == ally.procedure)
-}
+}
+
+@(test)
+test_map_get :: proc(t: ^testing.T) {
+	check :: proc(t: ^testing.T, m: map[$K]$V, loc := #caller_location) {
+		for k, v in m {
+			got_key, got_val, ok := runtime.map_get(m, k)
+			testing.expect_value(t, got_key, k, loc = loc)
+			testing.expect_value(t, got_val, v, loc = loc)
+			testing.expect(t, ok, loc = loc)
+		}
+	}
+
+	// small keys & values
+	{
+		m := map[int]int{
+			1 = 10,
+			2 = 20,
+			3 = 30,
+		}
+		defer delete(m)
+		check(t, m)
+	}
+
+	// small keys; 2 values per cell
+	{
+		m := map[int][3]int{
+			1 = [3]int{10, 100, 1000},
+			2 = [3]int{20, 200, 2000},
+			3 = [3]int{30, 300, 3000},
+		}
+		defer delete(m)
+		check(t, m)
+	}
+
+	// 2 keys per cell; small values
+	{
+		m := map[[3]int]int{
+			[3]int{10, 100, 1000} = 1,
+			[3]int{20, 200, 2000} = 2,
+			[3]int{30, 300, 3000} = 3,
+		}
+		defer delete(m)
+		check(t, m)
+	}
+
+
+	// small keys; 3 values per cell
+	{
+		val :: struct #packed {
+			a, b: int,
+			c:    i32,
+		}
+		m := map[int]val{
+			1 = val{10, 100, 1000},
+			2 = val{20, 200, 2000},
+			3 = val{30, 300, 3000},
+		}
+		defer delete(m)
+		check(t, m)
+	}
+
+	// 3 keys per cell; small values
+	{
+		key :: struct #packed {
+			a, b: int,
+			c:    i32,
+		}
+		m := map[key]int{
+			key{10, 100, 1000} = 1,
+			key{20, 200, 2000} = 2,
+			key{30, 300, 3000} = 3,
+		}
+		defer delete(m)
+		check(t, m)
+	}
+
+	// small keys; value bigger than a chacheline
+	{
+		m := map[int][9]int{
+			1 = [9]int{10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000},
+			2 = [9]int{20, 200, 2000, 20000, 200000, 2000000, 20000000, 200000000, 2000000000},
+			3 = [9]int{30, 300, 3000, 30000, 300000, 3000000, 30000000, 300000000, 3000000000},
+		}
+		defer delete(m)
+		check(t, m)
+	}
+	// keys bigger than a chacheline; small values
+	{
+		m := map[[9]int]int{
+			[9]int{10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000} = 1,
+			[9]int{20, 200, 2000, 20000, 200000, 2000000, 20000000, 200000000, 2000000000} = 2,
+			[9]int{30, 300, 3000, 30000, 300000, 3000000, 30000000, 300000000, 3000000000} = 3,
+		}
+		defer delete(m)
+		check(t, m)
+	}
+}

+ 1 - 0
tests/core/slice/test_core_slice.odin

@@ -1,3 +1,4 @@
+#+feature dynamic-literals
 package test_core_slice
 
 import "core:slice"

+ 1 - 0
tests/issues/run.bat

@@ -16,6 +16,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style
 ..\..\..\odin test ..\test_issue_2637.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_2666.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_4210.odin %COMMON%  || exit /b
+..\..\..\odin test ..\test_issue_4584.odin %COMMON%  || exit /b
 
 @echo off
 

+ 1 - 0
tests/issues/run.sh

@@ -17,6 +17,7 @@ $ODIN test ../test_issue_2615.odin $COMMON
 $ODIN test ../test_issue_2637.odin $COMMON
 $ODIN test ../test_issue_2666.odin $COMMON
 $ODIN test ../test_issue_4210.odin $COMMON
+$ODIN test ../test_issue_4584.odin $COMMON
 if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then
 	echo "SUCCESSFUL 1/1"
 else

+ 198 - 0
tests/issues/test_issue_4584.odin

@@ -0,0 +1,198 @@
+// Tests issue #4584 https://github.com/odin-lang/Odin/issues/4584
+package test_issues
+
+import "core:testing"
+import "core:log"
+import "core:math/linalg"
+import glm "core:math/linalg/glsl"
+import hlm "core:math/linalg/hlsl"
+
+@test
+test_adjugate_2x2 :: proc(t: ^testing.T) {
+	I := linalg.identity(matrix[2,2]int)
+	m := matrix[2,2]int {
+		-3, 2,
+		-1, 0,
+	}
+	expected := matrix[2,2]int {
+		 0, -2,
+		 1, -3,
+	}
+	testing.expect_value(t, linalg.adjugate(m), expected)
+	testing.expect_value(t, linalg.determinant(m), 2)
+	testing.expect_value(t, linalg.adjugate(m) * m, 2 * I)
+	testing.expect_value(t, m * linalg.adjugate(m), 2 * I)
+
+	testing.expect_value(t, glm.adjugate(m), expected)
+	testing.expect_value(t, glm.determinant(m), 2)
+	testing.expect_value(t, glm.adjugate(m) * m, 2 * I)
+	testing.expect_value(t, m * glm.adjugate(m), 2 * I)
+
+	testing.expect_value(t, hlm.adjugate(m), expected)
+	testing.expect_value(t, hlm.determinant(m), 2)
+	testing.expect_value(t, hlm.adjugate(m) * m, 2 * I)
+	testing.expect_value(t, m * hlm.adjugate(m), 2 * I)
+}
+
+@test
+test_adjugate_3x3 :: proc(t: ^testing.T) {
+	I := linalg.identity(matrix[3,3]int)
+	m := matrix[3,3]int {
+		-3,  2, -5,
+		-1,  0, -2,
+		 3, -4,  1,
+	}
+	expected := matrix[3,3]int {
+		-8, 18, -4,
+		-5, 12, -1,
+		 4, -6,  2,
+	}
+	testing.expect_value(t, linalg.adjugate(m), expected)
+	testing.expect_value(t, linalg.determinant(m), -6)
+	testing.expect_value(t, linalg.adjugate(m) * m, -6 * I)
+	testing.expect_value(t, m * linalg.adjugate(m), -6 * I)
+
+	testing.expect_value(t, glm.adjugate(m), expected)
+	testing.expect_value(t, glm.determinant(m), -6)
+	testing.expect_value(t, glm.adjugate(m) * m, -6 * I)
+	testing.expect_value(t, m * glm.adjugate(m), -6 * I)
+
+	testing.expect_value(t, hlm.adjugate(m), expected)
+	testing.expect_value(t, hlm.determinant(m), -6)
+	testing.expect_value(t, hlm.adjugate(m) * m, -6 * I)
+	testing.expect_value(t, m * hlm.adjugate(m), -6 * I)
+}
+
+@test
+test_adjugate_4x4 :: proc(t: ^testing.T) {
+	I := linalg.identity(matrix[4,4]int)
+	m := matrix[4,4]int {
+		-3,  2, -5, 1,
+		-1,  0, -2, 2,
+		 3, -4,  1, 3,
+		 4,  5,  6, 7,
+	}
+	expected := matrix[4,4]int {
+		-144,  266, -92, -16,
+		 -57,   92,  -5, -16,
+		 105, -142,  55,   2,
+		  33,  -96,   9,  -6,
+	}
+	testing.expect_value(t, linalg.adjugate(m), expected)
+	testing.expect_value(t, linalg.determinant(m), -174)
+	testing.expect_value(t, linalg.adjugate(m) * m, -174 * I)
+	testing.expect_value(t, m * linalg.adjugate(m), -174 * I)
+
+	testing.expect_value(t, glm.adjugate(m), expected)
+	testing.expect_value(t, glm.determinant(m), -174)
+	testing.expect_value(t, glm.adjugate(m) * m, -174 * I)
+	testing.expect_value(t, m * glm.adjugate(m), -174 * I)
+
+	testing.expect_value(t, hlm.adjugate(m), expected)
+	testing.expect_value(t, hlm.determinant(m), -174)
+	testing.expect_value(t, hlm.adjugate(m) * m, -174 * I)
+	testing.expect_value(t, m * hlm.adjugate(m), -174 * I)
+}
+
+@test
+test_inverse_regression_2x2 :: proc(t: ^testing.T) {
+	I := linalg.identity(matrix[2,2]f32)
+	m := matrix[2,2]f32 {
+		-3, 2,
+		-1, 0,
+	}
+	expected := matrix[2,2]f32 {
+		    0.0,     -1.0,
+		1.0/2.0, -3.0/2.0,
+	}
+	expect_float_matrix_value(t, linalg.inverse(m), expected)
+	expect_float_matrix_value(t, linalg.inverse_transpose(m), linalg.transpose(expected))
+	expect_float_matrix_value(t, linalg.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * linalg.inverse(m), I)
+
+	expect_float_matrix_value(t, glm.inverse(m), expected)
+	expect_float_matrix_value(t, glm.inverse_transpose(m), glm.transpose(expected))
+	expect_float_matrix_value(t, glm.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * glm.inverse(m), I)
+
+	expect_float_matrix_value(t, hlm.inverse(m), expected)
+	expect_float_matrix_value(t, hlm.inverse_transpose(m), hlm.transpose(expected))
+	expect_float_matrix_value(t, hlm.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * hlm.inverse(m), I)
+}
+
+@test
+test_inverse_regression_3x3 :: proc(t: ^testing.T) {
+	I := linalg.identity(matrix[3,3]f32)
+	m := matrix[3,3]f32 {
+		-3,  2, -5,
+		-1,  0, -2,
+		 3, -4,  1,
+	}
+	expected := matrix[3,3]f32 {
+		 4.0/3.0, -3.0,  2.0/3.0,
+		 5.0/6.0, -2.0,  1.0/6.0,
+		-2.0/3.0,  1.0, -1.0/3.0,
+	}
+	expect_float_matrix_value(t, linalg.inverse(m), expected)
+	expect_float_matrix_value(t, linalg.inverse_transpose(m), linalg.transpose(expected))
+	expect_float_matrix_value(t, linalg.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * linalg.inverse(m), I)
+
+	expect_float_matrix_value(t, glm.inverse(m), expected)
+	expect_float_matrix_value(t, glm.inverse_transpose(m), glm.transpose(expected))
+	expect_float_matrix_value(t, glm.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * glm.inverse(m), I)
+
+	expect_float_matrix_value(t, hlm.inverse(m), expected)
+	expect_float_matrix_value(t, hlm.inverse_transpose(m), hlm.transpose(expected))
+	expect_float_matrix_value(t, hlm.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * hlm.inverse(m), I)
+}
+
+@test
+test_inverse_regression_4x4 :: proc(t: ^testing.T) {
+	I := linalg.identity(matrix[4,4]f32)
+	m := matrix[4,4]f32 {
+		-3,  2, -5, 1,
+		-1,  0, -2, 2,
+		 3, -4,  1, 3,
+		 4,  5,  6, 7,
+	}
+	expected := matrix[4,4]f32 {
+		 24.0/29.0, -133.0/87.0,   46.0/87.0,  8.0/87.0,
+		 19.0/58.0,  -46.0/87.0,   5.0/174.0,  8.0/87.0,
+		-35.0/58.0,   71.0/87.0, -55.0/174.0, -1.0/87.0,
+		-11.0/58.0,   16.0/29.0,   -3.0/58.0,  1.0/29.0,
+	}
+	expect_float_matrix_value(t, linalg.inverse(m), expected)
+	expect_float_matrix_value(t, linalg.inverse_transpose(m), linalg.transpose(expected))
+	expect_float_matrix_value(t, linalg.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * linalg.inverse(m), I)
+
+	expect_float_matrix_value(t, glm.inverse(m), expected)
+	expect_float_matrix_value(t, glm.inverse_transpose(m), glm.transpose(expected))
+	expect_float_matrix_value(t, glm.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * glm.inverse(m), I)
+
+	expect_float_matrix_value(t, hlm.inverse(m), expected)
+	expect_float_matrix_value(t, hlm.inverse_transpose(m), hlm.transpose(expected))
+	expect_float_matrix_value(t, hlm.inverse(m) * m, I)
+	expect_float_matrix_value(t, m * hlm.inverse(m), I)
+}
+
+@(private="file")
+expect_float_matrix_value :: proc(t: ^testing.T, value, expected: $M/matrix[$N, N]f32, loc := #caller_location, value_expr := #caller_expression(value)) -> bool {
+	ok := true
+	outer: for i in 0..<N {
+		for j in 0..<N {
+			diff := abs(value[i, j] - expected[i, j])
+			if diff > 1e-6 {
+				ok = false
+				break outer
+			}
+		}
+	}
+	if !ok do log.errorf("expected %v to be %v, got %v", value_expr, expected, value, location=loc)
+	return ok
+}

+ 1 - 0
tests/issues/test_issue_829.odin

@@ -1,4 +1,5 @@
 // Tests issue #829 https://github.com/odin-lang/Odin/issues/829
+#+feature dynamic-literals
 package test_issues
 
 import "core:testing"

+ 3 - 11
vendor/box2d/box2d.odin

@@ -24,19 +24,11 @@ when ODIN_OS == .Windows {
 }
 
 when !#exists(LIB_PATH) {
-	#panic("Could not find the compiled box2d libraries at \"" + LIB_PATH + "\", they can be compiled by running the `build.sh` script at `" + ODIN_ROOT + "vendor/box2d/build_box2d.sh\"`")
+	#panic("Could not find the compiled box2d libraries at \"" + LIB_PATH + "\", they can be compiled by running the `build_box2d.sh` script at `" + ODIN_ROOT + "vendor/box2d/build_box2d.sh\"`")
 }
 
-when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
-	when VECTOR_EXT == "_simd" {
-		foreign import lib "lib/box2d_wasm_simd.o"
-	} else {
-		foreign import lib "lib/box2d_wasm.o"
-	}
-} else {
-	foreign import lib {
-		LIB_PATH,
-	}
+foreign import lib {
+	LIB_PATH,
 }
 
 

+ 5 - 0
vendor/box2d/build_box2d.sh

@@ -68,7 +68,12 @@ esac
 
 cd ..
 
+set +e
 make -f wasm.Makefile
+if [[ $? -ne 0 ]]; then
+	printf "\e[30;43mwarning:\e[0m Native Box2D libraries were built successfully, the WASM build failed, likely because your default C compiler and/or linker doesn't support WASM, you can set the CC and LD environment variables to point to a compiler and linker that support it\n"
+fi
+set -e
 
 rm -rf v3.0.0.tar.gz
 rm -rf box2d-3.0.0

+ 3 - 1
vendor/box2d/wasm.Makefile

@@ -2,6 +2,8 @@
 # I tried to make a cmake toolchain file for this / use cmake but this is far easier.
 # NOTE: We are pretending to be emscripten to box2d so it takes WASM code paths, but we don't actually use emscripten.
 
+# WARN: wasm is probably not supported by your default C compiler and linker, overwrite the CC and LD environment variables accordingly.
+# Example for MacOS:
 # CC = $(shell brew --prefix llvm)/bin/clang
 # LD = $(shell brew --prefix llvm)/bin/wasm-ld
 
@@ -10,7 +12,7 @@ SRCS      = $(wildcard box2d-$(VERSION)/src/*.c)
 OBJS_SIMD = $(SRCS:.c=_simd.o)
 OBJS      = $(SRCS:.c=.o)
 SYSROOT   = $(shell odin root)/vendor/libc
-CFLAGS    = -Ibox2d-$(VERSION)/include -Ibox2d-$(VERSION)/Extern/simde --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT)
+CFLAGS    = -Ibox2d-$(VERSION)/include -Ibox2d-$(VERSION)/extern/simde --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT)
 
 all: lib/box2d_wasm.o lib/box2d_wasm_simd.o clean
 

+ 58 - 13
vendor/cgltf/cgltf.odin

@@ -105,6 +105,7 @@ type :: enum c.int {
 }
 
 primitive_type :: enum c.int {
+	invalid,
 	points,
 	lines,
 	line_loop,
@@ -222,15 +223,6 @@ accessor_sparse :: struct {
 	indices_component_type:   component_type,
 	values_buffer_view:       ^buffer_view,
 	values_byte_offset:       uint,
-	extras:                   extras_t,
-	indices_extras:           extras_t,
-	values_extras:            extras_t,
-	extensions_count:         uint,
-	extensions:               [^]extension `fmt:"v,extensions_count"`,
-	indices_extensions_count: uint,
-	indices_extensions:       [^]extension `fmt:"v,indices_extensions_count"`,
-	values_extensions_count:  uint,
-	values_extensions:        [^]extension `fmt:"v,values_extensions_count"`,
 }
 
 accessor :: struct {
@@ -306,9 +298,6 @@ texture_view :: struct {
 	scale:            f32, /* equivalent to strength for occlusion_texture */
 	has_transform:    b32,
 	transform:        texture_transform,
-	extras:           extras_t,
-	extensions_count: uint,
-	extensions:       [^]extension `fmt:"v,extensions_count"`,
 }
 
 pbr_metallic_roughness :: struct {
@@ -381,6 +370,16 @@ iridescence :: struct {
 	iridescence_thickness_texture: texture_view,
 }
 
+anisotropy :: struct {
+	anisotropy_strength: f32,
+	anisotropy_rotation: f32,
+	anisotropy_texture:  texture_view,
+}
+
+dispersion :: struct {
+	dispersion: f32,
+}
+
 material :: struct {
 	name: cstring,
 	has_pbr_metallic_roughness:  b32,
@@ -393,6 +392,8 @@ material :: struct {
 	has_sheen:                   b32,
 	has_emissive_strength:       b32,
 	has_iridescence:             b32,
+	has_anisotropy:              b32,
+	has_dispersion:              b32,
 	pbr_metallic_roughness:      pbr_metallic_roughness,
 	pbr_specular_glossiness:     pbr_specular_glossiness,
 	clearcoat:                   clearcoat,
@@ -403,6 +404,8 @@ material :: struct {
 	volume:                      volume,
 	emissive_strength:           emissive_strength,
 	iridescence:                 iridescence,
+	anisotropy:                  anisotropy,
+	dispersion:                  dispersion,
 	normal_texture:              texture_view,
 	occlusion_texture:           texture_view,
 	emissive_texture:            texture_view,
@@ -432,7 +435,6 @@ draco_mesh_compression :: struct {
 }
 
 mesh_gpu_instancing :: struct {
-	buffer_view: ^buffer_view,
 	attributes:  []attribute,
 }
 
@@ -683,6 +685,9 @@ foreign lib {
 	node_transform_local :: proc(node: ^node, out_matrix: [^]f32) ---
 	node_transform_world :: proc(node: ^node, out_matrix: [^]f32) ---
 
+	@(require_results)
+	buffer_view_data :: proc(view: ^/*const*/buffer_view) -> [^]byte ---
+
 	@(require_results)
 	accessor_read_float :: proc(accessor: ^/*const*/accessor, index: uint, out: [^]f32,    element_size: uint) -> b32 ---
 	@(require_results)
@@ -693,13 +698,53 @@ foreign lib {
 	@(require_results)
 	num_components :: proc(type: type) -> uint ---
 
+	@(require_results)
+	component_size :: proc(component_type: component_type) -> uint ---
+	@(require_results)
+	calc_size :: proc(type: type, component_type: component_type) -> uint ---
+
 	@(require_results)
 	accessor_unpack_floats :: proc(accessor: ^/*const*/accessor, out: [^]f32, float_count: uint) -> uint ---
+	@(require_results)
+	accessor_unpack_indices :: proc(accessor: ^/*const*/accessor , out: rawptr, out_component_size: uint, index_count: uint) -> uint ---
 
 	/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
 	@(require_results)
 	copy_extras_json :: proc(data: ^data, extras: ^extras_t, dest: [^]byte, dest_size: ^uint) -> result ---
 
+	@(require_results)
+	mesh_index        :: proc(data: ^/*const*/data, object: ^/*const*/mesh) -> uint ---
+	@(require_results)
+	material_index    :: proc(data: ^/*const*/data, object: ^/*const*/material) -> uint ---
+	@(require_results)
+	accessor_index    :: proc(data: ^/*const*/data, object: ^/*const*/accessor) -> uint ---
+	@(require_results)
+	buffer_view_index :: proc(data: ^/*const*/data, object: ^/*const*/buffer_view) -> uint ---
+	@(require_results)
+	buffer_index      :: proc(data: ^/*const*/data, object: ^/*const*/buffer) -> uint ---
+	@(require_results)
+	image_index       :: proc(data: ^/*const*/data, object: ^/*const*/image) -> uint ---
+	@(require_results)
+	texture_index     :: proc(data: ^/*const*/data, object: ^/*const*/texture) -> uint ---
+	@(require_results)
+	sampler_index     :: proc(data: ^/*const*/data, object: ^/*const*/sampler) -> uint ---
+	@(require_results)
+	skin_index        :: proc(data: ^/*const*/data, object: ^/*const*/skin) -> uint ---
+	@(require_results)
+	camera_index      :: proc(data: ^/*const*/data, object: ^/*const*/camera) -> uint ---
+	@(require_results)
+	light_index       :: proc(data: ^/*const*/data, object: ^/*const*/light) -> uint ---
+	@(require_results)
+	node_index        :: proc(data: ^/*const*/data, object: ^/*const*/node) -> uint ---
+	@(require_results)
+	scene_index       :: proc(data: ^/*const*/data, object: ^/*const*/scene) -> uint ---
+	@(require_results)
+	animation_index   :: proc(data: ^/*const*/data, object: ^/*const*/animation) -> uint ---
+	@(require_results)
+	animation_sampler_index :: proc(animation: ^/*const*/animation, object: ^/*const*/animation_sampler) -> uint ---
+	@(require_results)
+	animation_channel_index :: proc(animation: ^/*const*/animation, object: ^/*const*/animation_channel) -> uint ---
+	
 	@(require_results)
 	write_file :: proc(#by_ptr options: options, path:   cstring,             data: ^data) -> result ---
 	@(require_results)

BIN
vendor/cgltf/lib/cgltf.lib


BIN
vendor/cgltf/lib/cgltf_wasm.o


File diff suppressed because it is too large
+ 338 - 200
vendor/cgltf/src/cgltf.h


+ 55 - 12
vendor/cgltf/src/cgltf_write.h

@@ -1,7 +1,7 @@
 /**
  * cgltf_write - a single-file glTF 2.0 writer written in C99.
  *
- * Version: 1.13
+ * Version: 1.14
  *
  * Website: https://github.com/jkuhlmann/cgltf
  *
@@ -85,6 +85,8 @@ cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size si
 #define CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH (1 << 13)
 #define CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING (1 << 14)
 #define CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE (1 << 15)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY (1 << 16)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION (1 << 17)
 
 typedef struct {
 	char* buffer;
@@ -152,7 +154,6 @@ typedef struct {
 			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
 			cgltf_write_texture_transform(context, &info.transform); \
 		} \
-		cgltf_write_extras(context, &info.extras); \
 		cgltf_write_line(context, "}"); }
 
 #define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \
@@ -164,7 +165,6 @@ typedef struct {
 			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
 			cgltf_write_texture_transform(context, &info.transform); \
 		} \
-		cgltf_write_extras(context, &info.extras); \
 		cgltf_write_line(context, "}"); }
 
 #define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \
@@ -176,12 +176,11 @@ typedef struct {
 			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
 			cgltf_write_texture_transform(context, &info.transform); \
 		} \
-		cgltf_write_extras(context, &info.extras); \
 		cgltf_write_line(context, "}"); }
 
 #ifndef CGLTF_CONSTS
-static const cgltf_size GlbHeaderSize = 12;
-static const cgltf_size GlbChunkHeaderSize = 8;
+#define GlbHeaderSize 12
+#define GlbChunkHeaderSize 8
 static const uint32_t GlbVersion = 2;
 static const uint32_t GlbMagic = 0x46546C67;
 static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
@@ -360,6 +359,21 @@ static int cgltf_int_from_component_type(cgltf_component_type ctype)
 	}
 }
 
+static int cgltf_int_from_primitive_type(cgltf_primitive_type ctype)
+{
+	switch (ctype)
+	{
+		case cgltf_primitive_type_points: return 0;
+		case cgltf_primitive_type_lines: return 1;
+		case cgltf_primitive_type_line_loop: return 2;
+		case cgltf_primitive_type_line_strip: return 3;
+		case cgltf_primitive_type_triangles: return 4;
+		case cgltf_primitive_type_triangle_strip: return 5;
+		case cgltf_primitive_type_triangle_fan: return 6;
+		default: return -1;
+	}
+}
+
 static const char* cgltf_str_from_alpha_mode(cgltf_alpha_mode alpha_mode)
 {
 	switch (alpha_mode)
@@ -455,7 +469,7 @@ static void cgltf_write_asset(cgltf_write_context* context, const cgltf_asset* a
 
 static void cgltf_write_primitive(cgltf_write_context* context, const cgltf_primitive* prim)
 {
-	cgltf_write_intprop(context, "mode", (int) prim->type, 4);
+	cgltf_write_intprop(context, "mode", cgltf_int_from_primitive_type(prim->type), 4);
 	CGLTF_WRITE_IDXPROP("indices", prim->indices, context->data->accessors);
 	CGLTF_WRITE_IDXPROP("material", prim->material, context->data->materials);
 	cgltf_write_line(context, "\"attributes\": {");
@@ -641,6 +655,16 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
 		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE;
 	}
 
+	if (material->has_anisotropy)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY;
+	}
+
+	if (material->has_dispersion)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION;
+	}
+
 	if (material->has_pbr_metallic_roughness)
 	{
 		const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness;
@@ -656,7 +680,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
 		cgltf_write_line(context, "}");
 	}
 
-	if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength || material->has_iridescence)
+	if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength || material->has_iridescence || material->has_anisotropy || material->has_dispersion)
 	{
 		cgltf_write_line(context, "\"extensions\": {");
 		if (material->has_clearcoat)
@@ -767,6 +791,22 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
 			CGLTF_WRITE_TEXTURE_INFO("iridescenceThicknessTexture", params->iridescence_thickness_texture);
 			cgltf_write_line(context, "}");
 		}
+		if (material->has_anisotropy)
+		{
+			cgltf_write_line(context, "\"KHR_materials_anisotropy\": {");
+			const cgltf_anisotropy* params = &material->anisotropy;
+			cgltf_write_floatprop(context, "anisotropyFactor", params->anisotropy_strength, 0.f);
+			cgltf_write_floatprop(context, "anisotropyRotation", params->anisotropy_rotation, 0.f);
+			CGLTF_WRITE_TEXTURE_INFO("anisotropyTexture", params->anisotropy_texture);
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_dispersion)
+		{
+			cgltf_write_line(context, "\"KHR_materials_dispersion\": {");
+			const cgltf_dispersion* params = &material->dispersion;
+			cgltf_write_floatprop(context, "dispersion", params->dispersion, 0.f);
+			cgltf_write_line(context, "}");
+		}
 		cgltf_write_line(context, "}");
 	}
 
@@ -977,7 +1017,6 @@ static void cgltf_write_node(cgltf_write_context* context, const cgltf_node* nod
 
 		cgltf_write_line(context, "\"EXT_mesh_gpu_instancing\": {");
 		{
-			CGLTF_WRITE_IDXPROP("bufferView", node->mesh_gpu_instancing.buffer_view, context->data->buffer_views);
 			cgltf_write_line(context, "\"attributes\": {");
 			{
 				for (cgltf_size i = 0; i < node->mesh_gpu_instancing.attributes_count; ++i)
@@ -1044,14 +1083,11 @@ static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_acces
 		cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0);
 		CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views);
 		cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0);
-		cgltf_write_extras(context, &accessor->sparse.indices_extras);
 		cgltf_write_line(context, "}");
 		cgltf_write_line(context, "\"values\": {");
 		cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0);
 		CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views);
-		cgltf_write_extras(context, &accessor->sparse.values_extras);
 		cgltf_write_line(context, "}");
-		cgltf_write_extras(context, &accessor->sparse.extras);
 		cgltf_write_line(context, "}");
 	}
 	cgltf_write_extras(context, &accessor->extras);
@@ -1123,6 +1159,7 @@ static void cgltf_write_light(cgltf_write_context* context, const cgltf_light* l
 		cgltf_write_floatprop(context, "outerConeAngle", light->spot_outer_cone_angle, 3.14159265358979323846f/4.0f);
 		cgltf_write_line(context, "}");
 	}
+	cgltf_write_extras( context, &light->extras );
 	cgltf_write_line(context, "}");
 }
 
@@ -1249,9 +1286,15 @@ static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extens
 	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE) {
 		cgltf_write_stritem(context, "KHR_materials_iridescence");
 	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY) {
+		cgltf_write_stritem(context, "KHR_materials_anisotropy");
+	}
 	if (extension_flags & CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING) {
 		cgltf_write_stritem(context, "EXT_mesh_gpu_instancing");
 	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION) {
+		cgltf_write_stritem(context, "KHR_materials_dispersion");
+	}
 }
 
 cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data)

+ 2 - 2
vendor/libc/include/math.h

@@ -17,5 +17,5 @@ double fabs(double x);
 int abs(int);
 double ldexp(double, int);
 double exp(double);
-float log(float);
-float sin(float);
+double log(double);
+double sin(double);

+ 2 - 2
vendor/libc/math.odin

@@ -90,11 +90,11 @@ exp :: proc "c" (x: f64) -> f64 {
 }
 
 @(require, linkage="strong", link_name="log")
-log :: proc "c" (x: f32) -> f32 {
+log :: proc "c" (x: f64) -> f64 {
 	return math.ln(x)
 }
 
 @(require, linkage="strong", link_name="sin")
-sin :: proc "c" (x: f32) -> f32 {
+sin :: proc "c" (x: f64) -> f64 {
 	return math.sin(x)
 }

+ 1 - 0
vendor/libc/stdio.odin

@@ -1,3 +1,4 @@
+#+build !freestanding
 package odin_libc
 
 import "core:c"

+ 5 - 0
vendor/raylib/raygui.odin

@@ -3,6 +3,7 @@ package raylib
 import "core:c"
 
 RAYGUI_SHARED :: #config(RAYGUI_SHARED, false)
+RAYGUI_WASM_LIB :: #config(RAYGUI_WASM_LIB, "wasm/libraygui.a")
 
 when ODIN_OS == .Windows {
 	foreign import lib {
@@ -22,6 +23,10 @@ when ODIN_OS == .Windows {
 			"macos/libraygui.dylib" when RAYGUI_SHARED else "macos/libraygui.a",
 		}
 	}
+} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
+	foreign import lib {
+		RAYGUI_WASM_LIB,
+	}
 } else {
 	foreign import lib "system:raygui"
 }

+ 5 - 0
vendor/raylib/raylib.odin

@@ -100,6 +100,7 @@ MAX_TEXT_BUFFER_LENGTH :: #config(RAYLIB_MAX_TEXT_BUFFER_LENGTH, 1024)
 #assert(size_of(rune) == size_of(c.int))
 
 RAYLIB_SHARED :: #config(RAYLIB_SHARED, false)
+RAYLIB_WASM_LIB :: #config(RAYLIB_WASM_LIB, "wasm/libraylib.a")
 
 when ODIN_OS == .Windows {
 	@(extra_linker_flags="/NODEFAULTLIB:" + ("msvcrt" when RAYLIB_SHARED else "libcmt"))
@@ -127,6 +128,10 @@ when ODIN_OS == .Windows {
 		"system:OpenGL.framework",
 		"system:IOKit.framework",
 	} 
+} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
+	foreign import lib {
+		RAYLIB_WASM_LIB,
+	}
 } else {
 	foreign import lib "system:raylib"
 }

+ 1 - 1
vendor/raylib/raymath.odin

@@ -523,7 +523,7 @@ Vector3Unproject :: proc "c" (source: Vector3, projection: Matrix, view: Matrix)
 
 	quat: Quaternion
 	quat.x = source.x
-	quat.y = source.z
+	quat.y = source.y
 	quat.z = source.z
 	quat.w = 1
 

+ 6 - 1
vendor/raylib/rlgl/rlgl.odin

@@ -113,6 +113,7 @@ import rl "../."
 VERSION :: "5.0"
 
 RAYLIB_SHARED :: #config(RAYLIB_SHARED, false)
+RAYLIB_WASM_LIB :: #config(RAYLIB_WASM_LIB, "../wasm/libraylib.a")
 
 // Note: We pull in the full raylib library. If you want a truly stand-alone rlgl, then:
 // - Compile a separate rlgl library and use that in the foreign import blocks below.
@@ -145,6 +146,10 @@ when ODIN_OS == .Windows {
 		"system:OpenGL.framework",
 		"system:IOKit.framework",
 	} 
+} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
+	foreign import lib {
+		RAYLIB_WASM_LIB,
+	}
 } else {
 	foreign import lib "system:raylib"
 }
@@ -509,7 +514,7 @@ foreign lib {
 	UpdateVertexBufferElements       :: proc(id: c.uint, data: rawptr, dataSize: c.int, offset: c.int) ---        // Update vertex buffer elements with new data
 	UnloadVertexArray                :: proc(vaoId: c.uint) ---
 	UnloadVertexBuffer               :: proc(vboId: c.uint) ---
-	SetVertexAttribute               :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: rawptr) ---
+	SetVertexAttribute               :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, offset: c.int) ---
 	SetVertexAttributeDivisor        :: proc(index: c.uint, divisor: c.int) ---
 	SetVertexAttributeDefault        :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value
 	DrawVertexArray                  :: proc(offset: c.int, count: c.int) ---

BIN
vendor/raylib/wasm/libraygui.a


BIN
vendor/raylib/wasm/libraylib.a


+ 34 - 12
vendor/vulkan/_gen/create_vulkan_odin_wrapper.py

@@ -16,6 +16,8 @@ file_and_urls = [
     ("vulkan_macos.h",   'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_macos.h',   False),
     ("vulkan_ios.h",     'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_ios.h',     False),
     ("vulkan_wayland.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_wayland.h', False),
+    ("vulkan_xlib.h",    'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_xlib.h',    False),
+    ("vulkan_xcb.h",     'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_xcb.h',     False),
     # Vulkan Video
     ("vulkan_video_codec_av1std.h",         'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vk_video/vulkan_video_codec_av1std.h', False),
     ("vulkan_video_codec_av1std_decode.h",  'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vk_video/vulkan_video_codec_av1std_decode.h', False),
@@ -46,17 +48,18 @@ def no_vk(t):
     t = t.replace('PFN_', 'Proc')
     t = t.replace('PFN_', 'Proc')
 
-    t = re.sub('(?:Vk|VK_)?(\w+)', '\\1', t)
+    t = re.sub('(?:Vk|VK_)?(\\w+)', '\\1', t)
 
     # Vulkan Video
-    t = re.sub('(?:Std|STD_|VK_STD)?(\w+)', '\\1', t)
+    t = re.sub('(?:Std|STD_|VK_STD)?(\\w+)', '\\1', t)
     return t
 
 OPAQUE_STRUCTS = """
-wl_surface   :: struct {} // Opaque struct defined by Wayland
-wl_display   :: struct {} // Opaque struct defined by Wayland
-IOSurfaceRef :: struct {} // Opaque struct defined by Apple’s CoreGraphics framework
-""" 
+wl_surface       :: struct {} // Opaque struct defined by Wayland
+wl_display       :: struct {} // Opaque struct defined by Wayland
+xcb_connection_t :: struct {} // Opaque struct defined by xcb
+IOSurfaceRef     :: struct {} // Opaque struct defined by Apple’s CoreGraphics framework
+"""
 
 def convert_type(t, prev_name, curr_name):
     table = {
@@ -91,6 +94,9 @@ def convert_type(t, prev_name, curr_name):
         "struct BaseInStructure":  "BaseInStructure",
         "struct wl_display": "wl_display",
         "struct wl_surface": "wl_surface",
+        "Display": "XlibDisplay",
+        "Window": "XlibWindow",
+        "VisualID": "XlibVisualID",
         'v': '',
     }
 
@@ -106,7 +112,7 @@ def convert_type(t, prev_name, curr_name):
     elif t.endswith("*"):
         pointer = "^"
         ttype = t[:len(t)-1]
-        elem = convert_type(ttype, prev_name, curr_name)    
+        elem = convert_type(ttype, prev_name, curr_name)
 
         if curr_name.endswith("s") or curr_name.endswith("Table"):
             if prev_name.endswith("Count") or prev_name.endswith("Counts"):
@@ -445,7 +451,7 @@ def parse_enums(f):
 
 def parse_fake_enums(f):
     data = re.findall(r"static const Vk(\w+FlagBits2) VK_(\w+?) = (\w+);", src, re.S)
-    
+
     data.sort(key=lambda x: x[0])
 
     fake_enums = {}
@@ -507,7 +513,7 @@ def parse_fake_enums(f):
                 continue
 
             ff.append((n, v))
-        
+
         max_flag_value = max([int(v) for n, v in ff if is_int(v)] + [0])
         max_group_value = max([int(v) for n, v in groups if is_int(v)] + [0])
         if max_flag_value < max_group_value:
@@ -575,13 +581,13 @@ def parse_structs(f):
                     ffields.append(tuple([bit_field_name, bit_field_type, comment]))
                     prev_name = ""
                     continue
-                
+
                 # The second way has many fields that are each 1 bit
                 elif int(fname) == 1:
                     bit_field_type = do_type(bit_field[0], prev_name, fname)
                     ffields.append(tuple(["bitfield", bit_field_type, comment]))
                     break
-                    
+
 
 
             if '[' in fname:
@@ -894,6 +900,10 @@ import "core:c"
 
 import win32 "core:sys/windows"
 _ :: win32
+
+import "vendor:x11/xlib"
+_ :: xlib
+
 when ODIN_OS == .Windows {
 \tHINSTANCE           :: win32.HINSTANCE
 \tHWND                :: win32.HWND
@@ -919,7 +929,19 @@ when ODIN_OS == .Windows {
 \t}
 }
 
-CAMetalLayer :: struct {}
+when xlib.IS_SUPPORTED {
+\tXlibDisplay  :: xlib.Display
+\tXlibWindow   :: xlib.Window
+\tXlibVisualID :: xlib.VisualID
+} else {
+\tXlibDisplay  :: struct {} // Opaque struct defined by Xlib
+\tXlibWindow   :: c.ulong
+\tXlibVisualID :: c.ulong
+}
+
+xcb_visualid_t :: u32
+xcb_window_t   :: u32
+CAMetalLayer   :: struct {}
 
 MTLBuffer_id       :: rawptr
 MTLTexture_id      :: rawptr

+ 56 - 0
vendor/vulkan/_gen/vulkan_xcb.h

@@ -0,0 +1,56 @@
+#ifndef VULKAN_XCB_H_
+#define VULKAN_XCB_H_ 1
+
+/*
+** Copyright 2015-2024 The Khronos Group Inc.
+**
+** SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+** This header is generated from the Khronos Vulkan XML API Registry.
+**
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+// VK_KHR_xcb_surface is a preprocessor guard. Do not pass it to API calls.
+#define VK_KHR_xcb_surface 1
+#define VK_KHR_XCB_SURFACE_SPEC_VERSION   6
+#define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface"
+typedef VkFlags VkXcbSurfaceCreateFlagsKHR;
+typedef struct VkXcbSurfaceCreateInfoKHR {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkXcbSurfaceCreateFlagsKHR    flags;
+    xcb_connection_t*             connection;
+    xcb_window_t                  window;
+} VkXcbSurfaceCreateInfoKHR;
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR(
+    VkInstance                                  instance,
+    const VkXcbSurfaceCreateInfoKHR*            pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+
+VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXcbPresentationSupportKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    queueFamilyIndex,
+    xcb_connection_t*                           connection,
+    xcb_visualid_t                              visual_id);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 56 - 0
vendor/vulkan/_gen/vulkan_xlib.h

@@ -0,0 +1,56 @@
+#ifndef VULKAN_XLIB_H_
+#define VULKAN_XLIB_H_ 1
+
+/*
+** Copyright 2015-2024 The Khronos Group Inc.
+**
+** SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+** This header is generated from the Khronos Vulkan XML API Registry.
+**
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+// VK_KHR_xlib_surface is a preprocessor guard. Do not pass it to API calls.
+#define VK_KHR_xlib_surface 1
+#define VK_KHR_XLIB_SURFACE_SPEC_VERSION  6
+#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
+typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
+typedef struct VkXlibSurfaceCreateInfoKHR {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkXlibSurfaceCreateFlagsKHR    flags;
+    Display*                       dpy;
+    Window                         window;
+} VkXlibSurfaceCreateInfoKHR;
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR(
+    VkInstance                                  instance,
+    const VkXlibSurfaceCreateInfoKHR*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+
+VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    queueFamilyIndex,
+    Display*                                    dpy,
+    VisualID                                    visualID);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 6 - 0
vendor/vulkan/core.odin

@@ -1161,6 +1161,12 @@ EXT_METAL_OBJECTS_EXTENSION_NAME                          :: "VK_EXT_metal_objec
 KHR_wayland_surface                                       :: 1
 KHR_WAYLAND_SURFACE_SPEC_VERSION                          :: 6
 KHR_WAYLAND_SURFACE_EXTENSION_NAME                        :: "VK_KHR_wayland_surface"
+KHR_xlib_surface                                          :: 1
+KHR_XLIB_SURFACE_SPEC_VERSION                             :: 6
+KHR_XLIB_SURFACE_EXTENSION_NAME                           :: "VK_KHR_xlib_surface"
+KHR_xcb_surface                                           :: 1
+KHR_XCB_SURFACE_SPEC_VERSION                              :: 6
+KHR_XCB_SURFACE_EXTENSION_NAME                            :: "VK_KHR_xcb_surface"
 
 // Handles types
 Instance       :: distinct Handle

+ 4 - 0
vendor/vulkan/enums.odin

@@ -4585,6 +4585,10 @@ WaylandSurfaceCreateFlagsKHR                         :: distinct bit_set[Wayland
 WaylandSurfaceCreateFlagKHR                          :: enum u32 {}
 Win32SurfaceCreateFlagsKHR                           :: distinct bit_set[Win32SurfaceCreateFlagKHR; Flags]
 Win32SurfaceCreateFlagKHR                            :: enum u32 {}
+XcbSurfaceCreateFlagsKHR                             :: distinct bit_set[XcbSurfaceCreateFlagKHR; Flags]
+XcbSurfaceCreateFlagKHR                              :: enum u32 {}
+XlibSurfaceCreateFlagsKHR                            :: distinct bit_set[XlibSurfaceCreateFlagKHR; Flags]
+XlibSurfaceCreateFlagKHR                             :: enum u32 {}
 AccessFlags2 :: distinct bit_set[AccessFlag2; Flags64]
 AccessFlag2 :: enum Flags64 {
 	INDIRECT_COMMAND_READ                     = 0,

+ 16 - 0
vendor/vulkan/procedures.odin

@@ -36,6 +36,8 @@ ProcCreateMacOSSurfaceMVK                                            :: #type pr
 ProcCreateMetalSurfaceEXT                                            :: #type proc "system" (instance: Instance, pCreateInfo: ^MetalSurfaceCreateInfoEXT, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result
 ProcCreateWaylandSurfaceKHR                                          :: #type proc "system" (instance: Instance, pCreateInfo: ^WaylandSurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result
 ProcCreateWin32SurfaceKHR                                            :: #type proc "system" (instance: Instance, pCreateInfo: ^Win32SurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result
+ProcCreateXcbSurfaceKHR                                              :: #type proc "system" (instance: Instance, pCreateInfo: ^XcbSurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result
+ProcCreateXlibSurfaceKHR                                             :: #type proc "system" (instance: Instance, pCreateInfo: ^XlibSurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result
 ProcDebugReportMessageEXT                                            :: #type proc "system" (instance: Instance, flags: DebugReportFlagsEXT, objectType: DebugReportObjectTypeEXT, object: u64, location: int, messageCode: i32, pLayerPrefix: cstring, pMessage: cstring)
 ProcDestroyDebugReportCallbackEXT                                    :: #type proc "system" (instance: Instance, callback: DebugReportCallbackEXT, pAllocator: ^AllocationCallbacks)
 ProcDestroyDebugUtilsMessengerEXT                                    :: #type proc "system" (instance: Instance, messenger: DebugUtilsMessengerEXT, pAllocator: ^AllocationCallbacks)
@@ -113,6 +115,8 @@ ProcGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR            :: #type pr
 ProcGetPhysicalDeviceVideoFormatPropertiesKHR                        :: #type proc "system" (physicalDevice: PhysicalDevice, pVideoFormatInfo: ^PhysicalDeviceVideoFormatInfoKHR, pVideoFormatPropertyCount: ^u32, pVideoFormatProperties: [^]VideoFormatPropertiesKHR) -> Result
 ProcGetPhysicalDeviceWaylandPresentationSupportKHR                   :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, display: ^wl_display) -> b32
 ProcGetPhysicalDeviceWin32PresentationSupportKHR                     :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32) -> b32
+ProcGetPhysicalDeviceXcbPresentationSupportKHR                       :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, connection: ^xcb_connection_t, visual_id: xcb_visualid_t) -> b32
+ProcGetPhysicalDeviceXlibPresentationSupportKHR                      :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, dpy: ^XlibDisplay, visualID: XlibVisualID) -> b32
 ProcGetWinrtDisplayNV                                                :: #type proc "system" (physicalDevice: PhysicalDevice, deviceRelativeId: u32, pDisplay: ^DisplayKHR) -> Result
 ProcReleaseDisplayEXT                                                :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR) -> Result
 ProcSubmitDebugUtilsMessageEXT                                       :: #type proc "system" (instance: Instance, messageSeverity: DebugUtilsMessageSeverityFlagsEXT, messageTypes: DebugUtilsMessageTypeFlagsEXT, pCallbackData: ^DebugUtilsMessengerCallbackDataEXT)
@@ -730,6 +734,8 @@ CreateMacOSSurfaceMVK:                                            ProcCreateMacO
 CreateMetalSurfaceEXT:                                            ProcCreateMetalSurfaceEXT
 CreateWaylandSurfaceKHR:                                          ProcCreateWaylandSurfaceKHR
 CreateWin32SurfaceKHR:                                            ProcCreateWin32SurfaceKHR
+CreateXcbSurfaceKHR:                                              ProcCreateXcbSurfaceKHR
+CreateXlibSurfaceKHR:                                             ProcCreateXlibSurfaceKHR
 DebugReportMessageEXT:                                            ProcDebugReportMessageEXT
 DestroyDebugReportCallbackEXT:                                    ProcDestroyDebugReportCallbackEXT
 DestroyDebugUtilsMessengerEXT:                                    ProcDestroyDebugUtilsMessengerEXT
@@ -806,6 +812,8 @@ GetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR:            ProcGetPhysica
 GetPhysicalDeviceVideoFormatPropertiesKHR:                        ProcGetPhysicalDeviceVideoFormatPropertiesKHR
 GetPhysicalDeviceWaylandPresentationSupportKHR:                   ProcGetPhysicalDeviceWaylandPresentationSupportKHR
 GetPhysicalDeviceWin32PresentationSupportKHR:                     ProcGetPhysicalDeviceWin32PresentationSupportKHR
+GetPhysicalDeviceXcbPresentationSupportKHR:                       ProcGetPhysicalDeviceXcbPresentationSupportKHR
+GetPhysicalDeviceXlibPresentationSupportKHR:                      ProcGetPhysicalDeviceXlibPresentationSupportKHR
 GetWinrtDisplayNV:                                                ProcGetWinrtDisplayNV
 ReleaseDisplayEXT:                                                ProcReleaseDisplayEXT
 SubmitDebugUtilsMessageEXT:                                       ProcSubmitDebugUtilsMessageEXT
@@ -1423,6 +1431,8 @@ load_proc_addresses_custom :: proc(set_proc_address: SetProcAddressType) {
 	set_proc_address(&CreateMetalSurfaceEXT,                                            "vkCreateMetalSurfaceEXT")
 	set_proc_address(&CreateWaylandSurfaceKHR,                                          "vkCreateWaylandSurfaceKHR")
 	set_proc_address(&CreateWin32SurfaceKHR,                                            "vkCreateWin32SurfaceKHR")
+	set_proc_address(&CreateXcbSurfaceKHR,                                              "vkCreateXcbSurfaceKHR")
+	set_proc_address(&CreateXlibSurfaceKHR,                                             "vkCreateXlibSurfaceKHR")
 	set_proc_address(&DebugReportMessageEXT,                                            "vkDebugReportMessageEXT")
 	set_proc_address(&DestroyDebugReportCallbackEXT,                                    "vkDestroyDebugReportCallbackEXT")
 	set_proc_address(&DestroyDebugUtilsMessengerEXT,                                    "vkDestroyDebugUtilsMessengerEXT")
@@ -1499,6 +1509,8 @@ load_proc_addresses_custom :: proc(set_proc_address: SetProcAddressType) {
 	set_proc_address(&GetPhysicalDeviceVideoFormatPropertiesKHR,                        "vkGetPhysicalDeviceVideoFormatPropertiesKHR")
 	set_proc_address(&GetPhysicalDeviceWaylandPresentationSupportKHR,                   "vkGetPhysicalDeviceWaylandPresentationSupportKHR")
 	set_proc_address(&GetPhysicalDeviceWin32PresentationSupportKHR,                     "vkGetPhysicalDeviceWin32PresentationSupportKHR")
+	set_proc_address(&GetPhysicalDeviceXcbPresentationSupportKHR,                       "vkGetPhysicalDeviceXcbPresentationSupportKHR")
+	set_proc_address(&GetPhysicalDeviceXlibPresentationSupportKHR,                      "vkGetPhysicalDeviceXlibPresentationSupportKHR")
 	set_proc_address(&GetWinrtDisplayNV,                                                "vkGetWinrtDisplayNV")
 	set_proc_address(&ReleaseDisplayEXT,                                                "vkReleaseDisplayEXT")
 	set_proc_address(&SubmitDebugUtilsMessageEXT,                                       "vkSubmitDebugUtilsMessageEXT")
@@ -3879,6 +3891,8 @@ load_proc_addresses_instance :: proc(instance: Instance) {
 	CreateMetalSurfaceEXT                                            = auto_cast GetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT")
 	CreateWaylandSurfaceKHR                                          = auto_cast GetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR")
 	CreateWin32SurfaceKHR                                            = auto_cast GetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR")
+	CreateXcbSurfaceKHR                                              = auto_cast GetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR")
+	CreateXlibSurfaceKHR                                             = auto_cast GetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR")
 	DebugReportMessageEXT                                            = auto_cast GetInstanceProcAddr(instance, "vkDebugReportMessageEXT")
 	DestroyDebugReportCallbackEXT                                    = auto_cast GetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")
 	DestroyDebugUtilsMessengerEXT                                    = auto_cast GetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")
@@ -3955,6 +3969,8 @@ load_proc_addresses_instance :: proc(instance: Instance) {
 	GetPhysicalDeviceVideoFormatPropertiesKHR                        = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceVideoFormatPropertiesKHR")
 	GetPhysicalDeviceWaylandPresentationSupportKHR                   = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR")
 	GetPhysicalDeviceWin32PresentationSupportKHR                     = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR")
+	GetPhysicalDeviceXcbPresentationSupportKHR                       = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR")
+	GetPhysicalDeviceXlibPresentationSupportKHR                      = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR")
 	GetWinrtDisplayNV                                                = auto_cast GetInstanceProcAddr(instance, "vkGetWinrtDisplayNV")
 	ReleaseDisplayEXT                                                = auto_cast GetInstanceProcAddr(instance, "vkReleaseDisplayEXT")
 	SubmitDebugUtilsMessageEXT                                       = auto_cast GetInstanceProcAddr(instance, "vkSubmitDebugUtilsMessageEXT")

+ 37 - 4
vendor/vulkan/structs.odin

@@ -7,6 +7,10 @@ import "core:c"
 
 import win32 "core:sys/windows"
 _ :: win32
+
+import "vendor:x11/xlib"
+_ :: xlib
+
 when ODIN_OS == .Windows {
 	HINSTANCE           :: win32.HINSTANCE
 	HWND                :: win32.HWND
@@ -32,7 +36,19 @@ when ODIN_OS == .Windows {
 	}
 }
 
-CAMetalLayer :: struct {}
+when xlib.IS_SUPPORTED {
+	XlibDisplay  :: xlib.Display
+	XlibWindow   :: xlib.Window
+	XlibVisualID :: xlib.VisualID
+} else {
+	XlibDisplay  :: struct {} // Opaque struct defined by Xlib
+	XlibWindow   :: c.ulong
+	XlibVisualID :: c.ulong
+}
+
+xcb_visualid_t :: u32
+xcb_window_t   :: u32
+CAMetalLayer   :: struct {}
 
 MTLBuffer_id       :: rawptr
 MTLTexture_id      :: rawptr
@@ -8910,6 +8926,22 @@ WaylandSurfaceCreateInfoKHR :: struct {
 	surface: ^wl_surface,
 }
 
+XlibSurfaceCreateInfoKHR :: struct {
+	sType:  StructureType,
+	pNext:  rawptr,
+	flags:  XlibSurfaceCreateFlagsKHR,
+	dpy:    ^XlibDisplay,
+	window: XlibWindow,
+}
+
+XcbSurfaceCreateInfoKHR :: struct {
+	sType:      StructureType,
+	pNext:      rawptr,
+	flags:      XcbSurfaceCreateFlagsKHR,
+	connection: ^xcb_connection_t,
+	window:     xcb_window_t,
+}
+
 VideoAV1ColorConfigFlags :: struct {
 	bitfield: u32,
 }
@@ -9753,9 +9785,10 @@ VideoEncodeH265ReferenceInfo :: struct {
 
 // Opaque structs
 
-wl_surface   :: struct {} // Opaque struct defined by Wayland
-wl_display   :: struct {} // Opaque struct defined by Wayland
-IOSurfaceRef :: struct {} // Opaque struct defined by Apple’s CoreGraphics framework
+wl_surface       :: struct {} // Opaque struct defined by Wayland
+wl_display       :: struct {} // Opaque struct defined by Wayland
+xcb_connection_t :: struct {} // Opaque struct defined by xcb
+IOSurfaceRef     :: struct {} // Opaque struct defined by Apple’s CoreGraphics framework
 // Aliases
 PhysicalDeviceVariablePointerFeatures                       :: PhysicalDeviceVariablePointersFeatures
 PhysicalDeviceShaderDrawParameterFeatures                   :: PhysicalDeviceShaderDrawParametersFeatures

+ 4 - 0
vendor/x11/xlib/xlib.odin

@@ -0,0 +1,4 @@
+package xlib
+
+// Value, specifying whether `vendor:x11/xlib` is available on the current platform.
+IS_SUPPORTED :: ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD

Some files were not shown because too many files changed in this diff