Ver código fonte

Merge branch 'odin-lang:master' into json-better-enum-support

blob1807 1 ano atrás
pai
commit
f14babe419
60 arquivos alterados com 4220 adições e 2293 exclusões
  1. 23 15
      .github/workflows/nightly.yml
  2. 1 0
      .gitignore
  3. 12 2
      base/runtime/core.odin
  4. 22 0
      base/runtime/internal.odin
  5. 14 0
      base/runtime/print.odin
  6. 14 2
      ci/upload_create_nightly.sh
  7. 678 0
      core/container/avl/avl.odin
  8. 3 0
      core/encoding/json/marshal.odin
  9. 211 44
      core/fmt/fmt.odin
  10. 16 8
      core/fmt/fmt_os.odin
  11. 4 2
      core/net/errors_darwin.odin
  12. 8 1
      core/net/socket_darwin.odin
  13. 7 2
      core/odin/doc-format/doc_format.odin
  14. 2 0
      core/odin/tokenizer/token.odin
  15. 2 5
      core/os/os.odin
  16. 2 5
      core/os/os2/file_util.odin
  17. 47 0
      core/reflect/reflect.odin
  18. 31 0
      core/reflect/types.odin
  19. 489 0
      core/sync/chan/chan.odin
  20. 24 0
      core/sync/extended.odin
  21. 8 0
      core/time/time.odin
  22. 2 0
      examples/all/all_main.odin
  23. 1067 1061
      src/bug_report.cpp
  24. 20 0
      src/check_builtin.cpp
  25. 1 0
      src/check_decl.cpp
  26. 164 31
      src/check_expr.cpp
  27. 10 0
      src/check_stmt.cpp
  28. 212 0
      src/check_type.cpp
  29. 18 0
      src/checker.cpp
  30. 1 0
      src/checker.hpp
  31. 4 0
      src/checker_builtin_procs.hpp
  32. 6 3
      src/docs_format.cpp
  33. 24 1
      src/docs_writer.cpp
  34. 2 0
      src/entity.cpp
  35. 17 55
      src/llvm_backend.cpp
  36. 9 3
      src/llvm_backend.hpp
  37. 36 0
      src/llvm_backend_debug.cpp
  38. 74 0
      src/llvm_backend_expr.cpp
  39. 52 3
      src/llvm_backend_general.cpp
  40. 3 3
      src/llvm_backend_proc.cpp
  41. 18 5
      src/llvm_backend_stmt.cpp
  42. 160 837
      src/llvm_backend_type.cpp
  43. 1 1
      src/llvm_backend_utility.cpp
  44. 97 0
      src/parser.cpp
  45. 17 0
      src/parser.hpp
  46. 3 0
      src/parser_pos.cpp
  47. 1 0
      src/tokenizer.cpp
  48. 185 5
      src/types.cpp
  49. 28 22
      tests/core/Makefile
  50. 0 1
      tests/core/c/libc/test_core_libc.odin
  51. 161 0
      tests/core/container/test_core_avl.odin
  52. 26 0
      tests/core/container/test_core_container.odin
  53. 9 19
      tests/core/container/test_core_small_array.odin
  54. 84 132
      tests/core/net/test_core_net.odin
  55. 5 2
      tests/core/text/match/test_core_text_match.odin
  56. 61 0
      vendor/egl/egl.odin
  57. 3 2
      vendor/glfw/wrapper.odin
  58. 6 6
      vendor/raylib/raylib.odin
  59. 1 1
      vendor/vulkan/_gen/create_vulkan_odin_wrapper.py
  60. 14 14
      vendor/vulkan/structs.odin

+ 23 - 15
.github/workflows/nightly.yml

@@ -63,11 +63,13 @@ jobs:
           cp -r core dist
           cp -r vendor dist
           cp -r examples dist
+          # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
+          zip -r dist.zip dist
       - name: Upload artifact
         uses: actions/upload-artifact@v1
         with:
           name: ubuntu_artifacts
-          path: dist
+          path: dist.zip
   build_macos:
     name: MacOS Build
     if: github.repository == 'odin-lang/Odin'
@@ -76,15 +78,13 @@ jobs:
       - uses: actions/checkout@v1
       - name: Download LLVM and setup PATH
         run: |
-          brew install llvm@13
+          brew install llvm@13 dylibbundler
           echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
           TMP_PATH=$(xcrun --show-sdk-path)/user/include
           echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
       - name: build odin
         run: make nightly
-      - name: Odin run
-        run: ./odin run examples/demo
-      - name: Copy artifacts
+      - name: Bundle
         run: |
           mkdir dist
           cp odin dist
@@ -94,28 +94,31 @@ jobs:
           cp -r core dist
           cp -r vendor dist
           cp -r examples dist
+          dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
+          # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
+          zip -r dist.zip dist
+      - name: Odin run
+        run: ./dist/odin run examples/demo
       - name: Upload artifact
         uses: actions/upload-artifact@v1
         with:
           name: macos_artifacts
-          path: dist
+          path: dist.zip
   build_macos_arm:
     name: MacOS ARM Build
     if: github.repository == 'odin-lang/Odin'
-    runs-on: macos-14
+    runs-on: macos-14 # ARM machine
     steps:
       - uses: actions/checkout@v1
       - name: Download LLVM and setup PATH
         run: |
-          brew install llvm@13
+          brew install llvm@13 dylibbundler
           echo "/opt/homebrew/opt/llvm@13/bin" >> $GITHUB_PATH
           TMP_PATH=$(xcrun --show-sdk-path)/user/include
           echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
       - name: build odin
         run: make nightly
-      - name: Odin run
-        run: ./odin run examples/demo
-      - name: Copy artifacts
+      - name: Bundle
         run: |
           mkdir dist
           cp odin dist
@@ -125,11 +128,16 @@ jobs:
           cp -r core dist
           cp -r vendor dist
           cp -r examples dist
+          dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
+          # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
+          zip -r dist.zip dist
+      - name: Odin run
+        run: ./dist/odin run examples/demo
       - name: Upload artifact
         uses: actions/upload-artifact@v1
         with:
           name: macos_arm_artifacts
-          path: dist
+          path: dist.zip
   upload_b2:
     runs-on: [ubuntu-latest]
     needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
@@ -182,9 +190,9 @@ jobs:
           echo Uploading artifcates to B2
           chmod +x ./ci/upload_create_nightly.sh
           ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
-          ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
-          ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
-          ./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/
+          ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/dist.zip
+          ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/dist.zip
+          ./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/dist.zip
 
           echo Deleting old artifacts in B2
           python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"

+ 1 - 0
.gitignore

@@ -28,6 +28,7 @@ tests/internal/test_map
 tests/internal/test_pow
 tests/internal/test_rtti
 tests/core/test_core_compress
+tests/core/test_core_container
 tests/core/test_core_filepath
 tests/core/test_core_fmt
 tests/core/test_core_i18n

+ 12 - 2
base/runtime/core.odin

@@ -181,6 +181,14 @@ Type_Info_Matrix :: struct {
 Type_Info_Soa_Pointer :: struct {
 	elem: ^Type_Info,
 }
+Type_Info_Bit_Field :: struct {
+	backing_type: ^Type_Info,
+	names:        []string,
+	types:        []^Type_Info,
+	bit_sizes:    []uintptr,
+	bit_offsets:  []uintptr,
+	tags:         []string,
+}
 
 Type_Info_Flag :: enum u8 {
 	Comparable     = 0,
@@ -223,6 +231,7 @@ Type_Info :: struct {
 		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Matrix,
 		Type_Info_Soa_Pointer,
+		Type_Info_Bit_Field,
 	},
 }
 
@@ -256,6 +265,7 @@ Typeid_Kind :: enum u8 {
 	Relative_Multi_Pointer,
 	Matrix,
 	Soa_Pointer,
+	Bit_Field,
 }
 #assert(len(Typeid_Kind) < 32)
 
@@ -270,7 +280,7 @@ Typeid_Kind :: enum u8 {
 
 // NOTE(bill): only the ones that are needed (not all types)
 // This will be set by the compiler
-type_table: []Type_Info
+type_table: []^Type_Info
 
 args__: []cstring
 
@@ -599,7 +609,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
 	if n < 0 || n >= len(type_table) {
 		n = 0
 	}
-	return &type_table[n]
+	return type_table[n]
 }
 
 when !ODIN_NO_RTTI {

+ 22 - 0
base/runtime/internal.odin

@@ -1034,3 +1034,25 @@ fixdfti :: proc(a: u64) -> i128 {
 	}
 
 }
+
+
+
+__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
+	for i in 0..<size {
+		j := offset+i
+		the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
+		b := the_bit<<(j&7)
+		dst[j/8] &~= b
+		dst[j/8] |=  b
+	}
+}
+
+__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
+	for j in 0..<size {
+		i := offset+j
+		the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
+		b := the_bit<<(j&7)
+		dst[j/8] &~= b
+		dst[j/8] |=  b
+	}
+}

+ 14 - 0
base/runtime/print.odin

@@ -459,6 +459,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
 		}
 		print_byte(']')
 
+	case Type_Info_Bit_Field:
+		print_string("bit_field ")
+		print_type(info.backing_type)
+		print_string(" {")
+		for name, i in info.names {
+			if i > 0 { print_string(", ") }
+			print_string(name)
+			print_string(": ")
+			print_type(info.types[i])
+			print_string(" | ")
+			print_u64(u64(info.bit_sizes[i]))
+		}
+		print_byte('}')
+
 
 	case Type_Info_Simd_Vector:
 		print_string("#simd[")

+ 14 - 2
ci/upload_create_nightly.sh

@@ -1,5 +1,7 @@
 #!/bin/bash
 
+set -e
+
 bucket=$1
 platform=$2
 artifact=$3
@@ -9,5 +11,15 @@ filename="odin-$platform-nightly+$now.zip"
 
 echo "Creating archive $filename from $artifact and uploading to $bucket"
 
-7z a -bd "output/$filename" -r "$artifact"
-b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
+# If this is already zipped up (done before artifact upload to keep permissions in tact), just move it.
+if [ "${artifact: -4}" == ".zip" ]
+then
+	echo "Artifact already a zip"
+	mkdir -p "output"
+	mv "$artifact" "output/$filename"
+else
+	echo "Artifact needs to be zipped"
+	7z a -bd "output/$filename" -r "$artifact"
+fi
+
+b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"

+ 678 - 0
core/container/avl/avl.odin

@@ -0,0 +1,678 @@
+/*
+package avl implements an AVL tree.
+
+The implementation is non-intrusive, and non-recursive.
+*/
+package container_avl
+
+import "base:intrinsics"
+import "base:runtime"
+import "core:slice"
+
+_ :: intrinsics
+_ :: runtime
+
+// Originally based on the CC0 implementation by Eric Biggers
+// See: https://github.com/ebiggers/avl_tree/
+
+// Direction specifies the traversal direction for a tree iterator.
+Direction :: enum i8 {
+	// Backward is the in-order backwards direction.
+	Backward = -1,
+	// Forward is the in-order forwards direction.
+	Forward  = 1,
+}
+
+// Ordering specifies order when inserting/finding values into the tree.
+Ordering :: slice.Ordering
+
+// Tree is an AVL tree.
+Tree :: struct($Value: typeid) {
+	// user_data is a parameter that will be passed to the on_remove
+	// callback.
+	user_data: rawptr,
+	// on_remove is an optional callback that can be called immediately
+	// after a node is removed from the tree.
+	on_remove: proc(value: Value, user_data: rawptr),
+
+	_root:           ^Node(Value),
+	_node_allocator: runtime.Allocator,
+	_cmp_fn:         proc(a, b: Value) -> Ordering,
+	_size:           int,
+}
+
+// Node is an AVL tree node.
+//
+// WARNING: It is unsafe to mutate value if the node is part of a tree
+// if doing so will alter the Node's sort position relative to other
+// elements in the tree.
+Node :: struct($Value: typeid) {
+	value: Value,
+
+	_parent:  ^Node(Value),
+	_left:    ^Node(Value),
+	_right:   ^Node(Value),
+	_balance: i8,
+}
+
+// Iterator is a tree iterator.
+//
+// WARNING: It is unsafe to modify the tree while iterating, except via
+// the iterator_remove method.
+Iterator :: struct($Value: typeid) {
+	_tree:        ^Tree(Value),
+	_cur:         ^Node(Value),
+	_next:        ^Node(Value),
+	_direction:   Direction,
+	_called_next: bool,
+}
+
+// init initializes a tree.
+init :: proc {
+	init_ordered,
+	init_cmp,
+}
+
+// init_cmp initializes a tree.
+init_cmp :: proc(
+	t: ^$T/Tree($Value),
+	cmp_fn: proc(a, b: Value) -> Ordering,
+	node_allocator := context.allocator,
+) {
+	t._root = nil
+	t._node_allocator = node_allocator
+	t._cmp_fn = cmp_fn
+	t._size = 0
+}
+
+// init_ordered initializes a tree containing ordered items, with
+// a comparison function that results in an ascending order sort.
+init_ordered :: proc(
+	t: ^$T/Tree($Value),
+	node_allocator := context.allocator,
+) where intrinsics.type_is_ordered_numeric(Value) {
+	init_cmp(t, slice.cmp_proc(Value), node_allocator)
+}
+
+// destroy de-initializes a tree.
+destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
+	iter := iterator(t, Direction.Forward)
+	for _ in iterator_next(&iter) {
+		iterator_remove(&iter, call_on_remove)
+	}
+}
+
+// len returns the number of elements in the tree.
+len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
+	return t._size
+}
+
+// first returns the first node in the tree (in-order) or nil iff
+// the tree is empty.
+first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
+	return tree_first_or_last_in_order(t, Direction.Backward)
+}
+
+// last returns the last element in the tree (in-order) or nil iff
+// the tree is empty.
+last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
+	return tree_first_or_last_in_order(t, Direction.Forward)
+}
+
+// find finds the value in the tree, and returns the corresponding
+// node or nil iff the value is not present.
+find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
+	cur := t._root
+	descend_loop: for cur != nil {
+		switch t._cmp_fn(value, cur.value) {
+		case .Less:
+			cur = cur._left
+		case .Greater:
+			cur = cur._right
+		case .Equal:
+			break descend_loop
+		}
+	}
+
+	return cur
+}
+
+// find_or_insert attempts to insert the value into the tree, and returns
+// the node, a boolean indicating if the value was inserted, and the
+// node allocator error if relevant.  If the value is already
+// present, the existing node is returned un-altered.
+find_or_insert :: proc(
+	t: ^$T/Tree($Value),
+	value: Value,
+) -> (
+	n: ^Node(Value),
+	inserted: bool,
+	err: runtime.Allocator_Error,
+) {
+	n_ptr := &t._root
+	for n_ptr^ != nil {
+		n = n_ptr^
+		switch t._cmp_fn(value, n.value) {
+		case .Less:
+			n_ptr = &n._left
+		case .Greater:
+			n_ptr = &n._right
+		case .Equal:
+			return
+		}
+	}
+
+	parent := n
+	n = new(Node(Value), t._node_allocator) or_return
+	n.value = value
+	n._parent = parent
+	n_ptr^ = n
+	tree_rebalance_after_insert(t, n)
+
+	t._size += 1
+	inserted = true
+
+	return
+}
+
+// remove removes a node or value from the tree, and returns true iff the
+// removal was successful.  While the node's value will be left intact,
+// the node itself will be freed via the tree's node allocator.
+remove :: proc {
+	remove_value,
+	remove_node,
+}
+
+// remove_value removes a value from the tree, and returns true iff the
+// removal was successful.  While the node's value will be left intact,
+// the node itself will be freed via the tree's node allocator.
+remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
+	n := find(t, value)
+	if n == nil {
+		return false
+	}
+	return remove_node(t, n, call_on_remove)
+}
+
+// remove_node removes a node from the tree, and returns true iff the
+// removal was successful.  While the node's value will be left intact,
+// the node itself will be freed via the tree's node allocator.
+remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
+	if node._parent == node || (node._parent == nil && t._root != node) {
+		return false
+	}
+	defer {
+		if call_on_remove && t.on_remove != nil {
+			t.on_remove(node.value, t.user_data)
+		}
+		free(node, t._node_allocator)
+	}
+
+	parent: ^Node(Value)
+	left_deleted: bool
+
+	t._size -= 1
+	if node._left != nil && node._right != nil {
+		parent, left_deleted = tree_swap_with_successor(t, node)
+	} else {
+		child := node._left
+		if child == nil {
+			child = node._right
+		}
+		parent = node._parent
+		if parent != nil {
+			if node == parent._left {
+				parent._left = child
+				left_deleted = true
+			} else {
+				parent._right = child
+				left_deleted = false
+			}
+			if child != nil {
+				child._parent = parent
+			}
+		} else {
+			if child != nil {
+				child._parent = parent
+			}
+			t._root = child
+			node_reset(node)
+			return true
+		}
+	}
+
+	for {
+		if left_deleted {
+			parent = tree_handle_subtree_shrink(t, parent, +1, &left_deleted)
+		} else {
+			parent = tree_handle_subtree_shrink(t, parent, -1, &left_deleted)
+		}
+		if parent == nil {
+			break
+		}
+	}
+	node_reset(node)
+
+	return true
+}
+
+// iterator returns a tree iterator in the specified direction.
+iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
+	it: Iterator(Value)
+	it._tree = transmute(^Tree(Value))t
+	it._direction = direction
+
+	iterator_first(&it)
+
+	return it
+}
+
+// iterator_from_pos returns a tree iterator in the specified direction,
+// spanning the range [pos, last] (inclusive).
+iterator_from_pos :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	pos: ^Node(Value),
+	direction: Direction,
+) -> Iterator(Value) {
+	it: Iterator(Value)
+	it._tree = transmute(^Tree(Value))t
+	it._direction = direction
+	it._next = nil
+	it._called_next = false
+
+	if it._cur = pos; pos != nil {
+		it._next = node_next_or_prev_in_order(it._cur, it._direction)
+	}
+
+	return it
+}
+
+// iterator_get returns the node currently pointed to by the iterator,
+// or nil iff the node has been removed, the tree is empty, or the end
+// of the tree has been reached.
+iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
+	return it._cur
+}
+
+// iterator_remove removes the node currently pointed to by the iterator,
+// and returns true iff the removal was successful.  Semantics are the
+// same as the Tree remove.
+iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
+	if it._cur == nil {
+		return false
+	}
+
+	ok := remove_node(it._tree, it._cur, call_on_remove)
+	if ok {
+		it._cur = nil
+	}
+
+	return ok
+}
+
+// iterator_next advances the iterator and returns the (node, true) or
+// or (nil, false) iff the end of the tree has been reached.
+//
+// Note: The first call to iterator_next will return the first node instead
+// of advancing the iterator.
+iterator_next :: proc "contextless" (it: ^$I/Iterator($Value)) -> (^Node(Value), bool) {
+	// This check is needed so that the first element gets returned from
+	// a brand-new iterator, and so that the somewhat contrived case where
+	// iterator_remove is called before the first call to iterator_next
+	// returns the correct value.
+	if !it._called_next {
+		it._called_next = true
+
+		// There can be the contrived case where iterator_remove is
+		// called before ever calling iterator_next, which needs to be
+		// handled as an actual call to next.
+		//
+		// If this happens it._cur will be nil, so only return the
+		// first value, if it._cur is valid.
+		if it._cur != nil {
+			return it._cur, true
+		}
+	}
+
+	if it._next == nil {
+		return nil, false
+	}
+
+	it._cur = it._next
+	it._next = node_next_or_prev_in_order(it._cur, it._direction)
+
+	return it._cur, true
+}
+
+@(private)
+tree_first_or_last_in_order :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	direction: Direction,
+) -> ^Node(Value) {
+	first, sign := t._root, i8(direction)
+	if first != nil {
+		for {
+			tmp := node_get_child(first, +sign)
+			if tmp == nil {
+				break
+			}
+			first = tmp
+		}
+	}
+
+	return first
+}
+
+@(private)
+tree_replace_child :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	parent, old_child, new_child: ^Node(Value),
+) {
+	if parent != nil {
+		if old_child == parent._left {
+			parent._left = new_child
+		} else {
+			parent._right = new_child
+		}
+	} else {
+		t._root = new_child
+	}
+}
+
+@(private)
+tree_rotate :: proc "contextless" (t: ^$T/Tree($Value), a: ^Node(Value), sign: i8) {
+	b := node_get_child(a, -sign)
+	e := node_get_child(b, +sign)
+	p := a._parent
+
+	node_set_child(a, -sign, e)
+	a._parent = b
+
+	node_set_child(b, +sign, a)
+	b._parent = p
+
+	if e != nil {
+		e._parent = a
+	}
+
+	tree_replace_child(t, p, a, b)
+}
+
+@(private)
+tree_double_rotate :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	b, a: ^Node(Value),
+	sign: i8,
+) -> ^Node(Value) {
+	e := node_get_child(b, +sign)
+	f := node_get_child(e, -sign)
+	g := node_get_child(e, +sign)
+	p := a._parent
+	e_bal := e._balance
+
+	node_set_child(a, -sign, g)
+	a_bal := -e_bal
+	if sign * e_bal >= 0 {
+		a_bal = 0
+	}
+	node_set_parent_balance(a, e, a_bal)
+
+	node_set_child(b, +sign, f)
+	b_bal := -e_bal
+	if sign * e_bal <= 0 {
+		b_bal = 0
+	}
+	node_set_parent_balance(b, e, b_bal)
+
+	node_set_child(e, +sign, a)
+	node_set_child(e, -sign, b)
+	node_set_parent_balance(e, p, 0)
+
+	if g != nil {
+		g._parent = a
+	}
+
+	if f != nil {
+		f._parent = b
+	}
+
+	tree_replace_child(t, p, a, e)
+
+	return e
+}
+
+@(private)
+tree_handle_subtree_growth :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	node, parent: ^Node(Value),
+	sign: i8,
+) -> bool {
+	old_balance_factor := parent._balance
+	if old_balance_factor == 0 {
+		node_adjust_balance_factor(parent, sign)
+		return false
+	}
+
+	new_balance_factor := old_balance_factor + sign
+	if new_balance_factor == 0 {
+		node_adjust_balance_factor(parent, sign)
+		return true
+	}
+
+	if sign * node._balance > 0 {
+		tree_rotate(t, parent, -sign)
+		node_adjust_balance_factor(parent, -sign)
+		node_adjust_balance_factor(node, -sign)
+	} else {
+		tree_double_rotate(t, node, parent, -sign)
+	}
+
+	return true
+}
+
+@(private)
+tree_rebalance_after_insert :: proc "contextless" (t: ^$T/Tree($Value), inserted: ^Node(Value)) {
+	node, parent := inserted, inserted._parent
+	switch {
+	case parent == nil:
+		return
+	case node == parent._left:
+		node_adjust_balance_factor(parent, -1)
+	case:
+		node_adjust_balance_factor(parent, +1)
+	}
+
+	if parent._balance == 0 {
+		return
+	}
+
+	for done := false; !done; {
+		node = parent
+		if parent = node._parent; parent == nil {
+			return
+		}
+
+		if node == parent._left {
+			done = tree_handle_subtree_growth(t, node, parent, -1)
+		} else {
+			done = tree_handle_subtree_growth(t, node, parent, +1)
+		}
+	}
+}
+
+@(private)
+tree_swap_with_successor :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	x: ^Node(Value),
+) -> (
+	^Node(Value),
+	bool,
+) {
+	ret: ^Node(Value)
+	left_deleted: bool
+
+	y := x._right
+	if y._left == nil {
+		ret = y
+	} else {
+		q: ^Node(Value)
+
+		for {
+			q = y
+			if y = y._left; y._left == nil {
+				break
+			}
+		}
+
+		if q._left = y._right; q._left != nil {
+			q._left._parent = q
+		}
+		y._right = x._right
+		x._right._parent = y
+		ret = q
+		left_deleted = true
+	}
+
+	y._left = x._left
+	x._left._parent = y
+
+	y._parent = x._parent
+	y._balance = x._balance
+
+	tree_replace_child(t, x._parent, x, y)
+
+	return ret, left_deleted
+}
+
+@(private)
+tree_handle_subtree_shrink :: proc "contextless" (
+	t: ^$T/Tree($Value),
+	parent: ^Node(Value),
+	sign: i8,
+	left_deleted: ^bool,
+) -> ^Node(Value) {
+	old_balance_factor := parent._balance
+	if old_balance_factor == 0 {
+		node_adjust_balance_factor(parent, sign)
+		return nil
+	}
+
+	node: ^Node(Value)
+	new_balance_factor := old_balance_factor + sign
+	if new_balance_factor == 0 {
+		node_adjust_balance_factor(parent, sign)
+		node = parent
+	} else {
+		node = node_get_child(parent, sign)
+		if sign * node._balance >= 0 {
+			tree_rotate(t, parent, -sign)
+			if node._balance == 0 {
+				node_adjust_balance_factor(node, -sign)
+				return nil
+			}
+			node_adjust_balance_factor(parent, -sign)
+			node_adjust_balance_factor(node, -sign)
+		} else {
+			node = tree_double_rotate(t, node, parent, -sign)
+		}
+	}
+
+	parent := parent
+	if parent = node._parent; parent != nil {
+		left_deleted^ = node == parent._left
+	}
+	return parent
+}
+
+@(private)
+node_reset :: proc "contextless" (n: ^Node($Value)) {
+	// Mostly pointless as n will be deleted after this is called, but
+	// attempt to be able to catch cases of n not being in the tree.
+	n._parent = n
+	n._left = nil
+	n._right = nil
+	n._balance = 0
+}
+
+@(private)
+node_set_parent_balance :: #force_inline proc "contextless" (
+	n, parent: ^Node($Value),
+	balance: i8,
+) {
+	n._parent = parent
+	n._balance = balance
+}
+
+@(private)
+node_get_child :: #force_inline proc "contextless" (n: ^Node($Value), sign: i8) -> ^Node(Value) {
+	if sign < 0 {
+		return n._left
+	}
+	return n._right
+}
+
+@(private)
+node_next_or_prev_in_order :: proc "contextless" (
+	n: ^Node($Value),
+	direction: Direction,
+) -> ^Node(Value) {
+	next, tmp: ^Node(Value)
+	sign := i8(direction)
+
+	if next = node_get_child(n, +sign); next != nil {
+		for {
+			tmp = node_get_child(next, -sign)
+			if tmp == nil {
+				break
+			}
+			next = tmp
+		}
+	} else {
+		tmp, next = n, n._parent
+		for next != nil && tmp == node_get_child(next, +sign) {
+			tmp, next = next, next._parent
+		}
+	}
+	return next
+}
+
+@(private)
+node_set_child :: #force_inline proc "contextless" (
+	n: ^Node($Value),
+	sign: i8,
+	child: ^Node(Value),
+) {
+	if sign < 0 {
+		n._left = child
+	} else {
+		n._right = child
+	}
+}
+
+@(private)
+node_adjust_balance_factor :: #force_inline proc "contextless" (n: ^Node($Value), amount: i8) {
+	n._balance += amount
+}
+
+@(private)
+iterator_first :: proc "contextless" (it: ^Iterator($Value)) {
+	// This is private because behavior when the user manually calls
+	// iterator_first followed by iterator_next is unintuitive, since
+	// the first call to iterator_next MUST return the first node
+	// instead of advancing so that `for node in iterator_next(&next)`
+	// works as expected.
+
+	switch it._direction {
+	case .Forward:
+		it._cur = tree_first_or_last_in_order(it._tree, .Backward)
+	case .Backward:
+		it._cur = tree_first_or_last_in_order(it._tree, .Forward)
+	}
+
+	it._next = nil
+	it._called_next = false
+
+	if it._cur != nil {
+		it._next = node_next_or_prev_in_order(it._cur, it._direction)
+	}
+}

+ 3 - 0
core/encoding/json/marshal.odin

@@ -231,6 +231,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 	case runtime.Type_Info_Matrix:
 		return .Unsupported_Type
 
+	case runtime.Type_Info_Bit_Field:
+		return .Unsupported_Type
+
 	case runtime.Type_Info_Array:
 		opt_write_start(w, opt, '[') or_return
 		for i in 0..<info.count {

+ 211 - 44
core/fmt/fmt.odin

@@ -147,17 +147,31 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
 // 	*Allocates Using Context's Allocator*
 //
 // 	Inputs:
-// 	- fmt: A format string with placeholders for the provided arguments.
-// 	- args: A variadic list of arguments to be formatted.
+//	- fmt: A format string with placeholders for the provided arguments.
+//	- args: A variadic list of arguments to be formatted.
+//	- newline: Whether the string should end with a newline. (See `aprintfln`.)
 //
 // 	Returns: A formatted string. The returned string must be freed accordingly.
 //
-aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
+aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
 	str: strings.Builder
 	strings.builder_init(&str, allocator)
-	sbprintf(&str, fmt, ..args)
+	sbprintf(&str, fmt, ..args, newline=newline)
 	return strings.to_string(str)
 }
+// 	Creates a formatted string using a format string and arguments, followed by a newline.
+//
+// 	*Allocates Using Context's Allocator*
+//
+// 	Inputs:
+// 	- fmt: A format string with placeholders for the provided arguments.
+// 	- args: A variadic list of arguments to be formatted.
+//
+// 	Returns: A formatted string. The returned string must be freed accordingly.
+//
+aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
+	return aprintf(fmt, ..args, allocator=allocator, newline=true)
+}
 // 	Creates a formatted string
 //
 // 	*Allocates Using Context's Temporary Allocator*
@@ -195,17 +209,31 @@ tprintln :: proc(args: ..any, sep := " ") -> string {
 // 	*Allocates Using Context's Temporary Allocator*
 //
 // 	Inputs:
-// 	- fmt: A format string with placeholders for the provided arguments.
-// 	- args: A variadic list of arguments to be formatted.
+//	- fmt: A format string with placeholders for the provided arguments.
+//	- args: A variadic list of arguments to be formatted.
+//	- newline: Whether the string should end with a newline. (See `tprintfln`.)
 //
 // 	Returns: A formatted string.
 //
-tprintf :: proc(fmt: string, args: ..any) -> string {
+tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
-	sbprintf(&str, fmt, ..args)
+	sbprintf(&str, fmt, ..args, newline=newline)
 	return strings.to_string(str)
 }
+// 	Creates a formatted string using a format string and arguments, followed by a newline.
+//
+// 	*Allocates Using Context's Temporary Allocator*
+//
+// 	Inputs:
+// 	- fmt: A format string with placeholders for the provided arguments.
+// 	- args: A variadic list of arguments to be formatted.
+//
+// 	Returns: A formatted string.
+//
+tprintfln :: proc(fmt: string, args: ..any) -> string {
+	return tprintf(fmt, ..args, newline=true)
+}
 // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
 //
 // Inputs:
@@ -238,12 +266,25 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 // - buf: The backing buffer
 // - fmt: A format string with placeholders for the provided arguments
 // - args: A variadic list of arguments to be formatted
+// - newline: Whether the string should end with a newline. (See `bprintfln`.)
 //
 // Returns: A formatted string
 //
-bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
+bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string {
 	sb := strings.builder_from_bytes(buf)
-	return sbprintf(&sb, fmt, ..args)
+	return sbprintf(&sb, fmt, ..args, newline=newline)
+}
+// Creates a formatted string using a supplied buffer as the backing array, followed by a newline. Writes into the buffer.
+//
+// Inputs:
+// - buf: The backing buffer
+// - fmt: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted string
+//
+bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string {
+	return bprintf(buf, fmt, ..args, newline=true)
 }
 // Runtime assertion with a formatted message
 //
@@ -294,17 +335,31 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
 // Inputs:
 // - format: A format string with placeholders for the provided arguments
 // - args: A variadic list of arguments to be formatted
+// - newline: Whether the string should end with a newline. (See `caprintfln`.)
 //
 // Returns: A formatted C string
 //
-caprintf :: proc(format: string, args: ..any) -> cstring {
+caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
 	str: strings.Builder
 	strings.builder_init(&str)
-	sbprintf(&str, format, ..args)
+	sbprintf(&str, format, ..args, newline=newline)
 	strings.write_byte(&str, 0)
 	s := strings.to_string(str)
 	return cstring(raw_data(s))
 }
+// Creates a formatted C string, followed by a newline.
+//
+// *Allocates Using Context's Allocator*
+//
+// Inputs:
+// - format: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted C string
+//
+caprintfln :: proc(format: string, args: ..any) -> cstring {
+	return caprintf(format, ..args, newline=true)
+}
 // Creates a formatted C string
 //
 // *Allocates Using Context's Temporary Allocator*
@@ -312,17 +367,31 @@ caprintf :: proc(format: string, args: ..any) -> cstring {
 // Inputs:
 // - format: A format string with placeholders for the provided arguments
 // - args: A variadic list of arguments to be formatted
+// - newline: Whether the string should end with a newline. (See `ctprintfln`.)
 //
 // Returns: A formatted C string
 //
-ctprintf :: proc(format: string, args: ..any) -> cstring {
+ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
-	sbprintf(&str, format, ..args)
+	sbprintf(&str, format, ..args, newline=newline)
 	strings.write_byte(&str, 0)
 	s := strings.to_string(str)
 	return cstring(raw_data(s))
 }
+// Creates a formatted C string, followed by a newline.
+//
+// *Allocates Using Context's Temporary Allocator*
+//
+// Inputs:
+// - format: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted C string
+//
+ctprintfln :: proc(format: string, args: ..any) -> cstring {
+	return ctprintf(format, ..args, newline=true)
+}
 // Formats using the default print settings and writes to the given strings.Builder
 //
 // Inputs:
@@ -355,13 +424,25 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 // - buf: A pointer to a strings.Builder buffer
 // - fmt: The format string
 // - args: A variadic list of arguments to be formatted
+// - newline: Whether a trailing newline should be written. (See `sbprintfln`.)
 //
 // Returns: The resulting formatted string
 //
-sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
-	wprintf(strings.to_writer(buf), fmt, ..args, flush=true)
+sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string {
+	wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline)
 	return strings.to_string(buf^)
 }
+// Formats and writes to a strings.Builder buffer according to the specified format string, followed by a newline.
+//
+// Inputs:
+// - buf:  A pointer to a strings.Builder to store the formatted string
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted string
+//
+sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string {
+	return sbprintf(buf, format, ..args, newline=true)
+}
 // Formats and writes to an io.Writer using the default print settings
 //
 // Inputs:
@@ -435,10 +516,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
 // - w: An io.Writer to write to
 // - fmt: The format string
 // - args: A variadic list of arguments to be formatted
+// - newline: Whether a trailing newline should be written. (See `wprintfln`.)
 //
 // Returns: The number of bytes written
 //
-wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
+wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
 	fi: Info
 	arg_index: int = 0
 	end := len(fmt)
@@ -708,12 +790,27 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
 		}
 		io.write_string(fi.writer, ")", &fi.n)
 	}
+
+	if newline {
+		io.write_byte(w, '\n', &fi.n)
+	}
 	if flush {
 		io.flush(w)
 	}
 
 	return fi.n
 }
+// Formats and writes to an io.Writer according to the specified format string, followed by a newline.
+//
+// Inputs:
+// - w: The io.Writer to write to.
+// - args: A variadic list of arguments to be formatted.
+//
+// Returns: The number of bytes written.
+//
+wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int {
+	return wprintf(w, format, ..args, flush=flush, newline=true)
+}
 // Writes a ^runtime.Type_Info value to an io.Writer
 //
 // Inputs:
@@ -1408,34 +1505,9 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
 //
 // Returns: The string representation of the enum value and a boolean indicating success.
 //
+@(require_results)
 enum_value_to_string :: proc(val: any) -> (string, bool) {
-	v := val
-	v.id = runtime.typeid_base(v.id)
-	type_info := type_info_of(v.id)
-
-	#partial switch e in type_info.variant {
-	case: return "", false
-	case runtime.Type_Info_Enum:
-		Enum_Value :: runtime.Type_Info_Enum_Value
-
-		ev_, ok := reflect.as_i64(val)
-		ev := Enum_Value(ev_)
-
-		if ok {
-			if len(e.values) == 0 {
-				return "", true
-			} else {
-				for val, idx in e.values {
-					if val == ev {
-						return e.names[idx], true
-					}
-				}
-			}
-			return "", false
-		}
-	}
-
-	return "", false
+	return reflect.enum_name_from_value_any(val)
 }
 // Returns the enum value of a string representation.
 //
@@ -2198,6 +2270,8 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
 	#partial switch b in info.base.variant {
 	case runtime.Type_Info_Struct:
 		fmt_struct(fi, v, verb, b, info.name)
+	case runtime.Type_Info_Bit_Field:
+		fmt_bit_field(fi, v, verb, b, info.name)
 	case runtime.Type_Info_Bit_Set:
 		fmt_bit_set(fi, v, verb = verb)
 	case:
@@ -2308,6 +2382,96 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
 		fmt_write_indent(fi)
 	}
 }
+
+fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit_Field, type_name: string) {
+	read_bits :: proc(ptr: [^]byte, offset, size: uintptr) -> (res: u64) {
+		for i in 0..<size {
+			j := i+offset
+			B := ptr[j/8]
+			k := j&7
+			if B & (u8(1)<<k) != 0 {
+				res |= u64(1)<<u64(i)
+			}
+		}
+		return
+	}
+
+	handle_bit_field_tag :: proc(data: rawptr, info: reflect.Type_Info_Bit_Field, idx: int, verb: ^rune) -> (do_continue: bool) {
+		tag := info.tags[idx]
+		if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
+			value := strings.trim_space(string(vt))
+			switch value {
+			case "": return false
+			case "-": return true
+			}
+			r, w := utf8.decode_rune_in_string(value)
+			value = value[w:]
+			if value == "" || value[0] == ',' {
+				verb^ = r
+			}
+		}
+		return false
+	}
+
+	io.write_string(fi.writer, type_name if len(type_name) != 0 else "bit_field", &fi.n)
+	io.write_string(fi.writer, "{", &fi.n)
+
+	hash   := fi.hash;   defer fi.hash = hash
+	indent := fi.indent; defer fi.indent -= 1
+	do_trailing_comma := hash
+
+	fi.indent += 1
+
+	if hash	{
+		io.write_byte(fi.writer, '\n', &fi.n)
+	}
+	defer {
+		if hash {
+			for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
+		}
+		io.write_byte(fi.writer, '}', &fi.n)
+	}
+
+
+	field_count := -1
+	for name, i in info.names {
+		field_verb := verb
+		if handle_bit_field_tag(v.data, info, i, &field_verb) {
+			continue
+		}
+
+		field_count += 1
+
+		if !do_trailing_comma && field_count > 0 {
+			io.write_string(fi.writer, ", ")
+		}
+		if hash {
+			fmt_write_indent(fi)
+		}
+
+		io.write_string(fi.writer, name, &fi.n)
+		io.write_string(fi.writer, " = ", &fi.n)
+
+		bit_offset := info.bit_offsets[i]
+		bit_size := info.bit_sizes[i]
+
+		value := read_bits(([^]byte)(v.data), bit_offset, bit_size)
+		type := info.types[i]
+
+		if !reflect.is_unsigned(runtime.type_info_core(type)) {
+			// Sign Extension
+			m := u64(1<<(bit_size-1))
+			value = (value ~ m) - m
+		}
+
+		fmt_value(fi, any{&value, type.id}, field_verb)
+		if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
+
+	}
+}
+
+
+
 // Formats a value based on its type and formatting verb
 //
 // Inputs:
@@ -2636,6 +2800,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 
 	case runtime.Type_Info_Matrix:
 		fmt_matrix(fi, v, verb, info)
+
+	case runtime.Type_Info_Bit_Field:
+		fmt_bit_field(fi, v, verb, info, "")
 	}
 }
 // Formats a complex number based on the given formatting verb

+ 16 - 8
core/fmt/fmt_os.odin

@@ -30,7 +30,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
 	return wprintln(w, ..args, sep=sep, flush=flush)
 }
 // fprintf formats according to the specified format string and writes to fd
-fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
+fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -38,7 +38,11 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
 	bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
 
 	w := bufio.writer_to_writer(&b)
-	return wprintf(w, fmt, ..args, flush=flush)
+	return wprintf(w, fmt, ..args, flush=flush, newline=newline)
+}
+// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
+fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
+	return fprintf(fd, fmt, ..args, flush=flush, newline=true)
 }
 fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
 	buf: [1024]byte
@@ -62,15 +66,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err:
 }
 
 // print formats using the default print settings and writes to os.stdout
-print   :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
+print    :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
 // println formats using the default print settings and writes to os.stdout
-println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
+println  :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
 // printf formats according to the specified format string and writes to os.stdout
-printf  :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
+printf   :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
+// printfln formats according to the specified format string and writes to os.stdout, followed by a newline.
+printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) }
 
 // eprint formats using the default print settings and writes to os.stderr
-eprint   :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
+eprint    :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
 // eprintln formats using the default print settings and writes to os.stderr
-eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
+eprintln  :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
 // eprintf formats according to the specified format string and writes to os.stderr
-eprintf  :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
+eprintf   :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
+// eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline.
+eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) }

+ 4 - 2
core/net/errors_darwin.odin

@@ -34,7 +34,7 @@ Create_Socket_Error :: enum c.int {
 
 Dial_Error :: enum c.int {
 	None                      = 0,
-	Port_Required             = -1,
+	Port_Required             = -1, // Attempted to dial an endpointing without a port being set.
 
 	Address_In_Use            = c.int(os.EADDRINUSE),
 	In_Progress               = c.int(os.EINPROGRESS),
@@ -54,7 +54,9 @@ Dial_Error :: enum c.int {
 }
 
 Bind_Error :: enum c.int {
-	None                    = 0,
+	None                         = 0,
+	Privileged_Port_Without_Root = -1, // Attempted to bind to a port less than 1024 without root access.
+
 	Address_In_Use          = c.int(os.EADDRINUSE),    // Another application is currently bound to this endpoint.
 	Given_Nonlocal_Address  = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine.
 	Broadcast_Disabled      = c.int(os.EACCES),        // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.

+ 8 - 1
core/net/socket_darwin.odin

@@ -92,13 +92,20 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
 	return
 }
 
+// On Darwin, any port below 1024 is 'privileged' - which means that you need root access in order to use it.
+MAX_PRIVILEGED_PORT :: 1023
+
 @(private)
 _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
 	sockaddr := _endpoint_to_sockaddr(ep)
 	s := any_socket_to_socket(skt)
 	res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	if res != os.ERROR_NONE {
-		err = Bind_Error(res)
+		if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
+			err = .Privileged_Port_Without_Root
+		} else {
+			err = Bind_Error(res)
+		}
 	}
 	return
 }

+ 7 - 2
core/odin/doc-format/doc_format.odin

@@ -10,8 +10,8 @@ Array :: struct($T: typeid) {
 String :: distinct Array(byte)
 
 Version_Type_Major :: 0
-Version_Type_Minor :: 2
-Version_Type_Patch :: 4
+Version_Type_Minor :: 3
+Version_Type_Patch :: 0
 
 Version_Type :: struct {
 	major, minor, patch: u8,
@@ -110,6 +110,8 @@ Entity_Flag :: enum u32le {
 	Param_No_Alias  = 7, // #no_alias
 	Param_Any_Int   = 8, // #any_int
 
+	Bit_Field_Field = 19,
+
 	Type_Alias = 20,
 
 	Builtin_Pkg_Builtin    = 30,
@@ -137,6 +139,7 @@ Entity :: struct {
 	// May be used by (Struct fields and procedure fields):
 	// .Variable
 	// .Constant
+	// This is equal to the negative of the "bit size" it this is a `bit_field`s field
 	field_group_index: i32le,
 
 	// May used by:
@@ -187,6 +190,7 @@ Type_Kind :: enum u32le {
 	Multi_Pointer          = 22,
 	Matrix                 = 23,
 	Soa_Pointer            = 24,
+	Bit_Field              = 25,
 }
 
 Type_Elems_Cap :: 4
@@ -247,6 +251,7 @@ Type :: struct {
 	// .Multi_Pointer      - 1 type:    0=element
 	// .Matrix             - 1 type:    0=element
 	// .Soa_Pointer        - 1 type:    0=element
+	// .Bit_Field          - 1 type:    0=backing type
 	types: Array(Type_Index),
 
 	// Used by:

+ 2 - 0
core/odin/tokenizer/token.odin

@@ -137,6 +137,7 @@ Token_Kind :: enum u32 {
 		Union,       // union
 		Enum,        // enum
 		Bit_Set,     // bit_set
+		Bit_Field,   // bit_field
 		Map,         // map
 		Dynamic,     // dynamic
 		Auto_Cast,   // auto_cast
@@ -270,6 +271,7 @@ tokens := [Token_Kind.COUNT]string {
 	"union",
 	"enum",
 	"bit_set",
+	"bit_field",
 	"map",
 	"dynamic",
 	"auto_cast",

+ 2 - 5
core/os/os.odin

@@ -1,7 +1,6 @@
 package os
 
 import "base:runtime"
-import "core:mem"
 import "core:strconv"
 import "core:unicode/utf8"
 
@@ -160,13 +159,11 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
 }
 
 write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
-	s := transmute([]byte)mem.Raw_Slice{data, len}
-	return write(fd, s)
+	return write(fd, ([^]byte)(data)[:len])
 }
 
 read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
-	s := transmute([]byte)mem.Raw_Slice{data, len}
-	return read(fd, s)
+	return read(fd, ([^]byte)(data)[:len])
 }
 
 heap_allocator_proc :: runtime.heap_allocator_proc

+ 2 - 5
core/os/os2/file_util.odin

@@ -1,6 +1,5 @@
 package os2
 
-import "core:mem"
 import "base:runtime"
 import "core:strconv"
 import "core:unicode/utf8"
@@ -64,13 +63,11 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
 
 
 write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
-	s := transmute([]byte)mem.Raw_Slice{data, len}
-	return write(f, s)
+	return write(f, ([^]byte)(data)[:len])
 }
 
 read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
-	s := transmute([]byte)mem.Raw_Slice{data, len}
-	return read(f, s)
+	return read(f, ([^]byte)(data)[:len])
 }
 
 

+ 47 - 0
core/reflect/reflect.odin

@@ -35,6 +35,7 @@ Type_Info_Relative_Pointer       :: runtime.Type_Info_Relative_Pointer
 Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
 Type_Info_Matrix                 :: runtime.Type_Info_Matrix
 Type_Info_Soa_Pointer            :: runtime.Type_Info_Soa_Pointer
+Type_Info_Bit_Field              :: runtime.Type_Info_Bit_Field
 
 Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
 
@@ -70,6 +71,7 @@ Type_Kind :: enum {
 	Relative_Multi_Pointer,
 	Matrix,
 	Soa_Pointer,
+	Bit_Field,
 }
 
 
@@ -106,6 +108,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
 		case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
 		case Type_Info_Matrix:                 return .Matrix
 		case Type_Info_Soa_Pointer:            return .Soa_Pointer
+		case Type_Info_Bit_Field:              return .Bit_Field
 		}
 
 	}
@@ -627,6 +630,43 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info
 	return
 }
 
+@(require_results)
+enum_name_from_value :: proc(value: $Enum_Type) -> (name: string, ok: bool) where intrinsics.type_is_enum(Enum_Type) {
+	ti := type_info_base(type_info_of(Enum_Type))
+	e := ti.variant.(runtime.Type_Info_Enum) or_return
+	if len(e.values) == 0 {
+		return
+	}
+	ev := Type_Info_Enum_Value(value)
+	for val, idx in e.values {
+		if val == ev {
+			return e.names[idx], true
+		}
+	}
+	return
+}
+
+@(require_results)
+enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
+	if value.id == nil {
+		return
+	}
+	ti := type_info_base(type_info_of(value.id))
+	e := ti.variant.(runtime.Type_Info_Enum) or_return
+	if len(e.values) == 0 {
+		return
+	}
+	ev := Type_Info_Enum_Value(as_i64(value) or_return)
+	for val, idx in e.values {
+		if val == ev {
+			return e.names[idx], true
+		}
+	}
+	return
+}
+
+
+
 
 @(require_results)
 enum_field_names :: proc(Enum_Type: typeid) -> []string {
@@ -1567,6 +1607,13 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 			}	
 		}
 		return true
+
+	case Type_Info_Bit_Field:
+		x, y := a, b
+		x.id = v.backing_type.id
+		y.id = v.backing_type.id
+		return equal(x, y, including_indirect_array_recursion, recursion_level+0)
+
 	}
 	
 	runtime.print_typeid(a.id)

+ 31 - 0
core/reflect/types.odin

@@ -174,6 +174,23 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
 		if x.row_count != y.row_count { return false }
 		if x.column_count != y.column_count { return false }
 		return are_types_identical(x.elem, y.elem)
+
+	case Type_Info_Bit_Field:
+		y := b.variant.(Type_Info_Bit_Field) or_return
+		if !are_types_identical(x.backing_type, y.backing_type) { return false }
+		if len(x.names) != len(y.names) { return false }
+		for _, i in x.names {
+			if x.names[i] != y.names[i] {
+				return false
+			}
+			if !are_types_identical(x.types[i], y.types[i]) {
+				return false
+			}
+			if x.bit_sizes[i] != y.bit_sizes[i] {
+				return false
+			}
+		}
+		return true
 	}
 
 	return false
@@ -639,6 +656,20 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
 		}
 		io.write_byte(w, ']', &n) or_return
 
+	case Type_Info_Bit_Field:
+		io.write_string(w, "bit_field ", &n) or_return
+		write_type(w, info.backing_type, &n) or_return
+		io.write_string(w, " {",         &n) or_return
+		for name, i in info.names {
+			if i > 0 { io.write_string(w, ", ", &n) or_return }
+			io.write_string(w, name,     &n) or_return
+			io.write_string(w, ": ",     &n) or_return
+			write_type(w, info.types[i], &n) or_return
+			io.write_string(w, " | ",    &n) or_return
+			io.write_u64(w, u64(info.bit_sizes[i]), 10, &n) or_return
+		}
+		io.write_string(w, "}", &n) or_return
+
 	case Type_Info_Simd_Vector:
 		io.write_string(w, "#simd[",         &n) or_return
 		io.write_i64(w, i64(info.count), 10, &n) or_return

+ 489 - 0
core/sync/chan/chan.odin

@@ -0,0 +1,489 @@
+package sync_chan
+
+import "base:builtin"
+import "base:intrinsics"
+import "base:runtime"
+import "core:mem"
+import "core:sync"
+import "core:math/rand"
+
+Direction :: enum {
+	Send = -1,
+	Both =  0,
+	Recv = +1,
+}
+
+Chan :: struct($T: typeid, $D: Direction = Direction.Both) {
+	#subtype impl: ^Raw_Chan `fmt:"-"`,
+}
+
+Raw_Chan :: struct {
+	// Shared
+	allocator:       runtime.Allocator,
+	allocation_size: int,
+	msg_size:        u16,
+	closed:          b16, // atomic
+	mutex:           sync.Mutex,
+	r_cond:          sync.Cond,
+	w_cond:          sync.Cond,
+	r_waiting:       int,  // atomic
+	w_waiting:       int,  // atomic
+
+	// Buffered
+	queue: ^Raw_Queue,
+
+	// Unbuffered
+	r_mutex:         sync.Mutex,
+	w_mutex:         sync.Mutex,
+	unbuffered_data: rawptr,
+}
+
+
+create :: proc{
+	create_unbuffered,
+	create_buffered,
+}
+
+@(require_results)
+create_unbuffered :: proc($C: typeid/Chan($T), allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)
+	where size_of(T) <= int(max(u16)) {
+	c.impl, err = create_raw_unbuffered(size_of(T), align_of(T), allocator)
+	return
+}
+
+@(require_results)
+create_buffered :: proc($C: typeid/Chan($T), #any_int cap: int, allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)
+	where size_of(T) <= int(max(u16)) {
+	c.impl, err = create_raw_buffered(size_of(T), align_of(T), cap, allocator)
+	return
+}
+
+create_raw :: proc{
+	create_raw_unbuffered,
+	create_raw_buffered,
+}
+
+@(require_results)
+create_raw_unbuffered :: proc(#any_int msg_size, msg_alignment: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) {
+	assert(msg_size <= int(max(u16)))
+	align := max(align_of(Raw_Chan), msg_alignment)
+
+	size := mem.align_forward_int(size_of(Raw_Chan), align)
+	offset := size
+	size += msg_size
+	size = mem.align_forward_int(size, align)
+
+	ptr := mem.alloc(size, align, allocator) or_return
+	c = (^Raw_Chan)(ptr)
+	c.allocation_size = size
+	c.unbuffered_data = ([^]byte)(ptr)[offset:]
+	c.msg_size = u16(msg_size)
+	return
+}
+
+@(require_results)
+create_raw_buffered :: proc(#any_int msg_size, msg_alignment: int, #any_int cap: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) {
+	assert(msg_size <= int(max(u16)))
+	if cap <= 0 {
+		return create_raw_unbuffered(msg_size, msg_alignment, allocator)
+	}
+
+	align := max(align_of(Raw_Chan), msg_alignment, align_of(Raw_Queue))
+
+	size := mem.align_forward_int(size_of(Raw_Chan), align)
+	q_offset := size
+	size = mem.align_forward_int(q_offset + size_of(Raw_Queue), msg_alignment)
+	offset := size
+	size += msg_size * cap
+	size = mem.align_forward_int(size, align)
+
+	ptr := mem.alloc(size, align, allocator) or_return
+	c = (^Raw_Chan)(ptr)
+	c.allocation_size = size
+
+	bptr := ([^]byte)(ptr)
+
+	c.queue = (^Raw_Queue)(bptr[q_offset:])
+	c.msg_size = u16(msg_size)
+
+	raw_queue_init(c.queue, ([^]byte)(bptr[offset:]), cap, msg_size)
+	return
+}
+
+destroy :: proc(c: ^Raw_Chan) -> (err: runtime.Allocator_Error) {
+	if c != nil {
+		allocator := c.allocator
+		err = mem.free_with_size(c, c.allocation_size, allocator)
+	}
+	return
+}
+
+@(require_results)
+as_send :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (s: Chan(T, .Send)) where C.D <= .Both {
+	return transmute(type_of(s))c
+}
+@(require_results)
+as_recv :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (r: Chan(T, .Recv)) where C.D >= .Both {
+	return transmute(type_of(r))c
+}
+
+
+send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both {
+	data := data
+	ok = send_raw(c, &data)
+	return
+}
+
+@(require_results)
+try_send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both {
+	data := data
+	ok = try_send_raw(c, &data)
+	return
+}
+
+@(require_results)
+recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both {
+	ok = recv_raw(c, &data)
+	return
+}
+
+
+@(require_results)
+try_recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both {
+	ok = try_recv_raw(c, &data)
+	return
+}
+
+
+@(require_results)
+send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
+	if c == nil {
+		return
+	}
+	if c.queue != nil { // buffered
+		sync.guard(&c.mutex)
+		for c.queue.len == c.queue.cap {
+			sync.atomic_add(&c.w_waiting, 1)
+			sync.wait(&c.w_cond, &c.mutex)
+			sync.atomic_sub(&c.w_waiting, 1)
+		}
+
+		ok = raw_queue_push(c.queue, msg_in)
+		if sync.atomic_load(&c.r_waiting) > 0 {
+			sync.signal(&c.r_cond)
+		}
+	} else if c.unbuffered_data != nil { // unbuffered
+		sync.guard(&c.w_mutex)
+		sync.guard(&c.mutex)
+
+		if sync.atomic_load(&c.closed) {
+			return false
+		}
+
+		mem.copy(c.unbuffered_data, msg_in, int(c.msg_size))
+		sync.atomic_add(&c.w_waiting, 1)
+		if sync.atomic_load(&c.r_waiting) > 0 {
+			sync.signal(&c.r_cond)
+		}
+		sync.wait(&c.w_cond, &c.mutex)
+		ok = true
+	}
+	return
+}
+
+@(require_results)
+recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) {
+	if c == nil {
+		return
+	}
+	if c.queue != nil { // buffered
+		sync.guard(&c.mutex)
+		for c.queue.len == 0 {
+			if sync.atomic_load(&c.closed) {
+				return
+			}
+
+			sync.atomic_add(&c.r_waiting, 1)
+			sync.wait(&c.r_cond, &c.mutex)
+			sync.atomic_sub(&c.r_waiting, 1)
+		}
+
+		msg := raw_queue_pop(c.queue)
+		if msg != nil {
+			mem.copy(msg_out, msg, int(c.msg_size))
+		}
+
+		if sync.atomic_load(&c.w_waiting) > 0 {
+			sync.signal(&c.w_cond)
+		}
+		ok = true
+	} else if c.unbuffered_data != nil { // unbuffered
+		sync.guard(&c.r_mutex)
+		sync.guard(&c.mutex)
+
+		for !sync.atomic_load(&c.closed) &&
+		    sync.atomic_load(&c.w_waiting) == 0 {
+			sync.atomic_add(&c.r_waiting, 1)
+			sync.wait(&c.r_cond, &c.mutex)
+			sync.atomic_sub(&c.r_waiting, 1)
+		}
+
+		if sync.atomic_load(&c.closed) {
+			return
+		}
+
+		mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
+		sync.atomic_sub(&c.w_waiting, 1)
+
+		sync.signal(&c.w_cond)
+		ok = true
+	}
+	return
+}
+
+
+@(require_results)
+try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
+	if c == nil {
+		return false
+	}
+	if c.queue != nil { // buffered
+		sync.guard(&c.mutex)
+		if c.queue.len == c.queue.cap {
+			return false
+		}
+
+		ok = raw_queue_push(c.queue, msg_in)
+		if sync.atomic_load(&c.r_waiting) > 0 {
+			sync.signal(&c.r_cond)
+		}
+	} else if c.unbuffered_data != nil { // unbuffered
+		sync.guard(&c.w_mutex)
+		sync.guard(&c.mutex)
+
+		if sync.atomic_load(&c.closed) {
+			return false
+		}
+
+		mem.copy(c.unbuffered_data, msg_in, int(c.msg_size))
+		sync.atomic_add(&c.w_waiting, 1)
+		if sync.atomic_load(&c.r_waiting) > 0 {
+			sync.signal(&c.r_cond)
+		}
+		sync.wait(&c.w_cond, &c.mutex)
+		ok = true
+	}
+	return
+}
+
+@(require_results)
+try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
+	if c == nil {
+		return false
+	}
+	if c.queue != nil { // buffered
+		sync.guard(&c.mutex)
+		if c.queue.len == 0 {
+			return false
+		}
+
+		msg := raw_queue_pop(c.queue)
+		if msg != nil {
+			mem.copy(msg_out, msg, int(c.msg_size))
+		}
+
+		if sync.atomic_load(&c.w_waiting) > 0 {
+			sync.signal(&c.w_cond)
+		}
+		return true
+	} else if c.unbuffered_data != nil { // unbuffered
+		sync.guard(&c.r_mutex)
+		sync.guard(&c.mutex)
+
+		if sync.atomic_load(&c.closed) ||
+		   sync.atomic_load(&c.w_waiting) == 0 {
+		   	return false
+		}
+
+		mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
+		sync.atomic_sub(&c.w_waiting, 1)
+
+		sync.signal(&c.w_cond)
+		return true
+	}
+	return false
+}
+
+
+
+@(require_results)
+is_buffered :: proc "contextless" (c: ^Raw_Chan) -> bool {
+	return c != nil && c.queue != nil
+}
+
+@(require_results)
+is_unbuffered :: proc "contextless" (c: ^Raw_Chan) -> bool {
+	return c != nil && c.unbuffered_data != nil
+}
+
+@(require_results)
+len :: proc "contextless" (c: ^Raw_Chan) -> int {
+	if c != nil && c.queue != nil {
+		sync.guard(&c.mutex)
+		return c.queue.len
+	}
+	return 0
+}
+
+@(require_results)
+cap :: proc "contextless" (c: ^Raw_Chan) -> int {
+	if c != nil && c.queue != nil {
+		sync.guard(&c.mutex)
+		return c.queue.cap
+	}
+	return 0
+}
+
+close :: proc "contextless" (c: ^Raw_Chan) -> bool {
+	if c == nil {
+		return false
+	}
+	sync.guard(&c.mutex)
+	if sync.atomic_load(&c.closed) {
+		return false
+	}
+	sync.atomic_store(&c.closed, true)
+	sync.broadcast(&c.r_cond)
+	sync.broadcast(&c.w_cond)
+	return true
+}
+
+@(require_results)
+is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool {
+	if c == nil {
+		return true
+	}
+	sync.guard(&c.mutex)
+	return bool(sync.atomic_load(&c.closed))
+}
+
+
+
+
+Raw_Queue :: struct {
+	data: [^]byte,
+	len:  int,
+	cap:  int,
+	next: int,
+	size: int, // element size
+}
+
+raw_queue_init :: proc "contextless" (q: ^Raw_Queue, data: rawptr, cap: int, size: int) {
+	q.data = ([^]byte)(data)
+	q.len  = 0
+	q.cap  = cap
+	q.next = 0
+	q.size = size
+}
+
+
+@(require_results)
+raw_queue_push :: proc "contextless" (q: ^Raw_Queue, data: rawptr) -> bool {
+	if q.len == q.cap {
+		return false
+	}
+	pos := q.next + q.len
+	if pos >= q.cap {
+		pos -= q.cap
+	}
+
+	val_ptr := q.data[pos*q.size:]
+	mem.copy(val_ptr, data, q.size)
+	q.len += 1
+	return true
+}
+
+@(require_results)
+raw_queue_pop :: proc "contextless" (q: ^Raw_Queue) -> (data: rawptr) {
+	if q.len > 0 {
+		data = q.data[q.next*q.size:]
+		q.next += 1
+		q.len -= 1
+		if q.next >= q.cap {
+			q.next -= q.cap
+		}
+	}
+	return
+}
+
+
+@(require_results)
+can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool {
+	if is_buffered(c) {
+		return len(c) > 0
+	}
+	sync.guard(&c.mutex)
+	return sync.atomic_load(&c.w_waiting) > 0
+}
+
+
+@(require_results)
+can_send :: proc "contextless" (c: ^Raw_Chan) -> bool {
+	if is_buffered(c) {
+		sync.guard(&c.mutex)
+		return len(c) < cap(c)
+	}
+	sync.guard(&c.mutex)
+	return sync.atomic_load(&c.r_waiting) > 0
+}
+
+
+
+@(require_results)
+select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, ok: bool) #no_bounds_check {
+	Select_Op :: struct {
+		idx:     int, // local to the slice that was given
+		is_recv: bool,
+	}
+
+	candidate_count := builtin.len(recvs)+builtin.len(sends)
+	candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op)))
+	count := 0
+
+	for c, i in recvs {
+		if can_recv(c) {
+			candidates[count] = {
+				is_recv = true,
+				idx     = i,
+			}
+			count += 1
+		}
+	}
+
+	for c, i in sends {
+		if can_send(c) {
+			candidates[count] = {
+				is_recv = false,
+				idx     = i,
+			}
+			count += 1
+		}
+	}
+
+	if count == 0 {
+		return
+	}
+
+	r: ^rand.Rand = nil
+
+
+	select_idx = rand.int_max(count, r) if count > 0 else 0
+
+	sel := candidates[select_idx]
+	if sel.is_recv {
+		ok = recv_raw(recvs[sel.idx], recv_out)
+	} else {
+		ok = send_raw(sends[sel.idx], send_msgs[sel.idx])
+	}
+	return
+}

+ 24 - 0
core/sync/extended.odin

@@ -417,4 +417,28 @@ unpark :: proc "contextless" (p: ^Parker)  {
 	if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED {
 		futex_signal(&p.state)
 	}
+}
+
+
+
+// A One_Shot_Event is an associated token which is initially not present:
+//     * The `one_shot_event_wait` blocks the current thread until the event
+//       is made available
+//     * The `one_shot_event_signal` procedure automatically makes the token
+//       available if its was not already.
+One_Shot_Event :: struct #no_copy {
+	state: Futex,
+}
+
+// Blocks the current thread until the event is made available with `one_shot_event_signal`.
+one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
+	for atomic_load_explicit(&e.state, .Acquire) == 0 {
+		futex_wait(&e.state, 1)
+	}
+}
+
+// Releases any threads that are currently blocked by this event with `one_shot_event_wait`.
+one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) {
+	atomic_store_explicit(&e.state, 1, .Release)
+	futex_broadcast(&e.state)
 }

+ 8 - 0
core/time/time.odin

@@ -369,6 +369,10 @@ datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second:
 		mod = year % divisor
 		return
 	}
+	_is_leap_year :: proc "contextless" (year: int) -> bool {
+		return year%4 == 0 && (year%100 != 0 || year%400 == 0)
+	}
+
 
 	ok = true
 
@@ -395,6 +399,10 @@ datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second:
 
 	days += int(days_before[_m]) + _d
 
+	if _is_leap_year(year) && _m >= 2 {
+		days += 1
+	}
+
 	s += i64(days)   * SECONDS_PER_DAY
 	s += i64(hour)   * SECONDS_PER_HOUR
 	s += i64(minute) * SECONDS_PER_MINUTE

+ 2 - 0
examples/all/all_main.odin

@@ -14,6 +14,7 @@ import shoco            "core:compress/shoco"
 import gzip             "core:compress/gzip"
 import zlib             "core:compress/zlib"
 
+import avl              "core:container/avl"
 import bit_array        "core:container/bit_array"
 import priority_queue   "core:container/priority_queue"
 import queue            "core:container/queue"
@@ -131,6 +132,7 @@ _ :: compress
 _ :: shoco
 _ :: gzip
 _ :: zlib
+_ :: avl
 _ :: bit_array
 _ :: priority_queue
 _ :: queue

+ 1067 - 1061
src/bug_report.cpp

@@ -1,1061 +1,1067 @@
-/*
-	Gather and print platform and version info to help with reporting Odin bugs.
-*/
-
-#if !defined(GB_COMPILER_MSVC)
-	#if defined(GB_CPU_X86)
-		#include <cpuid.h>
-	#endif
-#endif
-
-#if defined(GB_SYSTEM_LINUX)
-	#include <sys/utsname.h>
-	#include <sys/sysinfo.h>
-#endif
-
-#if defined(GB_SYSTEM_OSX)
-	#include <sys/sysctl.h>
-#endif
-
-#if defined(GB_SYSTEM_OPENBSD)
-	#include <sys/sysctl.h>
-	#include <sys/utsname.h>
-#endif
-
-#if defined(GB_SYSTEM_FREEBSD)
-	#include <sys/sysctl.h>
-#endif
-
-/*
-	NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
-*/
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal void report_windows_product_type(DWORD ProductType) {
-	switch (ProductType) {
-	case PRODUCT_ULTIMATE:
-		gb_printf("Ultimate");
-		break;
-
-	case PRODUCT_HOME_BASIC:
-		gb_printf("Home Basic");
-		break;
-
-	case PRODUCT_HOME_PREMIUM:
-		gb_printf("Home Premium");
-		break;
-
-	case PRODUCT_ENTERPRISE:
-		gb_printf("Enterprise");
-		break;
-
-	case PRODUCT_CORE:
-		gb_printf("Home Basic");
-		break;
-
-	case PRODUCT_HOME_BASIC_N:
-		gb_printf("Home Basic N");
-		break;
-
-	case PRODUCT_EDUCATION:
-		gb_printf("Education");
-		break;
-
-	case PRODUCT_EDUCATION_N:
-		gb_printf("Education N");
-		break;
-
-	case PRODUCT_BUSINESS:
-		gb_printf("Business");
-		break;
-
-	case PRODUCT_STANDARD_SERVER:
-		gb_printf("Standard Server");
-		break;
-
-	case PRODUCT_DATACENTER_SERVER:
-		gb_printf("Datacenter");
-		break;
-
-	case PRODUCT_SMALLBUSINESS_SERVER:
-		gb_printf("Windows Small Business Server");
-		break;
-
-	case PRODUCT_ENTERPRISE_SERVER:
-		gb_printf("Enterprise Server");
-		break;
-
-	case PRODUCT_STARTER:
-		gb_printf("Starter");
-		break;
-
-	case PRODUCT_DATACENTER_SERVER_CORE:
-		gb_printf("Datacenter Server Core");
-		break;
-
-	case PRODUCT_STANDARD_SERVER_CORE:
-		gb_printf("Server Standard Core");
-		break;
-
-	case PRODUCT_ENTERPRISE_SERVER_CORE:
-		gb_printf("Enterprise Server Core");
-		break;
-
-	case PRODUCT_BUSINESS_N:
-		gb_printf("Business N");
-		break;
-
-	case PRODUCT_HOME_SERVER:
-		gb_printf("Home Server");
-		break;
-
-	case PRODUCT_SERVER_FOR_SMALLBUSINESS:
-		gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
-		break;
-
-	case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
-		gb_printf("Small Business Server Premium");
-		break;
-
-	case PRODUCT_HOME_PREMIUM_N:
-		gb_printf("Home Premium N");
-		break;
-
-	case PRODUCT_ENTERPRISE_N:
-		gb_printf("Enterprise N");
-		break;
-
-	case PRODUCT_ULTIMATE_N:
-		gb_printf("Ultimate N");
-		break;
-
-	case PRODUCT_HYPERV:
-		gb_printf("HyperV");
-		break;
-
-	case PRODUCT_STARTER_N:
-		gb_printf("Starter N");
-		break;
-
-	case PRODUCT_PROFESSIONAL:
-		gb_printf("Professional");
-		break;
-
-	case PRODUCT_PROFESSIONAL_N:
-		gb_printf("Professional N");
-		break;
-
-	case PRODUCT_UNLICENSED:
-		gb_printf("Unlicensed");
-		break;
-
-	default:
-		gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
-	}
-}
-#endif
-
-gb_internal void odin_cpuid(int leaf, int result[]) {
-	#if defined(GB_CPU_ARM)
-		return;
-
-	#elif defined(GB_CPU_X86)
-	
-		#if defined(GB_COMPILER_MSVC)
-			__cpuid(result, leaf);
-		#else
-			__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
-		#endif
-
-	#endif
-}
-
-gb_internal void report_cpu_info() {
-	gb_printf("\tCPU:  ");
-
-	#if defined(GB_CPU_X86)
-
-	/*
-		Get extended leaf info
-	*/
-	int cpu[4];
-
-	odin_cpuid(0x80000000, &cpu[0]);
-	int number_of_extended_ids = cpu[0];
-
-	int brand[0x12] = {};
-
-	/*
-		Read CPU brand if supported.
-	*/
-	if (number_of_extended_ids >= 0x80000004) {
-		odin_cpuid(0x80000002, &brand[0]);
-		odin_cpuid(0x80000003, &brand[4]);
-		odin_cpuid(0x80000004, &brand[8]);
-
-		/*
-			Some CPUs like `      Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
-		*/
-		char * brand_name = (char *)&brand[0];
-		for (; brand_name[0] == ' '; brand_name++) {}
-
-		gb_printf("%s\n", brand_name);
-	} else {
-		gb_printf("Unable to retrieve.\n");
-	}
-
-	#elif defined(GB_CPU_ARM)
-		/*
-			TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
-		*/
-		#if defined(GB_ARCH_64_BIT)
-			gb_printf("ARM64\n");
-		#else
-			gb_printf("ARM\n");
-		#endif
-	#else
-		gb_printf("Unknown\n");
-	#endif
-}
-
-/*
-	Report the amount of installed RAM.
-*/
-gb_internal void report_ram_info() {
-	gb_printf("\tRAM:  ");
-
-	#if defined(GB_SYSTEM_WINDOWS)
-		MEMORYSTATUSEX statex;
-		statex.dwLength = sizeof(statex);
-		GlobalMemoryStatusEx (&statex);
-
-		gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
-
-	#elif defined(GB_SYSTEM_LINUX)
-		/*
-			Retrieve RAM info using `sysinfo()`, 
-		*/
-		struct sysinfo info;
-		int result = sysinfo(&info);
-
-		if (result == 0x0) {
-			gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1));
-		} else {
-			gb_printf("Unknown.\n");
-		}
-	#elif defined(GB_SYSTEM_OSX)
-		uint64_t ram_amount;
-		size_t   val_size = sizeof(ram_amount);
-
-		int mibs[] = { CTL_HW, HW_MEMSIZE };
-		if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
-			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
-		}
-	#elif defined(GB_SYSTEM_OPENBSD)
-		uint64_t ram_amount;
-		size_t   val_size = sizeof(ram_amount);
-
-		int mibs[] = { CTL_HW, HW_PHYSMEM64 };
-		if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
-			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
-		}
-	#elif defined(GB_SYSTEM_FREEBSD)
-		uint64_t ram_amount;
-		size_t   val_size = sizeof(ram_amount);
-
-		int mibs[] = { CTL_HW, HW_PHYSMEM };
-		if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
-			gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
-		}
-	#else
-		gb_printf("Unknown.\n");
-	#endif
-}
-
-gb_internal void report_os_info() {
-	gb_printf("\tOS:   ");
-
-	#if defined(GB_SYSTEM_WINDOWS)
-	/*
-		NOTE(Jeroen): 
-			`GetVersionEx`  will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
-			`RtlGetVersion` will return the true version.
-
-			Rather than include the WinDDK, we ask the kernel directly.
-
-			`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
-
-	*/
-	OSVERSIONINFOEXW osvi;
-	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
-	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
-
-	typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
-	typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
-
-	// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
-	RtlGetVersionPtr  RtlGetVersion  =  (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
-	// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
-	GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
-
-	NTSTATUS status  = {};
-	DWORD ProductType = {};
-	if (RtlGetVersion != nullptr) {
-		status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
-	}
-
-	if (RtlGetVersion == nullptr || status != 0x0) {
-		gb_printf("Windows (Unknown Version)");
-	} else {
-		if (GetProductInfo != nullptr) {
-			GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
-		}
-
-		if (false) {
-			gb_printf("dwMajorVersion:    %u\n", cast(unsigned)osvi.dwMajorVersion);
-			gb_printf("dwMinorVersion:    %u\n", cast(unsigned)osvi.dwMinorVersion);
-			gb_printf("dwBuildNumber:     %u\n", cast(unsigned)osvi.dwBuildNumber);
-			gb_printf("dwPlatformId:      %u\n", cast(unsigned)osvi.dwPlatformId);
-			gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
-			gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
-			gb_printf("wSuiteMask:        %u\n", cast(unsigned)osvi.wSuiteMask);
-			gb_printf("wProductType:      %u\n", cast(unsigned)osvi.wProductType);
-		}
-
-		gb_printf("Windows ");
-
-		switch (osvi.dwMajorVersion) {
-		case 10:
-			/*
-				Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
-			*/
-			switch (osvi.wProductType) {
-			case VER_NT_WORKSTATION: // Workstation
-				if (osvi.dwBuildNumber < 22000) {
-					gb_printf("10 ");
-				} else {
-					gb_printf("11 ");
-				}
-				
-				report_windows_product_type(ProductType);
-
-				break;
-			default: // Server or Domain Controller
-				switch(osvi.dwBuildNumber) {
-				case 14393:
-					gb_printf("2016 Server");
-					break;
-				case 17763:
-					gb_printf("2019 Server");
-					break;
-				case 20348:
-					gb_printf("2022 Server");
-					break;
-				default:
-					gb_printf("Unknown Server");
-					break;
-				}
-			}
-			break;
-		case 6:
-			switch (osvi.dwMinorVersion) {
-				case 0:
-					switch (osvi.wProductType) {
-						case VER_NT_WORKSTATION:
-							gb_printf("Windows Vista ");
-							report_windows_product_type(ProductType);
-							break;
-						case 3:
-							gb_printf("Windows Server 2008");
-							break;
-					}
-					break;
-
-				case 1:
-					switch (osvi.wProductType) {
-						case VER_NT_WORKSTATION:
-							gb_printf("Windows 7 ");
-							report_windows_product_type(ProductType);
-							break;
-						case 3:
-							gb_printf("Windows Server 2008 R2");
-							break;
-					}
-					break;
-				case 2:
-					switch (osvi.wProductType) {
-						case VER_NT_WORKSTATION:
-							gb_printf("Windows 8 ");
-							report_windows_product_type(ProductType);
-							break;
-						case 3:
-							gb_printf("Windows Server 2012");
-							break;
-					}
-					break;
-				case 3:
-					switch (osvi.wProductType) {
-						case VER_NT_WORKSTATION:
-							gb_printf("Windows 8.1 ");
-							report_windows_product_type(ProductType);
-							break;
-						case 3:
-							gb_printf("Windows Server 2012 R2");
-							break;
-					}
-					break;
-			}
-			break;
-		case 5:
-			switch (osvi.dwMinorVersion) {
-				case 0:
-					gb_printf("Windows 2000");
-					break;
-				case 1:
-					gb_printf("Windows XP");
-					break;
-				case 2:
-					gb_printf("Windows Server 2003");
-					break;
-			}
-			break;
-		default:
-			break;
-		}
-
-		/*
-			Grab Windows DisplayVersion (like 20H02)
-		*/
-		LPDWORD ValueType = {};
-		DWORD   UBR;
-		char    DisplayVersion[256];
-		DWORD   ValueSize = 256;
-
-		status = RegGetValue(
-			HKEY_LOCAL_MACHINE,
-			TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
-			TEXT("DisplayVersion"),
-			RRF_RT_REG_SZ,
-			ValueType,
-			DisplayVersion,
-			&ValueSize
-		);
-
-		if (status == 0x0) {
-			gb_printf(" (version: %s)", DisplayVersion);
-		}
-
-		/*
-			Now print build number.
-		*/
-		gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
-
-		ValueSize = sizeof(UBR);
-		status = RegGetValue(
-			HKEY_LOCAL_MACHINE,
-			TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
-			TEXT("UBR"),
-			RRF_RT_REG_DWORD,
-			ValueType,
-			&UBR,
-			&ValueSize
-		);
-
-		if (status == 0x0) {
-			gb_printf(".%u", cast(unsigned)UBR);
-		}
-		gb_printf("\n");
-	}
-	#elif defined(GB_SYSTEM_LINUX)
-		/*
-			Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
-		*/
-		gbAllocator a = heap_allocator();
-
-		gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
-		defer (gb_file_free_contents(&release));
-
-		b32 found = 0;
-		if (release.size) {
-			char *start        = (char *)release.data;
-			char *end          = (char *)release.data + release.size;
-			const char *needle = "PRETTY_NAME=\"";
-			isize needle_len   = gb_strlen((needle));
-		
-			char *c = start;
-			for (; c < end; c++) {
-				if (gb_strncmp(c, needle, needle_len) == 0) {
-					found = 1;
-					start = c + needle_len;
-					break;
-				}
-			}
-
-			if (found) {
-				for (c = start; c < end; c++) {
-					if (*c == '"') {
-						// Found the closing quote. Replace it with \0
-						*c = 0;
-						gb_printf("%s", (char *)start);
-						break;
-					} else if (*c == '\n') {
-						found = 0;
-					}
-				}
-			}
-		}
-
-		if (!found) {
-			gb_printf("Unknown Linux Distro");
-		}
-
-		/*
-			Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
-		*/
-		char buffer[1024];
-		uname((struct utsname *)&buffer[0]);
-
-		struct utsname *info;
-		info = (struct utsname *)&buffer[0];
-
-		gb_printf(", %s %s\n", info->sysname, info->release);
-
-	#elif defined(GB_SYSTEM_OSX)
-		struct Darwin_To_Release {
-			const char* build;    // 21G83
-			int   darwin[3];      // Darwin kernel triplet
-			const char* os_name;  // OS X, MacOS
-			struct {
-				const char* name; // Monterey, Mojave, etc.
-				int version[3];   // 12.4, etc.
-			} release;
-		};
-
-		Darwin_To_Release macos_release_map[] = {
-			{"8A428",    { 8,  0,  0}, "macOS", {"Tiger",         {10,  4,  0}}},
-			{"8A432",    { 8,  0,  0}, "macOS", {"Tiger",         {10,  4,  0}}},
-			{"8B15",     { 8,  1,  0}, "macOS", {"Tiger",         {10,  4,  1}}},
-			{"8B17",     { 8,  1,  0}, "macOS", {"Tiger",         {10,  4,  1}}},
-			{"8C46",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
-			{"8C47",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
-			{"8E102",    { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
-			{"8E45",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
-			{"8E90",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
-			{"8F46",     { 8,  3,  0}, "macOS", {"Tiger",         {10,  4,  3}}},
-			{"8G32",     { 8,  4,  0}, "macOS", {"Tiger",         {10,  4,  4}}},
-			{"8G1165",   { 8,  4,  0}, "macOS", {"Tiger",         {10,  4,  4}}},
-			{"8H14",     { 8,  5,  0}, "macOS", {"Tiger",         {10,  4,  5}}},
-			{"8G1454",   { 8,  5,  0}, "macOS", {"Tiger",         {10,  4,  5}}},
-			{"8I127",    { 8,  6,  0}, "macOS", {"Tiger",         {10,  4,  6}}},
-			{"8I1119",   { 8,  6,  0}, "macOS", {"Tiger",         {10,  4,  6}}},
-			{"8J135",    { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
-			{"8J2135a",  { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
-			{"8K1079",   { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
-			{"8N5107",   { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
-			{"8L127",    { 8,  8,  0}, "macOS", {"Tiger",         {10,  4,  8}}},
-			{"8L2127",   { 8,  8,  0}, "macOS", {"Tiger",         {10,  4,  8}}},
-			{"8P135",    { 8,  9,  0}, "macOS", {"Tiger",         {10,  4,  9}}},
-			{"8P2137",   { 8,  9,  0}, "macOS", {"Tiger",         {10,  4,  9}}},
-			{"8R218",    { 8, 10,  0}, "macOS", {"Tiger",         {10,  4, 10}}},
-			{"8R2218",   { 8, 10,  0}, "macOS", {"Tiger",         {10,  4, 10}}},
-			{"8R2232",   { 8, 10,  0}, "macOS", {"Tiger",         {10,  4, 10}}},
-			{"8S165",    { 8, 11,  0}, "macOS", {"Tiger",         {10,  4, 11}}},
-			{"8S2167",   { 8, 11,  0}, "macOS", {"Tiger",         {10,  4, 11}}},
-			{"9A581",    { 9,  0,  0}, "macOS", {"Leopard",       {10,  5,  0}}},
-			{"9B18",     { 9,  1,  0}, "macOS", {"Leopard",       {10,  5,  1}}},
-			{"9B2117",   { 9,  1,  1}, "macOS", {"Leopard",       {10,  5,  1}}},
-			{"9C31",     { 9,  2,  0}, "macOS", {"Leopard",       {10,  5,  2}}},
-			{"9C7010",   { 9,  2,  0}, "macOS", {"Leopard",       {10,  5,  2}}},
-			{"9D34",     { 9,  3,  0}, "macOS", {"Leopard",       {10,  5,  3}}},
-			{"9E17",     { 9,  4,  0}, "macOS", {"Leopard",       {10,  5,  4}}},
-			{"9F33",     { 9,  5,  0}, "macOS", {"Leopard",       {10,  5,  5}}},
-			{"9G55",     { 9,  6,  0}, "macOS", {"Leopard",       {10,  5,  6}}},
-			{"9G66",     { 9,  6,  0}, "macOS", {"Leopard",       {10,  5,  6}}},
-			{"9G71",     { 9,  6,  0}, "macOS", {"Leopard",       {10,  5,  6}}},
-			{"9J61",     { 9,  7,  0}, "macOS", {"Leopard",       {10,  5,  7}}},
-			{"9L30",     { 9,  8,  0}, "macOS", {"Leopard",       {10,  5,  8}}},
-			{"9L34",     { 9,  8,  0}, "macOS", {"Leopard",       {10,  5,  8}}},
-			{"10A432",   {10,  0,  0}, "macOS", {"Snow Leopard",  {10,  6,  0}}},
-			{"10A433",   {10,  0,  0}, "macOS", {"Snow Leopard",  {10,  6,  0}}},
-			{"10B504",   {10,  1,  0}, "macOS", {"Snow Leopard",  {10,  6,  1}}},
-			{"10C540",   {10,  2,  0}, "macOS", {"Snow Leopard",  {10,  6,  2}}},
-			{"10D573",   {10,  3,  0}, "macOS", {"Snow Leopard",  {10,  6,  3}}},
-			{"10D575",   {10,  3,  0}, "macOS", {"Snow Leopard",  {10,  6,  3}}},
-			{"10D578",   {10,  3,  0}, "macOS", {"Snow Leopard",  {10,  6,  3}}},
-			{"10F569",   {10,  4,  0}, "macOS", {"Snow Leopard",  {10,  6,  4}}},
-			{"10H574",   {10,  5,  0}, "macOS", {"Snow Leopard",  {10,  6,  5}}},
-			{"10J567",   {10,  6,  0}, "macOS", {"Snow Leopard",  {10,  6,  6}}},
-			{"10J869",   {10,  7,  0}, "macOS", {"Snow Leopard",  {10,  6,  7}}},
-			{"10J3250",  {10,  7,  0}, "macOS", {"Snow Leopard",  {10,  6,  7}}},
-			{"10J4138",  {10,  7,  0}, "macOS", {"Snow Leopard",  {10,  6,  7}}},
-			{"10K540",   {10,  8,  0}, "macOS", {"Snow Leopard",  {10,  6,  8}}},
-			{"10K549",   {10,  8,  0}, "macOS", {"Snow Leopard",  {10,  6,  8}}},
-			{"11A511",   {11,  0,  0}, "macOS", {"Lion",          {10,  7,  0}}},
-			{"11A511s",  {11,  0,  0}, "macOS", {"Lion",          {10,  7,  0}}},
-			{"11A2061",  {11,  0,  2}, "macOS", {"Lion",          {10,  7,  0}}},
-			{"11A2063",  {11,  0,  2}, "macOS", {"Lion",          {10,  7,  0}}},
-			{"11B26",    {11,  1,  0}, "macOS", {"Lion",          {10,  7,  1}}},
-			{"11B2118",  {11,  1,  0}, "macOS", {"Lion",          {10,  7,  1}}},
-			{"11C74",    {11,  2,  0}, "macOS", {"Lion",          {10,  7,  2}}},
-			{"11D50",    {11,  3,  0}, "macOS", {"Lion",          {10,  7,  3}}},
-			{"11E53",    {11,  4,  0}, "macOS", {"Lion",          {10,  7,  4}}},
-			{"11G56",    {11,  4,  2}, "macOS", {"Lion",          {10,  7,  5}}},
-			{"11G63",    {11,  4,  2}, "macOS", {"Lion",          {10,  7,  5}}},
-			{"12A269",   {12,  0,  0}, "macOS", {"Mountain Lion", {10,  8,  0}}},
-			{"12B19",    {12,  1,  0}, "macOS", {"Mountain Lion", {10,  8,  1}}},
-			{"12C54",    {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
-			{"12C60",    {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
-			{"12C2034",  {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
-			{"12C3104",  {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
-			{"12D78",    {12,  3,  0}, "macOS", {"Mountain Lion", {10,  8,  3}}},
-			{"12E55",    {12,  4,  0}, "macOS", {"Mountain Lion", {10,  8,  4}}},
-			{"12E3067",  {12,  4,  0}, "macOS", {"Mountain Lion", {10,  8,  4}}},
-			{"12E4022",  {12,  4,  0}, "macOS", {"Mountain Lion", {10,  8,  4}}},
-			{"12F37",    {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
-			{"12F45",    {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
-			{"12F2501",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
-			{"12F2518",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
-			{"12F2542",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
-			{"12F2560",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
-			{"13A603",   {13,  0,  0}, "macOS", {"Mavericks",     {10,  9,  0}}},
-			{"13B42",    {13,  0,  0}, "macOS", {"Mavericks",     {10,  9,  1}}},
-			{"13C64",    {13,  1,  0}, "macOS", {"Mavericks",     {10,  9,  2}}},
-			{"13C1021",  {13,  1,  0}, "macOS", {"Mavericks",     {10,  9,  2}}},
-			{"13D65",    {13,  2,  0}, "macOS", {"Mavericks",     {10,  9,  3}}},
-			{"13E28",    {13,  3,  0}, "macOS", {"Mavericks",     {10,  9,  4}}},
-			{"13F34",    {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1066",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1077",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1096",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1112",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1134",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1507",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1603",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1712",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1808",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"13F1911",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
-			{"14A389",   {14,  0,  0}, "macOS", {"Yosemite",      {10, 10,  0}}},
-			{"14B25",    {14,  0,  0}, "macOS", {"Yosemite",      {10, 10,  1}}},
-			{"14C109",   {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
-			{"14C1510",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
-			{"14C2043",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
-			{"14C1514",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
-			{"14C2513",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
-			{"14D131",   {14,  3,  0}, "macOS", {"Yosemite",      {10, 10,  3}}},
-			{"14D136",   {14,  3,  0}, "macOS", {"Yosemite",      {10, 10,  3}}},
-			{"14E46",    {14,  4,  0}, "macOS", {"Yosemite",      {10, 10,  4}}},
-			{"14F27",    {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1021",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1505",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1509",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1605",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1713",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1808",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1909",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F1912",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F2009",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F2109",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F2315",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F2411",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"14F2511",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
-			{"15A284",   {15,  0,  0}, "macOS", {"El Capitan",    {10, 11,  0}}},
-			{"15B42",    {15,  0,  0}, "macOS", {"El Capitan",    {10, 11,  1}}},
-			{"15C50",    {15,  2,  0}, "macOS", {"El Capitan",    {10, 11,  2}}},
-			{"15D21",    {15,  3,  0}, "macOS", {"El Capitan",    {10, 11,  3}}},
-			{"15E65",    {15,  4,  0}, "macOS", {"El Capitan",    {10, 11,  4}}},
-			{"15F34",    {15,  5,  0}, "macOS", {"El Capitan",    {10, 11,  5}}},
-			{"15G31",    {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1004",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1011",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1108",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1212",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1217",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1421",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1510",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G1611",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G17023", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G18013", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G19009", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G20015", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G21013", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"15G22010", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
-			{"16A323",   {16,  0,  0}, "macOS", {"Sierra",        {10, 12,  0}}},
-			{"16B2555",  {16,  1,  0}, "macOS", {"Sierra",        {10, 12,  1}}},
-			{"16B2657",  {16,  1,  0}, "macOS", {"Sierra",        {10, 12,  1}}},
-			{"16C67",    {16,  3,  0}, "macOS", {"Sierra",        {10, 12,  2}}},
-			{"16C68",    {16,  3,  0}, "macOS", {"Sierra",        {10, 12,  2}}},
-			{"16D32",    {16,  4,  0}, "macOS", {"Sierra",        {10, 12,  3}}},
-			{"16E195",   {16,  5,  0}, "macOS", {"Sierra",        {10, 12,  4}}},
-			{"16F73",    {16,  6,  0}, "macOS", {"Sierra",        {10, 12,  5}}},
-			{"16F2073",  {16,  6,  0}, "macOS", {"Sierra",        {10, 12,  5}}},
-			{"16G29",    {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1036",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1114",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1212",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1314",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1408",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1510",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1618",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1710",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1815",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1917",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G1918",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G2016",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G2127",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G2128",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"16G2136",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
-			{"17A365",   {17,  0,  0}, "macOS", {"High Sierra",   {10, 13,  0}}},
-			{"17A405",   {17,  0,  0}, "macOS", {"High Sierra",   {10, 13,  0}}},
-			{"17B48",    {17,  2,  0}, "macOS", {"High Sierra",   {10, 13,  1}}},
-			{"17B1002",  {17,  2,  0}, "macOS", {"High Sierra",   {10, 13,  1}}},
-			{"17B1003",  {17,  2,  0}, "macOS", {"High Sierra",   {10, 13,  1}}},
-			{"17C88",    {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
-			{"17C89",    {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
-			{"17C205",   {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
-			{"17C2205",  {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
-			{"17D47",    {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
-			{"17D2047",  {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
-			{"17D102",   {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
-			{"17D2102",  {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
-			{"17E199",   {17,  5,  0}, "macOS", {"High Sierra",   {10, 13,  4}}},
-			{"17E202",   {17,  5,  0}, "macOS", {"High Sierra",   {10, 13,  4}}},
-			{"17F77",    {17,  6,  0}, "macOS", {"High Sierra",   {10, 13,  5}}},
-			{"17G65",    {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G2208",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G2307",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G3025",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G4015",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G5019",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G6029",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G6030",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G7024",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G8029",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G8030",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G8037",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G9016",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G10021", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G11023", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G12034", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G13033", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G13035", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G14019", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G14033", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"17G14042", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
-			{"18A391",   {18,  0,  0}, "macOS", {"Mojave",        {10, 14,  0}}},
-			{"18B75",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  1}}},
-			{"18B2107",  {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  1}}},
-			{"18B3094",  {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  1}}},
-			{"18C54",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  2}}},
-			{"18D42",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  3}}},
-			{"18D43",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  3}}},
-			{"18D109",   {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  3}}},
-			{"18E226",   {18,  5,  0}, "macOS", {"Mojave",        {10, 14,  4}}},
-			{"18E227",   {18,  5,  0}, "macOS", {"Mojave",        {10, 14,  4}}},
-			{"18F132",   {18,  6,  0}, "macOS", {"Mojave",        {10, 14,  5}}},
-			{"18G84",    {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G87",    {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G95",    {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G103",   {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G1012",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G2022",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G3020",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G4032",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G5033",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G6020",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G6032",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G6042",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G7016",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G8012",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G8022",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G9028",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G9216",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"18G9323",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
-			{"19A583",   {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  0}}},
-			{"19A602",   {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  0}}},
-			{"19A603",   {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  0}}},
-			{"19B88",    {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  1}}},
-			{"19C57",    {19,  2,  0}, "macOS", {"Catalina",      {10, 15,  2}}},
-			{"19C58",    {19,  2,  0}, "macOS", {"Catalina",      {10, 15,  2}}},
-			{"19D76",    {19,  3,  0}, "macOS", {"Catalina",      {10, 15,  3}}},
-			{"19E266",   {19,  4,  0}, "macOS", {"Catalina",      {10, 15,  4}}},
-			{"19E287",   {19,  4,  0}, "macOS", {"Catalina",      {10, 15,  4}}},
-			{"19F96",    {19,  5,  0}, "macOS", {"Catalina",      {10, 15,  5}}},
-			{"19F101",   {19,  5,  0}, "macOS", {"Catalina",      {10, 15,  5}}},
-			{"19G73",    {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  6}}},
-			{"19G2021",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  6}}},
-			{"19H2",     {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H4",     {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H15",    {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H114",   {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H512",   {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H524",   {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1030",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1217",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1323",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1417",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1419",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1519",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1615",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1713",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1715",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1824",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H1922",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"19H2026",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
-			{"20A2411",  {20,  1,  0}, "macOS", {"Big Sur",       {11,  0,  0}}},
-			{"20B29",    {20,  1,  0}, "macOS", {"Big Sur",       {11,  0,  1}}},
-			{"20B50",    {20,  1,  0}, "macOS", {"Big Sur",       {11,  0,  1}}},
-			{"20C69",    {20,  2,  0}, "macOS", {"Big Sur",       {11,  1,  0}}},
-			{"20D64",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  0}}},
-			{"20D74",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  1}}},
-			{"20D75",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  1}}},
-			{"20D80",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  2}}},
-			{"20D91",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  3}}},
-			{"20E232",   {20,  4,  0}, "macOS", {"Big Sur",       {11,  3,  0}}},
-			{"20E241",   {20,  4,  0}, "macOS", {"Big Sur",       {11,  3,  1}}},
-			{"20F71",    {20,  5,  0}, "macOS", {"Big Sur",       {11,  4,  0}}},
-			{"20G71",    {20,  6,  0}, "macOS", {"Big Sur",       {11,  5,  0}}},
-			{"20G80",    {20,  6,  0}, "macOS", {"Big Sur",       {11,  5,  1}}},
-			{"20G95",    {20,  6,  0}, "macOS", {"Big Sur",       {11,  5,  2}}},
-			{"20G165",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  0}}},
-			{"20G224",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  1}}},
-			{"20G314",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  2}}},
-			{"20G415",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  3}}},
-			{"20G417",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  4}}},
-			{"20G527",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  5}}},
-			{"20G624",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  6}}},
-			{"20G630",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  7}}},
-			{"20G730",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  8}}},
-			{"20G817",   {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   0}}},
-			{"20G918",   {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   1}}},
-			{"20G1020",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   2}}},
-			{"20G1116",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   3}}},
-			{"20G1120",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   4}}},
-			{"20G1225",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   5}}},
-			{"20G1231",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   6}}},
-			{"20G1345",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   7}}},
-			{"20G1351",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   8}}},
-			{"20G1426",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   9}}},
-			{"20G1427",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,  10}}},
-			{"21A344",   {21,  0,  1}, "macOS", {"Monterey",      {12,  0,  0}}},
-			{"21A559",   {21,  1,  0}, "macOS", {"Monterey",      {12,  0,  1}}},
-			{"21C52",    {21,  2,  0}, "macOS", {"Monterey",      {12,  1,  0}}},
-			{"21D49",    {21,  3,  0}, "macOS", {"Monterey",      {12,  2,  0}}},
-			{"21D62",    {21,  3,  0}, "macOS", {"Monterey",      {12,  2,  1}}},
-			{"21E230",   {21,  4,  0}, "macOS", {"Monterey",      {12,  3,  0}}},
-			{"21E258",   {21,  4,  0}, "macOS", {"Monterey",      {12,  3,  1}}},
-			{"21F79",    {21,  5,  0}, "macOS", {"Monterey",      {12,  4,  0}}},
-			{"21F2081",  {21,  5,  0}, "macOS", {"Monterey",      {12,  4,  0}}},
-			{"21F2092",  {21,  5,  0}, "macOS", {"Monterey",      {12,  4,  0}}},
-			{"21G72",    {21,  6,  0}, "macOS", {"Monterey",      {12,  5,  0}}},
-			{"21G83",    {21,  6,  0}, "macOS", {"Monterey",      {12,  5,  1}}},
-			{"21G115",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  0}}},
-			{"21G217",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  1}}},
-			{"21G320",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  2}}},
-			{"21G419",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  3}}},
-			{"21G526",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  4}}},
-			{"21G531",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  5}}},
-			{"21G646",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  6}}},
-			{"21G651",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  7}}},
-			{"21G725",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  8}}},
-			{"21G726",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  9}}},
-			{"21G816",   {21,  6,  0}, "macOS", {"Monterey",      {12,  7,  0}}},
-			{"21G920",   {21,  6,  0}, "macOS", {"Monterey",      {12,  7,  1}}},
-			{"21G1974",  {21,  6,  0}, "macOS", {"Monterey",      {12,  7,  2}}},
-			{"22A380",   {13,  0,  0}, "macOS", {"Ventura",       {22,  1,  0}}},
-			{"22A400",   {13,  0,  1}, "macOS", {"Ventura",       {22,  1,  0}}},
-			{"22C65",    {13,  1,  0}, "macOS", {"Ventura",       {22,  2,  0}}},
-			{"22D49",    {13,  2,  0}, "macOS", {"Ventura",       {22,  3,  0}}},
-			{"22D68",    {13,  2,  1}, "macOS", {"Ventura",       {22,  3,  0}}},
-			{"22E252",   {13,  3,  0}, "macOS", {"Ventura",       {22,  4,  0}}},
-			{"22E261",   {13,  3,  1}, "macOS", {"Ventura",       {22,  4,  0}}},
-			{"22F66",    {13,  4,  0}, "macOS", {"Ventura",       {22,  5,  0}}},
-			{"22F82",    {13,  4,  1}, "macOS", {"Ventura",       {22,  5,  0}}},
-			{"22E772610a", {13, 4, 1}, "macOS", {"Ventura",       {22,  5,  0}}},
-			{"22F770820d", {13, 4, 1}, "macOS", {"Ventura",       {22,  5,  0}}},
-			{"22G74",    {13,  5,  0}, "macOS", {"Ventura",       {22,  6,  0}}},
-			{"22G90",    {13,  5,  1}, "macOS", {"Ventura",       {22,  6,  0}}},
-			{"22G91",    {13,  5,  2}, "macOS", {"Ventura",       {22,  6,  0}}},
-			{"22G120",   {13,  6,  0}, "macOS", {"Ventura",       {22,  6,  0}}},
-			{"22G313",   {13,  6,  1}, "macOS", {"Ventura",       {22,  6,  0}}},
-			{"22G320",   {13,  6,  2}, "macOS", {"Ventura",       {22,  6,  0}}},
-			{"23A344",   {23,  0,  0}, "macOS", {"Sonoma",        {14,  0,  0}}},
-			{"23B74",    {23,  1,  0}, "macOS", {"Sonoma",        {14,  1,  0}}},
-			{"23B81",    {23,  1,  0}, "macOS", {"Sonoma",        {14,  1,  1}}},
-			{"23B92",    {23,  1,  0}, "macOS", {"Sonoma",        {14,  1,  2}}},
-			{"23C64",    {23,  2,  0}, "macOS", {"Sonoma",        {14,  2,  0}}},
-			{"23C71",    {23,  2,  0}, "macOS", {"Sonoma",        {14,  2,  1}}},
-		};
-
-
-		b32 build_found  = 1;
-		b32 darwin_found = 1;
-		uint32_t major, minor, patch;
-
-		#define MACOS_VERSION_BUFFER_SIZE 100
-		char build_buffer[MACOS_VERSION_BUFFER_SIZE];
-		char darwin_buffer[MACOS_VERSION_BUFFER_SIZE];
-		size_t build_buffer_size  = MACOS_VERSION_BUFFER_SIZE - 1;
-		size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
-		#undef MACOS_VERSION_BUFFER_SIZE
-
-		int build_mibs[] = { CTL_KERN, KERN_OSVERSION };
-		if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) {
-			build_found = 0;
-		}
-
-		int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
-		if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
-			gb_printf("macOS Unknown\n");
-			return;
-		} else {
-			if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
-				darwin_found = 0;
-			}
-		}
-
-		// Scan table for match on BUILD
-		int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
-		Darwin_To_Release build_match = {};
-		Darwin_To_Release kernel_match = {};
-	
-		for (int build = 0; build < macos_release_count; build++) {
-			Darwin_To_Release rel = macos_release_map[build];
-			
-			// Do we have an exact match on the BUILD?
-			if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
-				build_match = rel;
-				break;
-			}
-			
-			// Do we have an exact Darwin match?
-			if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
-				kernel_match = rel;
-			}
-	
-			// Major kernel version needs to match exactly,
-			if (rel.darwin[0] == major) {
-				// No major version match yet.
-				if (!kernel_match.os_name) {
-					kernel_match = rel;
-				}
-				if (minor >= rel.darwin[1]) {
-					kernel_match = rel;
-					if (patch >= rel.darwin[2]) {
-						kernel_match = rel;
-					}
-				}
-			}
-		}
-	
-		Darwin_To_Release match = {};
-		if(!build_match.build) {
-			match = kernel_match;
-		} else {
-			match = build_match;
-		}
-
-		if (match.os_name) {
-			gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
-			if (match.release.version[1] > 0 || match.release.version[2] > 0) {
-				gb_printf(".%d", match.release.version[1]);
-			}
-			if (match.release.version[2] > 0) {
-				gb_printf(".%d", match.release.version[2]);
-			}
-			if (build_found) {
-				gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
-			} else {
-				gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);				
-			}
-			return;
-		}
-
-		if (build_found && darwin_found) {
-			gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
-			return;
-		} else if (build_found) {
-			gb_printf("macOS Unknown (build: %s)\n", build_buffer);
-			return;
-		} else if (darwin_found) {
-			gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
-			return;
-		}
-	#elif defined(GB_SYSTEM_OPENBSD)
-		struct utsname un;
-		
-		if (uname(&un) != -1) {
-			gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
-		} else {
-			gb_printf("OpenBSD: Unknown\n");    
-		}
-	#elif defined(GB_SYSTEM_FREEBSD)
-		#define freebsd_version_buffer 129
-		char buffer[freebsd_version_buffer];
-		size_t buffer_size = freebsd_version_buffer - 1;
-		#undef freebsd_version_buffer
-
-		int mibs[] = { CTL_KERN, KERN_VERSION };
-		if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) {
-			gb_printf("FreeBSD: Unknown\n");
-		} else {
-			// KERN_VERSION can end in a \n, replace it with a \0
-			for (int i = 0; i < buffer_size; i += 1) {
-				if (buffer[i] == '\n') buffer[i] = 0;
-			}
-			gb_printf("%s", &buffer[0]);
-
-			// Retrieve kernel revision using `sysctl`, e.g. 199506
-			mibs[1] = KERN_OSREV;
-			uint64_t revision;
-			size_t revision_size = sizeof(revision);
-
-			if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) {
-				gb_printf("\n");
-			} else {
-				gb_printf(", revision %ld\n", revision);
-			}
-		}
-	#else
-		gb_printf("Unknown");
-	#endif
-}
-
-// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
-gb_internal void print_bug_report_help() {
-	gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
-	gb_printf("\tWebsite: https://odin-lang.org\n");
-	gb_printf("\tGitHub:  https://github.com/odin-lang/Odin/issues\n");
-	/*
-		Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
-		gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
-	*/
-	gb_printf("\n\n");
-
-	gb_printf("Useful information to add to a bug report:\n\n");
-
-	gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION));
-
-	#ifdef NIGHTLY
-	gb_printf("-nightly");
-	#endif
-
-	#ifdef GIT_SHA
-	gb_printf(":%s", GIT_SHA);
-	#endif
-
-	gb_printf("\n");
-
-	/*
-		Print OS information.
-	*/
-	report_os_info();
-
-	/*
-		Now print CPU info.
-	*/
-	report_cpu_info();
-
-	/*
-		And RAM info.
-	*/
-	report_ram_info();
-}
+/*
+	Gather and print platform and version info to help with reporting Odin bugs.
+*/
+
+#if !defined(GB_COMPILER_MSVC)
+	#if defined(GB_CPU_X86)
+		#include <cpuid.h>
+	#endif
+#endif
+
+#if defined(GB_SYSTEM_LINUX)
+	#include <sys/utsname.h>
+	#include <sys/sysinfo.h>
+#endif
+
+#if defined(GB_SYSTEM_OSX)
+	#include <sys/sysctl.h>
+#endif
+
+#if defined(GB_SYSTEM_OPENBSD)
+	#include <sys/sysctl.h>
+	#include <sys/utsname.h>
+#endif
+
+#if defined(GB_SYSTEM_FREEBSD)
+	#include <sys/sysctl.h>
+#endif
+
+/*
+	NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
+*/
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal void report_windows_product_type(DWORD ProductType) {
+	switch (ProductType) {
+	case PRODUCT_ULTIMATE:
+		gb_printf("Ultimate");
+		break;
+
+	case PRODUCT_HOME_BASIC:
+		gb_printf("Home Basic");
+		break;
+
+	case PRODUCT_HOME_PREMIUM:
+		gb_printf("Home Premium");
+		break;
+
+	case PRODUCT_ENTERPRISE:
+		gb_printf("Enterprise");
+		break;
+
+	case PRODUCT_CORE:
+		gb_printf("Home Basic");
+		break;
+
+	case PRODUCT_HOME_BASIC_N:
+		gb_printf("Home Basic N");
+		break;
+
+	case PRODUCT_EDUCATION:
+		gb_printf("Education");
+		break;
+
+	case PRODUCT_EDUCATION_N:
+		gb_printf("Education N");
+		break;
+
+	case PRODUCT_BUSINESS:
+		gb_printf("Business");
+		break;
+
+	case PRODUCT_STANDARD_SERVER:
+		gb_printf("Standard Server");
+		break;
+
+	case PRODUCT_DATACENTER_SERVER:
+		gb_printf("Datacenter");
+		break;
+
+	case PRODUCT_SMALLBUSINESS_SERVER:
+		gb_printf("Windows Small Business Server");
+		break;
+
+	case PRODUCT_ENTERPRISE_SERVER:
+		gb_printf("Enterprise Server");
+		break;
+
+	case PRODUCT_STARTER:
+		gb_printf("Starter");
+		break;
+
+	case PRODUCT_DATACENTER_SERVER_CORE:
+		gb_printf("Datacenter Server Core");
+		break;
+
+	case PRODUCT_STANDARD_SERVER_CORE:
+		gb_printf("Server Standard Core");
+		break;
+
+	case PRODUCT_ENTERPRISE_SERVER_CORE:
+		gb_printf("Enterprise Server Core");
+		break;
+
+	case PRODUCT_BUSINESS_N:
+		gb_printf("Business N");
+		break;
+
+	case PRODUCT_HOME_SERVER:
+		gb_printf("Home Server");
+		break;
+
+	case PRODUCT_SERVER_FOR_SMALLBUSINESS:
+		gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
+		break;
+
+	case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+		gb_printf("Small Business Server Premium");
+		break;
+
+	case PRODUCT_HOME_PREMIUM_N:
+		gb_printf("Home Premium N");
+		break;
+
+	case PRODUCT_ENTERPRISE_N:
+		gb_printf("Enterprise N");
+		break;
+
+	case PRODUCT_ULTIMATE_N:
+		gb_printf("Ultimate N");
+		break;
+
+	case PRODUCT_HYPERV:
+		gb_printf("HyperV");
+		break;
+
+	case PRODUCT_STARTER_N:
+		gb_printf("Starter N");
+		break;
+
+	case PRODUCT_PROFESSIONAL:
+		gb_printf("Professional");
+		break;
+
+	case PRODUCT_PROFESSIONAL_N:
+		gb_printf("Professional N");
+		break;
+
+	case PRODUCT_UNLICENSED:
+		gb_printf("Unlicensed");
+		break;
+
+	default:
+		gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
+	}
+}
+#endif
+
+gb_internal void odin_cpuid(int leaf, int result[]) {
+	#if defined(GB_CPU_ARM)
+		return;
+
+	#elif defined(GB_CPU_X86)
+	
+		#if defined(GB_COMPILER_MSVC)
+			__cpuid(result, leaf);
+		#else
+			__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
+		#endif
+
+	#endif
+}
+
+gb_internal void report_cpu_info() {
+	gb_printf("\tCPU:     ");
+
+	#if defined(GB_CPU_X86)
+
+	/*
+		Get extended leaf info
+	*/
+	int cpu[4];
+
+	odin_cpuid(0x80000000, &cpu[0]);
+	int number_of_extended_ids = cpu[0];
+
+	int brand[0x12] = {};
+
+	/*
+		Read CPU brand if supported.
+	*/
+	if (number_of_extended_ids >= 0x80000004) {
+		odin_cpuid(0x80000002, &brand[0]);
+		odin_cpuid(0x80000003, &brand[4]);
+		odin_cpuid(0x80000004, &brand[8]);
+
+		/*
+			Some CPUs like `      Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
+		*/
+		char * brand_name = (char *)&brand[0];
+		for (; brand_name[0] == ' '; brand_name++) {}
+
+		gb_printf("%s\n", brand_name);
+	} else {
+		gb_printf("Unable to retrieve.\n");
+	}
+
+	#elif defined(GB_CPU_ARM)
+		/*
+			TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
+		*/
+		#if defined(GB_ARCH_64_BIT)
+			gb_printf("ARM64\n");
+		#else
+			gb_printf("ARM\n");
+		#endif
+	#else
+		gb_printf("Unknown\n");
+	#endif
+}
+
+/*
+	Report the amount of installed RAM.
+*/
+gb_internal void report_ram_info() {
+	gb_printf("\tRAM:     ");
+
+	#if defined(GB_SYSTEM_WINDOWS)
+		MEMORYSTATUSEX statex;
+		statex.dwLength = sizeof(statex);
+		GlobalMemoryStatusEx (&statex);
+
+		gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
+
+	#elif defined(GB_SYSTEM_LINUX)
+		/*
+			Retrieve RAM info using `sysinfo()`, 
+		*/
+		struct sysinfo info;
+		int result = sysinfo(&info);
+
+		if (result == 0x0) {
+			gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1));
+		} else {
+			gb_printf("Unknown.\n");
+		}
+	#elif defined(GB_SYSTEM_OSX)
+		uint64_t ram_amount;
+		size_t   val_size = sizeof(ram_amount);
+
+		int mibs[] = { CTL_HW, HW_MEMSIZE };
+		if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+		}
+	#elif defined(GB_SYSTEM_OPENBSD)
+		uint64_t ram_amount;
+		size_t   val_size = sizeof(ram_amount);
+
+		int mibs[] = { CTL_HW, HW_PHYSMEM64 };
+		if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+		}
+	#elif defined(GB_SYSTEM_FREEBSD)
+		uint64_t ram_amount;
+		size_t   val_size = sizeof(ram_amount);
+
+		int mibs[] = { CTL_HW, HW_PHYSMEM };
+		if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+			gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
+		}
+	#else
+		gb_printf("Unknown.\n");
+	#endif
+}
+
+gb_internal void report_os_info() {
+	gb_printf("\tOS:      ");
+
+	#if defined(GB_SYSTEM_WINDOWS)
+	/*
+		NOTE(Jeroen): 
+			`GetVersionEx`  will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
+			`RtlGetVersion` will return the true version.
+
+			Rather than include the WinDDK, we ask the kernel directly.
+
+			`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
+
+	*/
+	OSVERSIONINFOEXW osvi;
+	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
+	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+	typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
+	typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
+
+	// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
+	RtlGetVersionPtr  RtlGetVersion  =  (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
+	// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
+	GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
+
+	NTSTATUS status  = {};
+	DWORD ProductType = {};
+	if (RtlGetVersion != nullptr) {
+		status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
+	}
+
+	if (RtlGetVersion == nullptr || status != 0x0) {
+		gb_printf("Windows (Unknown Version)");
+	} else {
+		if (GetProductInfo != nullptr) {
+			GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
+		}
+
+		if (false) {
+			gb_printf("dwMajorVersion:    %u\n", cast(unsigned)osvi.dwMajorVersion);
+			gb_printf("dwMinorVersion:    %u\n", cast(unsigned)osvi.dwMinorVersion);
+			gb_printf("dwBuildNumber:     %u\n", cast(unsigned)osvi.dwBuildNumber);
+			gb_printf("dwPlatformId:      %u\n", cast(unsigned)osvi.dwPlatformId);
+			gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
+			gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
+			gb_printf("wSuiteMask:        %u\n", cast(unsigned)osvi.wSuiteMask);
+			gb_printf("wProductType:      %u\n", cast(unsigned)osvi.wProductType);
+		}
+
+		gb_printf("Windows ");
+
+		switch (osvi.dwMajorVersion) {
+		case 10:
+			/*
+				Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
+			*/
+			switch (osvi.wProductType) {
+			case VER_NT_WORKSTATION: // Workstation
+				if (osvi.dwBuildNumber < 22000) {
+					gb_printf("10 ");
+				} else {
+					gb_printf("11 ");
+				}
+				
+				report_windows_product_type(ProductType);
+
+				break;
+			default: // Server or Domain Controller
+				switch(osvi.dwBuildNumber) {
+				case 14393:
+					gb_printf("2016 Server");
+					break;
+				case 17763:
+					gb_printf("2019 Server");
+					break;
+				case 20348:
+					gb_printf("2022 Server");
+					break;
+				default:
+					gb_printf("Unknown Server");
+					break;
+				}
+			}
+			break;
+		case 6:
+			switch (osvi.dwMinorVersion) {
+				case 0:
+					switch (osvi.wProductType) {
+						case VER_NT_WORKSTATION:
+							gb_printf("Windows Vista ");
+							report_windows_product_type(ProductType);
+							break;
+						case 3:
+							gb_printf("Windows Server 2008");
+							break;
+					}
+					break;
+
+				case 1:
+					switch (osvi.wProductType) {
+						case VER_NT_WORKSTATION:
+							gb_printf("Windows 7 ");
+							report_windows_product_type(ProductType);
+							break;
+						case 3:
+							gb_printf("Windows Server 2008 R2");
+							break;
+					}
+					break;
+				case 2:
+					switch (osvi.wProductType) {
+						case VER_NT_WORKSTATION:
+							gb_printf("Windows 8 ");
+							report_windows_product_type(ProductType);
+							break;
+						case 3:
+							gb_printf("Windows Server 2012");
+							break;
+					}
+					break;
+				case 3:
+					switch (osvi.wProductType) {
+						case VER_NT_WORKSTATION:
+							gb_printf("Windows 8.1 ");
+							report_windows_product_type(ProductType);
+							break;
+						case 3:
+							gb_printf("Windows Server 2012 R2");
+							break;
+					}
+					break;
+			}
+			break;
+		case 5:
+			switch (osvi.dwMinorVersion) {
+				case 0:
+					gb_printf("Windows 2000");
+					break;
+				case 1:
+					gb_printf("Windows XP");
+					break;
+				case 2:
+					gb_printf("Windows Server 2003");
+					break;
+			}
+			break;
+		default:
+			break;
+		}
+
+		/*
+			Grab Windows DisplayVersion (like 20H02)
+		*/
+		LPDWORD ValueType = {};
+		DWORD   UBR;
+		char    DisplayVersion[256];
+		DWORD   ValueSize = 256;
+
+		status = RegGetValue(
+			HKEY_LOCAL_MACHINE,
+			TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+			TEXT("DisplayVersion"),
+			RRF_RT_REG_SZ,
+			ValueType,
+			DisplayVersion,
+			&ValueSize
+		);
+
+		if (status == 0x0) {
+			gb_printf(" (version: %s)", DisplayVersion);
+		}
+
+		/*
+			Now print build number.
+		*/
+		gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
+
+		ValueSize = sizeof(UBR);
+		status = RegGetValue(
+			HKEY_LOCAL_MACHINE,
+			TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+			TEXT("UBR"),
+			RRF_RT_REG_DWORD,
+			ValueType,
+			&UBR,
+			&ValueSize
+		);
+
+		if (status == 0x0) {
+			gb_printf(".%u", cast(unsigned)UBR);
+		}
+		gb_printf("\n");
+	}
+	#elif defined(GB_SYSTEM_LINUX)
+		/*
+			Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+		*/
+		gbAllocator a = heap_allocator();
+
+		gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
+		defer (gb_file_free_contents(&release));
+
+		b32 found = 0;
+		if (release.size) {
+			char *start        = (char *)release.data;
+			char *end          = (char *)release.data + release.size;
+			const char *needle = "PRETTY_NAME=\"";
+			isize needle_len   = gb_strlen((needle));
+		
+			char *c = start;
+			for (; c < end; c++) {
+				if (gb_strncmp(c, needle, needle_len) == 0) {
+					found = 1;
+					start = c + needle_len;
+					break;
+				}
+			}
+
+			if (found) {
+				for (c = start; c < end; c++) {
+					if (*c == '"') {
+						// Found the closing quote. Replace it with \0
+						*c = 0;
+						gb_printf("%s", (char *)start);
+						break;
+					} else if (*c == '\n') {
+						found = 0;
+					}
+				}
+			}
+		}
+
+		if (!found) {
+			gb_printf("Unknown Linux Distro");
+		}
+
+		/*
+			Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
+		*/
+		char buffer[1024];
+		uname((struct utsname *)&buffer[0]);
+
+		struct utsname *info;
+		info = (struct utsname *)&buffer[0];
+
+		gb_printf(", %s %s\n", info->sysname, info->release);
+
+	#elif defined(GB_SYSTEM_OSX)
+		struct Darwin_To_Release {
+			const char* build;    // 21G83
+			int   darwin[3];      // Darwin kernel triplet
+			const char* os_name;  // OS X, MacOS
+			struct {
+				const char* name; // Monterey, Mojave, etc.
+				int version[3];   // 12.4, etc.
+			} release;
+		};
+
+		Darwin_To_Release macos_release_map[] = {
+			{"8A428",    { 8,  0,  0}, "macOS", {"Tiger",         {10,  4,  0}}},
+			{"8A432",    { 8,  0,  0}, "macOS", {"Tiger",         {10,  4,  0}}},
+			{"8B15",     { 8,  1,  0}, "macOS", {"Tiger",         {10,  4,  1}}},
+			{"8B17",     { 8,  1,  0}, "macOS", {"Tiger",         {10,  4,  1}}},
+			{"8C46",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
+			{"8C47",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
+			{"8E102",    { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
+			{"8E45",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
+			{"8E90",     { 8,  2,  0}, "macOS", {"Tiger",         {10,  4,  2}}},
+			{"8F46",     { 8,  3,  0}, "macOS", {"Tiger",         {10,  4,  3}}},
+			{"8G32",     { 8,  4,  0}, "macOS", {"Tiger",         {10,  4,  4}}},
+			{"8G1165",   { 8,  4,  0}, "macOS", {"Tiger",         {10,  4,  4}}},
+			{"8H14",     { 8,  5,  0}, "macOS", {"Tiger",         {10,  4,  5}}},
+			{"8G1454",   { 8,  5,  0}, "macOS", {"Tiger",         {10,  4,  5}}},
+			{"8I127",    { 8,  6,  0}, "macOS", {"Tiger",         {10,  4,  6}}},
+			{"8I1119",   { 8,  6,  0}, "macOS", {"Tiger",         {10,  4,  6}}},
+			{"8J135",    { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
+			{"8J2135a",  { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
+			{"8K1079",   { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
+			{"8N5107",   { 8,  7,  0}, "macOS", {"Tiger",         {10,  4,  7}}},
+			{"8L127",    { 8,  8,  0}, "macOS", {"Tiger",         {10,  4,  8}}},
+			{"8L2127",   { 8,  8,  0}, "macOS", {"Tiger",         {10,  4,  8}}},
+			{"8P135",    { 8,  9,  0}, "macOS", {"Tiger",         {10,  4,  9}}},
+			{"8P2137",   { 8,  9,  0}, "macOS", {"Tiger",         {10,  4,  9}}},
+			{"8R218",    { 8, 10,  0}, "macOS", {"Tiger",         {10,  4, 10}}},
+			{"8R2218",   { 8, 10,  0}, "macOS", {"Tiger",         {10,  4, 10}}},
+			{"8R2232",   { 8, 10,  0}, "macOS", {"Tiger",         {10,  4, 10}}},
+			{"8S165",    { 8, 11,  0}, "macOS", {"Tiger",         {10,  4, 11}}},
+			{"8S2167",   { 8, 11,  0}, "macOS", {"Tiger",         {10,  4, 11}}},
+			{"9A581",    { 9,  0,  0}, "macOS", {"Leopard",       {10,  5,  0}}},
+			{"9B18",     { 9,  1,  0}, "macOS", {"Leopard",       {10,  5,  1}}},
+			{"9B2117",   { 9,  1,  1}, "macOS", {"Leopard",       {10,  5,  1}}},
+			{"9C31",     { 9,  2,  0}, "macOS", {"Leopard",       {10,  5,  2}}},
+			{"9C7010",   { 9,  2,  0}, "macOS", {"Leopard",       {10,  5,  2}}},
+			{"9D34",     { 9,  3,  0}, "macOS", {"Leopard",       {10,  5,  3}}},
+			{"9E17",     { 9,  4,  0}, "macOS", {"Leopard",       {10,  5,  4}}},
+			{"9F33",     { 9,  5,  0}, "macOS", {"Leopard",       {10,  5,  5}}},
+			{"9G55",     { 9,  6,  0}, "macOS", {"Leopard",       {10,  5,  6}}},
+			{"9G66",     { 9,  6,  0}, "macOS", {"Leopard",       {10,  5,  6}}},
+			{"9G71",     { 9,  6,  0}, "macOS", {"Leopard",       {10,  5,  6}}},
+			{"9J61",     { 9,  7,  0}, "macOS", {"Leopard",       {10,  5,  7}}},
+			{"9L30",     { 9,  8,  0}, "macOS", {"Leopard",       {10,  5,  8}}},
+			{"9L34",     { 9,  8,  0}, "macOS", {"Leopard",       {10,  5,  8}}},
+			{"10A432",   {10,  0,  0}, "macOS", {"Snow Leopard",  {10,  6,  0}}},
+			{"10A433",   {10,  0,  0}, "macOS", {"Snow Leopard",  {10,  6,  0}}},
+			{"10B504",   {10,  1,  0}, "macOS", {"Snow Leopard",  {10,  6,  1}}},
+			{"10C540",   {10,  2,  0}, "macOS", {"Snow Leopard",  {10,  6,  2}}},
+			{"10D573",   {10,  3,  0}, "macOS", {"Snow Leopard",  {10,  6,  3}}},
+			{"10D575",   {10,  3,  0}, "macOS", {"Snow Leopard",  {10,  6,  3}}},
+			{"10D578",   {10,  3,  0}, "macOS", {"Snow Leopard",  {10,  6,  3}}},
+			{"10F569",   {10,  4,  0}, "macOS", {"Snow Leopard",  {10,  6,  4}}},
+			{"10H574",   {10,  5,  0}, "macOS", {"Snow Leopard",  {10,  6,  5}}},
+			{"10J567",   {10,  6,  0}, "macOS", {"Snow Leopard",  {10,  6,  6}}},
+			{"10J869",   {10,  7,  0}, "macOS", {"Snow Leopard",  {10,  6,  7}}},
+			{"10J3250",  {10,  7,  0}, "macOS", {"Snow Leopard",  {10,  6,  7}}},
+			{"10J4138",  {10,  7,  0}, "macOS", {"Snow Leopard",  {10,  6,  7}}},
+			{"10K540",   {10,  8,  0}, "macOS", {"Snow Leopard",  {10,  6,  8}}},
+			{"10K549",   {10,  8,  0}, "macOS", {"Snow Leopard",  {10,  6,  8}}},
+			{"11A511",   {11,  0,  0}, "macOS", {"Lion",          {10,  7,  0}}},
+			{"11A511s",  {11,  0,  0}, "macOS", {"Lion",          {10,  7,  0}}},
+			{"11A2061",  {11,  0,  2}, "macOS", {"Lion",          {10,  7,  0}}},
+			{"11A2063",  {11,  0,  2}, "macOS", {"Lion",          {10,  7,  0}}},
+			{"11B26",    {11,  1,  0}, "macOS", {"Lion",          {10,  7,  1}}},
+			{"11B2118",  {11,  1,  0}, "macOS", {"Lion",          {10,  7,  1}}},
+			{"11C74",    {11,  2,  0}, "macOS", {"Lion",          {10,  7,  2}}},
+			{"11D50",    {11,  3,  0}, "macOS", {"Lion",          {10,  7,  3}}},
+			{"11E53",    {11,  4,  0}, "macOS", {"Lion",          {10,  7,  4}}},
+			{"11G56",    {11,  4,  2}, "macOS", {"Lion",          {10,  7,  5}}},
+			{"11G63",    {11,  4,  2}, "macOS", {"Lion",          {10,  7,  5}}},
+			{"12A269",   {12,  0,  0}, "macOS", {"Mountain Lion", {10,  8,  0}}},
+			{"12B19",    {12,  1,  0}, "macOS", {"Mountain Lion", {10,  8,  1}}},
+			{"12C54",    {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
+			{"12C60",    {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
+			{"12C2034",  {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
+			{"12C3104",  {12,  2,  0}, "macOS", {"Mountain Lion", {10,  8,  2}}},
+			{"12D78",    {12,  3,  0}, "macOS", {"Mountain Lion", {10,  8,  3}}},
+			{"12E55",    {12,  4,  0}, "macOS", {"Mountain Lion", {10,  8,  4}}},
+			{"12E3067",  {12,  4,  0}, "macOS", {"Mountain Lion", {10,  8,  4}}},
+			{"12E4022",  {12,  4,  0}, "macOS", {"Mountain Lion", {10,  8,  4}}},
+			{"12F37",    {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
+			{"12F45",    {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
+			{"12F2501",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
+			{"12F2518",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
+			{"12F2542",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
+			{"12F2560",  {12,  5,  0}, "macOS", {"Mountain Lion", {10,  8,  5}}},
+			{"13A603",   {13,  0,  0}, "macOS", {"Mavericks",     {10,  9,  0}}},
+			{"13B42",    {13,  0,  0}, "macOS", {"Mavericks",     {10,  9,  1}}},
+			{"13C64",    {13,  1,  0}, "macOS", {"Mavericks",     {10,  9,  2}}},
+			{"13C1021",  {13,  1,  0}, "macOS", {"Mavericks",     {10,  9,  2}}},
+			{"13D65",    {13,  2,  0}, "macOS", {"Mavericks",     {10,  9,  3}}},
+			{"13E28",    {13,  3,  0}, "macOS", {"Mavericks",     {10,  9,  4}}},
+			{"13F34",    {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1066",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1077",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1096",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1112",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1134",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1507",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1603",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1712",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1808",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"13F1911",  {13,  4,  0}, "macOS", {"Mavericks",     {10,  9,  5}}},
+			{"14A389",   {14,  0,  0}, "macOS", {"Yosemite",      {10, 10,  0}}},
+			{"14B25",    {14,  0,  0}, "macOS", {"Yosemite",      {10, 10,  1}}},
+			{"14C109",   {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
+			{"14C1510",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
+			{"14C2043",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
+			{"14C1514",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
+			{"14C2513",  {14,  1,  0}, "macOS", {"Yosemite",      {10, 10,  2}}},
+			{"14D131",   {14,  3,  0}, "macOS", {"Yosemite",      {10, 10,  3}}},
+			{"14D136",   {14,  3,  0}, "macOS", {"Yosemite",      {10, 10,  3}}},
+			{"14E46",    {14,  4,  0}, "macOS", {"Yosemite",      {10, 10,  4}}},
+			{"14F27",    {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1021",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1505",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1509",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1605",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1713",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1808",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1909",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F1912",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F2009",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F2109",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F2315",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F2411",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"14F2511",  {14,  5,  0}, "macOS", {"Yosemite",      {10, 10,  5}}},
+			{"15A284",   {15,  0,  0}, "macOS", {"El Capitan",    {10, 11,  0}}},
+			{"15B42",    {15,  0,  0}, "macOS", {"El Capitan",    {10, 11,  1}}},
+			{"15C50",    {15,  2,  0}, "macOS", {"El Capitan",    {10, 11,  2}}},
+			{"15D21",    {15,  3,  0}, "macOS", {"El Capitan",    {10, 11,  3}}},
+			{"15E65",    {15,  4,  0}, "macOS", {"El Capitan",    {10, 11,  4}}},
+			{"15F34",    {15,  5,  0}, "macOS", {"El Capitan",    {10, 11,  5}}},
+			{"15G31",    {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1004",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1011",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1108",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1212",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1217",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1421",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1510",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G1611",  {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G17023", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G18013", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G19009", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G20015", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G21013", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"15G22010", {15,  6,  0}, "macOS", {"El Capitan",    {10, 11,  6}}},
+			{"16A323",   {16,  0,  0}, "macOS", {"Sierra",        {10, 12,  0}}},
+			{"16B2555",  {16,  1,  0}, "macOS", {"Sierra",        {10, 12,  1}}},
+			{"16B2657",  {16,  1,  0}, "macOS", {"Sierra",        {10, 12,  1}}},
+			{"16C67",    {16,  3,  0}, "macOS", {"Sierra",        {10, 12,  2}}},
+			{"16C68",    {16,  3,  0}, "macOS", {"Sierra",        {10, 12,  2}}},
+			{"16D32",    {16,  4,  0}, "macOS", {"Sierra",        {10, 12,  3}}},
+			{"16E195",   {16,  5,  0}, "macOS", {"Sierra",        {10, 12,  4}}},
+			{"16F73",    {16,  6,  0}, "macOS", {"Sierra",        {10, 12,  5}}},
+			{"16F2073",  {16,  6,  0}, "macOS", {"Sierra",        {10, 12,  5}}},
+			{"16G29",    {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1036",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1114",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1212",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1314",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1408",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1510",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1618",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1710",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1815",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1917",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G1918",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G2016",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G2127",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G2128",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"16G2136",  {16,  7,  0}, "macOS", {"Sierra",        {10, 12,  6}}},
+			{"17A365",   {17,  0,  0}, "macOS", {"High Sierra",   {10, 13,  0}}},
+			{"17A405",   {17,  0,  0}, "macOS", {"High Sierra",   {10, 13,  0}}},
+			{"17B48",    {17,  2,  0}, "macOS", {"High Sierra",   {10, 13,  1}}},
+			{"17B1002",  {17,  2,  0}, "macOS", {"High Sierra",   {10, 13,  1}}},
+			{"17B1003",  {17,  2,  0}, "macOS", {"High Sierra",   {10, 13,  1}}},
+			{"17C88",    {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
+			{"17C89",    {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
+			{"17C205",   {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
+			{"17C2205",  {17,  3,  0}, "macOS", {"High Sierra",   {10, 13,  2}}},
+			{"17D47",    {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
+			{"17D2047",  {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
+			{"17D102",   {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
+			{"17D2102",  {17,  4,  0}, "macOS", {"High Sierra",   {10, 13,  3}}},
+			{"17E199",   {17,  5,  0}, "macOS", {"High Sierra",   {10, 13,  4}}},
+			{"17E202",   {17,  5,  0}, "macOS", {"High Sierra",   {10, 13,  4}}},
+			{"17F77",    {17,  6,  0}, "macOS", {"High Sierra",   {10, 13,  5}}},
+			{"17G65",    {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G2208",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G2307",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G3025",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G4015",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G5019",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G6029",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G6030",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G7024",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G8029",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G8030",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G8037",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G9016",  {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G10021", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G11023", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G12034", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G13033", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G13035", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G14019", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G14033", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"17G14042", {17,  7,  0}, "macOS", {"High Sierra",   {10, 13,  6}}},
+			{"18A391",   {18,  0,  0}, "macOS", {"Mojave",        {10, 14,  0}}},
+			{"18B75",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  1}}},
+			{"18B2107",  {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  1}}},
+			{"18B3094",  {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  1}}},
+			{"18C54",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  2}}},
+			{"18D42",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  3}}},
+			{"18D43",    {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  3}}},
+			{"18D109",   {18,  2,  0}, "macOS", {"Mojave",        {10, 14,  3}}},
+			{"18E226",   {18,  5,  0}, "macOS", {"Mojave",        {10, 14,  4}}},
+			{"18E227",   {18,  5,  0}, "macOS", {"Mojave",        {10, 14,  4}}},
+			{"18F132",   {18,  6,  0}, "macOS", {"Mojave",        {10, 14,  5}}},
+			{"18G84",    {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G87",    {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G95",    {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G103",   {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G1012",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G2022",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G3020",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G4032",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G5033",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G6020",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G6032",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G6042",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G7016",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G8012",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G8022",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G9028",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G9216",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"18G9323",  {18,  7,  0}, "macOS", {"Mojave",        {10, 14,  6}}},
+			{"19A583",   {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  0}}},
+			{"19A602",   {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  0}}},
+			{"19A603",   {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  0}}},
+			{"19B88",    {19,  0,  0}, "macOS", {"Catalina",      {10, 15,  1}}},
+			{"19C57",    {19,  2,  0}, "macOS", {"Catalina",      {10, 15,  2}}},
+			{"19C58",    {19,  2,  0}, "macOS", {"Catalina",      {10, 15,  2}}},
+			{"19D76",    {19,  3,  0}, "macOS", {"Catalina",      {10, 15,  3}}},
+			{"19E266",   {19,  4,  0}, "macOS", {"Catalina",      {10, 15,  4}}},
+			{"19E287",   {19,  4,  0}, "macOS", {"Catalina",      {10, 15,  4}}},
+			{"19F96",    {19,  5,  0}, "macOS", {"Catalina",      {10, 15,  5}}},
+			{"19F101",   {19,  5,  0}, "macOS", {"Catalina",      {10, 15,  5}}},
+			{"19G73",    {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  6}}},
+			{"19G2021",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  6}}},
+			{"19H2",     {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H4",     {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H15",    {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H114",   {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H512",   {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H524",   {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1030",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1217",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1323",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1417",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1419",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1519",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1615",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1713",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1715",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1824",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H1922",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"19H2026",  {19,  6,  0}, "macOS", {"Catalina",      {10, 15,  7}}},
+			{"20A2411",  {20,  1,  0}, "macOS", {"Big Sur",       {11,  0,  0}}},
+			{"20B29",    {20,  1,  0}, "macOS", {"Big Sur",       {11,  0,  1}}},
+			{"20B50",    {20,  1,  0}, "macOS", {"Big Sur",       {11,  0,  1}}},
+			{"20C69",    {20,  2,  0}, "macOS", {"Big Sur",       {11,  1,  0}}},
+			{"20D64",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  0}}},
+			{"20D74",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  1}}},
+			{"20D75",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  1}}},
+			{"20D80",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  2}}},
+			{"20D91",    {20,  3,  0}, "macOS", {"Big Sur",       {11,  2,  3}}},
+			{"20E232",   {20,  4,  0}, "macOS", {"Big Sur",       {11,  3,  0}}},
+			{"20E241",   {20,  4,  0}, "macOS", {"Big Sur",       {11,  3,  1}}},
+			{"20F71",    {20,  5,  0}, "macOS", {"Big Sur",       {11,  4,  0}}},
+			{"20G71",    {20,  6,  0}, "macOS", {"Big Sur",       {11,  5,  0}}},
+			{"20G80",    {20,  6,  0}, "macOS", {"Big Sur",       {11,  5,  1}}},
+			{"20G95",    {20,  6,  0}, "macOS", {"Big Sur",       {11,  5,  2}}},
+			{"20G165",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  0}}},
+			{"20G224",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  1}}},
+			{"20G314",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  2}}},
+			{"20G415",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  3}}},
+			{"20G417",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  4}}},
+			{"20G527",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  5}}},
+			{"20G624",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  6}}},
+			{"20G630",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  7}}},
+			{"20G730",   {20,  6,  0}, "macOS", {"Big Sur",       {11,  6,  8}}},
+			{"20G817",   {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   0}}},
+			{"20G918",   {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   1}}},
+			{"20G1020",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   2}}},
+			{"20G1116",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   3}}},
+			{"20G1120",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   4}}},
+			{"20G1225",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   5}}},
+			{"20G1231",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   6}}},
+			{"20G1345",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   7}}},
+			{"20G1351",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   8}}},
+			{"20G1426",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,   9}}},
+			{"20G1427",  {20,  6,  0}, "macOS", {"Big Sur",       {11, 7,  10}}},
+			{"21A344",   {21,  0,  1}, "macOS", {"Monterey",      {12,  0,  0}}},
+			{"21A559",   {21,  1,  0}, "macOS", {"Monterey",      {12,  0,  1}}},
+			{"21C52",    {21,  2,  0}, "macOS", {"Monterey",      {12,  1,  0}}},
+			{"21D49",    {21,  3,  0}, "macOS", {"Monterey",      {12,  2,  0}}},
+			{"21D62",    {21,  3,  0}, "macOS", {"Monterey",      {12,  2,  1}}},
+			{"21E230",   {21,  4,  0}, "macOS", {"Monterey",      {12,  3,  0}}},
+			{"21E258",   {21,  4,  0}, "macOS", {"Monterey",      {12,  3,  1}}},
+			{"21F79",    {21,  5,  0}, "macOS", {"Monterey",      {12,  4,  0}}},
+			{"21F2081",  {21,  5,  0}, "macOS", {"Monterey",      {12,  4,  0}}},
+			{"21F2092",  {21,  5,  0}, "macOS", {"Monterey",      {12,  4,  0}}},
+			{"21G72",    {21,  6,  0}, "macOS", {"Monterey",      {12,  5,  0}}},
+			{"21G83",    {21,  6,  0}, "macOS", {"Monterey",      {12,  5,  1}}},
+			{"21G115",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  0}}},
+			{"21G217",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  1}}},
+			{"21G320",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  2}}},
+			{"21G419",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  3}}},
+			{"21G526",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  4}}},
+			{"21G531",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  5}}},
+			{"21G646",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  6}}},
+			{"21G651",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  7}}},
+			{"21G725",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  8}}},
+			{"21G726",   {21,  6,  0}, "macOS", {"Monterey",      {12,  6,  9}}},
+			{"21G816",   {21,  6,  0}, "macOS", {"Monterey",      {12,  7,  0}}},
+			{"21G920",   {21,  6,  0}, "macOS", {"Monterey",      {12,  7,  1}}},
+			{"21G1974",  {21,  6,  0}, "macOS", {"Monterey",      {12,  7,  2}}},
+			{"22A380",   {13,  0,  0}, "macOS", {"Ventura",       {22,  1,  0}}},
+			{"22A400",   {13,  0,  1}, "macOS", {"Ventura",       {22,  1,  0}}},
+			{"22C65",    {13,  1,  0}, "macOS", {"Ventura",       {22,  2,  0}}},
+			{"22D49",    {13,  2,  0}, "macOS", {"Ventura",       {22,  3,  0}}},
+			{"22D68",    {13,  2,  1}, "macOS", {"Ventura",       {22,  3,  0}}},
+			{"22E252",   {13,  3,  0}, "macOS", {"Ventura",       {22,  4,  0}}},
+			{"22E261",   {13,  3,  1}, "macOS", {"Ventura",       {22,  4,  0}}},
+			{"22F66",    {13,  4,  0}, "macOS", {"Ventura",       {22,  5,  0}}},
+			{"22F82",    {13,  4,  1}, "macOS", {"Ventura",       {22,  5,  0}}},
+			{"22E772610a", {13, 4, 1}, "macOS", {"Ventura",       {22,  5,  0}}},
+			{"22F770820d", {13, 4, 1}, "macOS", {"Ventura",       {22,  5,  0}}},
+			{"22G74",    {13,  5,  0}, "macOS", {"Ventura",       {22,  6,  0}}},
+			{"22G90",    {13,  5,  1}, "macOS", {"Ventura",       {22,  6,  0}}},
+			{"22G91",    {13,  5,  2}, "macOS", {"Ventura",       {22,  6,  0}}},
+			{"22G120",   {13,  6,  0}, "macOS", {"Ventura",       {22,  6,  0}}},
+			{"22G313",   {13,  6,  1}, "macOS", {"Ventura",       {22,  6,  0}}},
+			{"22G320",   {13,  6,  2}, "macOS", {"Ventura",       {22,  6,  0}}},
+			{"23A344",   {23,  0,  0}, "macOS", {"Sonoma",        {14,  0,  0}}},
+			{"23B74",    {23,  1,  0}, "macOS", {"Sonoma",        {14,  1,  0}}},
+			{"23B81",    {23,  1,  0}, "macOS", {"Sonoma",        {14,  1,  1}}},
+			{"23B92",    {23,  1,  0}, "macOS", {"Sonoma",        {14,  1,  2}}},
+			{"23C64",    {23,  2,  0}, "macOS", {"Sonoma",        {14,  2,  0}}},
+			{"23C71",    {23,  2,  0}, "macOS", {"Sonoma",        {14,  2,  1}}},
+		};
+
+
+		b32 build_found  = 1;
+		b32 darwin_found = 1;
+		uint32_t major, minor, patch;
+
+		#define MACOS_VERSION_BUFFER_SIZE 100
+		char build_buffer[MACOS_VERSION_BUFFER_SIZE];
+		char darwin_buffer[MACOS_VERSION_BUFFER_SIZE];
+		size_t build_buffer_size  = MACOS_VERSION_BUFFER_SIZE - 1;
+		size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
+		#undef MACOS_VERSION_BUFFER_SIZE
+
+		int build_mibs[] = { CTL_KERN, KERN_OSVERSION };
+		if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) {
+			build_found = 0;
+		}
+
+		int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
+		if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
+			gb_printf("macOS Unknown\n");
+			return;
+		} else {
+			if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
+				darwin_found = 0;
+			}
+		}
+
+		// Scan table for match on BUILD
+		int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
+		Darwin_To_Release build_match = {};
+		Darwin_To_Release kernel_match = {};
+	
+		for (int build = 0; build < macos_release_count; build++) {
+			Darwin_To_Release rel = macos_release_map[build];
+			
+			// Do we have an exact match on the BUILD?
+			if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
+				build_match = rel;
+				break;
+			}
+			
+			// Do we have an exact Darwin match?
+			if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
+				kernel_match = rel;
+			}
+	
+			// Major kernel version needs to match exactly,
+			if (rel.darwin[0] == major) {
+				// No major version match yet.
+				if (!kernel_match.os_name) {
+					kernel_match = rel;
+				}
+				if (minor >= rel.darwin[1]) {
+					kernel_match = rel;
+					if (patch >= rel.darwin[2]) {
+						kernel_match = rel;
+					}
+				}
+			}
+		}
+	
+		Darwin_To_Release match = {};
+		if(!build_match.build) {
+			match = kernel_match;
+		} else {
+			match = build_match;
+		}
+
+		if (match.os_name) {
+			gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
+			if (match.release.version[1] > 0 || match.release.version[2] > 0) {
+				gb_printf(".%d", match.release.version[1]);
+			}
+			if (match.release.version[2] > 0) {
+				gb_printf(".%d", match.release.version[2]);
+			}
+			if (build_found) {
+				gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
+			} else {
+				gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);				
+			}
+			return;
+		}
+
+		if (build_found && darwin_found) {
+			gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
+			return;
+		} else if (build_found) {
+			gb_printf("macOS Unknown (build: %s)\n", build_buffer);
+			return;
+		} else if (darwin_found) {
+			gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
+			return;
+		}
+	#elif defined(GB_SYSTEM_OPENBSD)
+		struct utsname un;
+		
+		if (uname(&un) != -1) {
+			gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+		} else {
+			gb_printf("OpenBSD: Unknown\n");    
+		}
+	#elif defined(GB_SYSTEM_FREEBSD)
+		#define freebsd_version_buffer 129
+		char buffer[freebsd_version_buffer];
+		size_t buffer_size = freebsd_version_buffer - 1;
+		#undef freebsd_version_buffer
+
+		int mibs[] = { CTL_KERN, KERN_VERSION };
+		if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) {
+			gb_printf("FreeBSD: Unknown\n");
+		} else {
+			// KERN_VERSION can end in a \n, replace it with a \0
+			for (int i = 0; i < buffer_size; i += 1) {
+				if (buffer[i] == '\n') buffer[i] = 0;
+			}
+			gb_printf("%s", &buffer[0]);
+
+			// Retrieve kernel revision using `sysctl`, e.g. 199506
+			mibs[1] = KERN_OSREV;
+			uint64_t revision;
+			size_t revision_size = sizeof(revision);
+
+			if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) {
+				gb_printf("\n");
+			} else {
+				gb_printf(", revision %ld\n", revision);
+			}
+		}
+	#else
+		gb_printf("Unknown");
+	#endif
+}
+
+gb_internal void report_backend_info() {
+	gb_printf("\tBackend: LLVM %s\n", LLVM_VERSION_STRING);
+}
+
+// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
+gb_internal void print_bug_report_help() {
+	gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
+	gb_printf("\tWebsite: https://odin-lang.org\n");
+	gb_printf("\tGitHub:  https://github.com/odin-lang/Odin/issues\n");
+	/*
+		Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
+		gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
+	*/
+	gb_printf("\n\n");
+
+	gb_printf("Useful information to add to a bug report:\n\n");
+
+	gb_printf("\tOdin:    %.*s", LIT(ODIN_VERSION));
+
+	#ifdef NIGHTLY
+	gb_printf("-nightly");
+	#endif
+
+	#ifdef GIT_SHA
+	gb_printf(":%s", GIT_SHA);
+	#endif
+
+	gb_printf("\n");
+
+	/*
+		Print OS information.
+	*/
+	report_os_info();
+
+	/*
+		Now print CPU info.
+	*/
+	report_cpu_info();
+
+	/*
+		And RAM info.
+	*/
+	report_ram_info();
+
+	report_backend_info();
+}

+ 20 - 0
src/check_builtin.cpp

@@ -5820,6 +5820,26 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		}
 		break;
 
+	case BuiltinProc_type_bit_set_backing_type:
+		{
+			Operand op = {};
+			Type *type = check_type(c, ce->args[0]);
+			Type *bt = base_type(type);
+			if (bt == nullptr || bt == t_invalid) {
+				error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+				return false;
+			}
+			if (bt->kind != Type_BitSet) {
+				gbString s = type_to_string(type);
+				error(ce->args[0], "Expected a bit_set type for '%.*s', got %s", LIT(builtin_name), s);
+				return false;
+			}
+
+			operand->mode = Addressing_Type;
+			operand->type = bit_set_to_int(bt);
+			break;
+		}
+
 	case BuiltinProc_type_equal_proc:
 		{
 			Operand op = {};

+ 1 - 0
src/check_decl.cpp

@@ -210,6 +210,7 @@ gb_internal bool is_type_distinct(Ast *node) {
 	case Ast_UnionType:
 	case Ast_EnumType:
 	case Ast_ProcType:
+	case Ast_BitFieldType:
 		return true;
 
 	case Ast_PointerType:

+ 164 - 31
src/check_expr.cpp

@@ -100,7 +100,7 @@ gb_internal void     check_union_type               (CheckerContext *c, Type *un
 gb_internal Type *   check_init_variable            (CheckerContext *c, Entity *e, Operand *operand, String context_name);
 
 
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0);
 gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
 
 gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
@@ -1241,7 +1241,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
 	}
 	case Type_Pointer:
 		if (source->kind == Type_Pointer) {
-			isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem);
+			isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true);
 			if (level > 0) {
 				return true;
 			}
@@ -1413,7 +1413,9 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
 				return ok;
 
 			}
-			// return check_is_assignable_to(c, &o, poly);
+
+			// NOTE(bill): Check for subtypes of
+			// return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
 		}
 		return false;
 	case Type_Tuple:
@@ -1884,33 +1886,55 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
 
 		BigInt i = v.value_integer;
 
-		i64 bit_size = type_size_of(type);
+		i64 byte_size = type_size_of(type);
 		BigInt umax = {};
 		BigInt imin = {};
 		BigInt imax = {};
 
-		if (bit_size < 16) {
-			big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]);
-			big_int_from_i64(&imin, signed_integer_mins[bit_size]);
-			big_int_from_i64(&imax, signed_integer_maxs[bit_size]);
-		} else {
+		if (c->bit_field_bit_size > 0) {
+			i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size);
+
 			big_int_from_u64(&umax, 1);
 			big_int_from_i64(&imin, 1);
 			big_int_from_i64(&imax, 1);
 
-			BigInt bi128 = {};
-			BigInt bi127 = {};
-			big_int_from_i64(&bi128, 128);
-			big_int_from_i64(&bi127, 127);
+			BigInt bu = {};
+			BigInt bi = {};
+			big_int_from_i64(&bu, bit_size);
+			big_int_from_i64(&bi, bit_size-1);
 
-			big_int_shl_eq(&umax, &bi128);
+			big_int_shl_eq(&umax, &bu);
 			mp_decr(&umax);
 
-			big_int_shl_eq(&imin, &bi127);
+			big_int_shl_eq(&imin, &bi);
 			big_int_neg(&imin, &imin);
 
-			big_int_shl_eq(&imax, &bi127);
+			big_int_shl_eq(&imax, &bi);
 			mp_decr(&imax);
+		} else {
+			if (byte_size < 16) {
+				big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]);
+				big_int_from_i64(&imin, signed_integer_mins[byte_size]);
+				big_int_from_i64(&imax, signed_integer_maxs[byte_size]);
+			} else {
+				big_int_from_u64(&umax, 1);
+				big_int_from_i64(&imin, 1);
+				big_int_from_i64(&imax, 1);
+
+				BigInt bi128 = {};
+				BigInt bi127 = {};
+				big_int_from_i64(&bi128, 128);
+				big_int_from_i64(&bi127, 127);
+
+				big_int_shl_eq(&umax, &bi128);
+				mp_decr(&umax);
+
+				big_int_shl_eq(&imin, &bi127);
+				big_int_neg(&imin, &imin);
+
+				big_int_shl_eq(&imax, &bi127);
+				mp_decr(&imax);
+			}
 		}
 
 		switch (type->Basic.kind) {
@@ -2069,11 +2093,17 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
 }
 
 
-gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0) {
 	if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
 		gbString b = type_to_string(type);
 
 		i64 sz = type_size_of(type);
+		i64 bit_size = 8*sz;
+		bool size_changed = false;
+		if (max_bit_size > 0) {
+			size_changed = (bit_size != max_bit_size);
+			bit_size = gb_min(bit_size, max_bit_size);
+		}
 		BigInt *bi = &o->value.value_integer;
 		if (is_type_unsigned(type)) {
 			if (big_int_is_neg(bi)) {
@@ -2081,25 +2111,36 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o,
 			} else {
 				BigInt one = big_int_make_u64(1);
 				BigInt max_size = big_int_make_u64(1);
-				BigInt bits = big_int_make_i64(8*sz);
+				BigInt bits = big_int_make_i64(bit_size);
 				big_int_shl_eq(&max_size, &bits);
 				big_int_sub_eq(&max_size, &one);
 				String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
-				error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+
+				if (size_changed) {
+					error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+				} else {
+					error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+				}
 			}
 		} else {
 			BigInt zero = big_int_make_u64(0);
 			BigInt one = big_int_make_u64(1);
 			BigInt max_size = big_int_make_u64(1);
-			BigInt bits = big_int_make_i64(8*sz - 1);
+			BigInt bits = big_int_make_i64(bit_size - 1);
 			big_int_shl_eq(&max_size, &bits);
+
+			String max_size_str = {};
 			if (big_int_is_neg(bi)) {
 				big_int_neg(&max_size, &max_size);
-				String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
-				error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+				max_size_str = big_int_to_string(temporary_allocator(), &max_size);
 			} else {
 				big_int_sub_eq(&max_size, &one);
-				String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+				max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+			}
+
+			if (size_changed) {
+				error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+			} else {
 				error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
 			}
 		}
@@ -2110,7 +2151,7 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o,
 	}
 	return false;
 }
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) {
 	gbString a = expr_to_string(o->expr);
 	gbString b = type_to_string(type);
 	defer(
@@ -2141,7 +2182,7 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
 		error_line("\t            whereas slices in general are assumed to be mutable.\n");
 	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
 		error_line("\tSuggestion: the expression may be casted to %s\n", b);
-	} else if (check_integer_exceed_suggestion(c, o, type)) {
+	} else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) {
 		return;
 	}
 }
@@ -2215,13 +2256,18 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
 			if (!is_type_integer(o->type) && is_type_integer(type)) {
 				error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
 			} else {
+				i64 max_bit_size = 0;
+				if (ctx->bit_field_bit_size) {
+					max_bit_size = ctx->bit_field_bit_size;
+				}
+
 				if (are_types_identical(o->type, type)) {
 					error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b);
 				} else {
 					error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c);
 				}
 
-				check_assignment_error_suggestion(ctx, o, type);
+				check_assignment_error_suggestion(ctx, o, type, max_bit_size);
 			}
 		} else {
 			error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
@@ -2232,6 +2278,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
 }
 
 gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
+	if (o->expr && o->expr->kind == Ast_SelectorExpr) {
+		if (o->expr->SelectorExpr.is_bit_field) {
+			return true;
+		}
+	}
 	if (o->mode == Addressing_OptionalOk) {
 		Ast *expr = unselector_expr(o->expr);
 		if (expr->kind != Ast_TypeAssertion) {
@@ -2304,6 +2355,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
 				Entity *e = entity_of_node(ue->expr);
 				if (e != nullptr && (e->flags & EntityFlag_Param) != 0) {
 					error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
+				} else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) {
+					error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str);
 				} else {
 					switch (o->mode) {
 					case Addressing_Constant:
@@ -2877,6 +2930,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
 		}
 	}
 
+	if (is_type_bit_field(src)) {
+		return are_types_identical(core_type(src->BitField.backing_type), dst);
+	}
+	if (is_type_bit_field(dst)) {
+		return are_types_identical(src, core_type(dst->BitField.backing_type));
+	}
+
 	if (is_type_integer(src) && is_type_rune(dst)) {
 		return true;
 	}
@@ -5065,6 +5125,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
 	operand->type = entity->type;
 	operand->expr = node;
 
+	if (entity->flags & EntityFlag_BitFieldField) {
+		add_package_dependency(c, "runtime", "__write_bits");
+		add_package_dependency(c, "runtime", "__read_bits");
+	}
+
 	switch (entity->kind) {
 	case Entity_Constant:
 	operand->value = entity->Constant.value;
@@ -5078,6 +5143,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
 		}
 		break;
 	case Entity_Variable:
+		if (sel.is_bit_field) {
+			se->is_bit_field = true;
+		}
 		if (sel.indirect) {
 			operand->mode = Addressing_Variable;
 		} else if (operand->mode == Addressing_Context) {
@@ -8394,6 +8462,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
 	StringMap<String> fields_visited_through_raw_union = {};
 	defer (string_map_destroy(&fields_visited_through_raw_union));
 
+	String assignment_str = str_lit("structure literal");
+	if (bt->kind == Type_BitField) {
+		assignment_str = str_lit("bit_field literal");
+	}
+
 	for (Ast *elem : elems) {
 		if (elem->kind != Ast_FieldValue) {
 			error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
@@ -8415,17 +8488,26 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
 			continue;
 		}
 
-		Entity *field = bt->Struct.fields[sel.index[0]];
+		Entity *field = nullptr;
+		if (bt->kind == Type_Struct) {
+			field = bt->Struct.fields[sel.index[0]];
+		} else if (bt->kind == Type_BitField) {
+			field = bt->BitField.fields[sel.index[0]];
+		} else {
+			GB_PANIC("Unknown type");
+		}
+
+
 		add_entity_use(c, fv->field, field);
 		if (string_set_update(&fields_visited, name)) {
 			if (sel.index.count > 1) {
 				if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
 					error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
 				} else {
-					error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string));
+					error(fv->field, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str));
 				}
 			} else {
-				error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
+				error(fv->field, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str));
 			}
 			continue;
 		} else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
@@ -8433,11 +8515,13 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
 			continue;
 		}
 		if (sel.indirect) {
-			error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name));
+			error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str));
 			continue;
 		}
 
 		if (sel.index.count > 1) {
+			GB_ASSERT(bt->kind == Type_Struct);
+
 			if (is_constant) {
 				Type *ft = type;
 				for (i32 index : sel.index) {
@@ -8498,7 +8582,15 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
 			is_constant = check_is_operand_compound_lit_constant(c, &o);
 		}
 
-		check_assignment(c, &o, field->type, str_lit("structure literal"));
+		u8 prev_bit_field_bit_size = c->bit_field_bit_size;
+		if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) {
+			// HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+			c->bit_field_bit_size = field->Variable.bit_field_bit_size;
+		}
+
+		check_assignment(c, &o, field->type, assignment_str);
+
+		c->bit_field_bit_size = prev_bit_field_bit_size;
 	}
 }
 
@@ -9300,6 +9392,21 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 		}
 		break;
 	}
+	case Type_BitField: {
+		if (cl->elems.count == 0) {
+			break; // NOTE(bill): No need to init
+		}
+		is_constant = false;
+		if (cl->elems[0]->kind != Ast_FieldValue) {
+			gbString type_str = type_to_string(type);
+			error(node, "%s ('bit_field') compound literals are only allowed to contain 'field = value' elements", type_str);
+			gb_string_free(type_str);
+		} else {
+			check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+		}
+		break;
+	}
+
 
 	default: {
 		if (cl->elems.count == 0) {
@@ -11113,6 +11220,32 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
 	case_end;
 
 
+	case_ast_node(f, BitFieldField, node);
+		str = write_expr_to_string(str, f->name, shorthand);
+		str = gb_string_appendc(str, ": ");
+		str = write_expr_to_string(str, f->type, shorthand);
+		str = gb_string_appendc(str, " | ");
+		str = write_expr_to_string(str, f->bit_size, shorthand);
+	case_end;
+	case_ast_node(bf, BitFieldType, node);
+		str = gb_string_appendc(str, "bit_field ");
+		if (!shorthand) {
+			str = write_expr_to_string(str, bf->backing_type, shorthand);
+		}
+		str = gb_string_appendc(str, " {");
+		if (shorthand) {
+			str = gb_string_appendc(str, "...");
+		} else {
+			for_array(i, bf->fields) {
+				if (i > 0) {
+					str = gb_string_appendc(str, ", ");
+				}
+				str = write_expr_to_string(str, bf->fields[i], false);
+			}
+		}
+		str = gb_string_appendc(str, "}");
+	case_end;
+
 	case_ast_node(ia, InlineAsmExpr, node);
 		str = gb_string_appendc(str, "asm(");
 		for_array(i, ia->param_types) {

+ 10 - 0
src/check_stmt.cpp

@@ -485,7 +485,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
 	}
 	}
 
+	Entity *lhs_e = entity_of_node(lhs->expr);
+	u8 prev_bit_field_bit_size = ctx->bit_field_bit_size;
+	if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) {
+		// HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+		ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size;
+	}
+
 	check_assignment(ctx, rhs, assignment_type, str_lit("assignment"));
+
+	ctx->bit_field_bit_size = prev_bit_field_bit_size;
+
 	if (rhs->mode == Addressing_Invalid) {
 		return nullptr;
 	}

+ 212 - 0
src/check_type.cpp

@@ -89,6 +89,8 @@ gb_internal bool does_field_type_allow_using(Type *t) {
 		return true;
 	} else if (is_type_array(t)) {
 		return t->Array.count <= 4;
+	} else if (is_type_bit_field(t)) {
+		return true;
 	}
 	return false;
 }
@@ -925,6 +927,202 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
 	enum_type->Enum.max_value_index = max_value_index;
 }
 
+gb_internal bool is_valid_bit_field_backing_type(Type *type) {
+	if (type == nullptr) {
+		return false;
+	}
+	type = base_type(type);
+	if (is_type_untyped(type)) {
+		return false;
+	}
+	if (is_type_integer(type)) {
+		return true;
+	}
+	if (type->kind == Type_Array) {
+		return is_type_integer(type->Array.elem);
+	}
+	return false;
+}
+
+gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) {
+	ast_node(bf, BitFieldType, node);
+	GB_ASSERT(is_type_bit_field(bit_field_type));
+
+	Type *backing_type = check_type(ctx, bf->backing_type);
+	if (backing_type == nullptr || !is_valid_bit_field_backing_type(backing_type)) {
+		error(node, "Backing type for a bit_field must be an integer or an array of an integer");
+		return;
+	}
+
+	bit_field_type->BitField.backing_type = backing_type;
+	bit_field_type->BitField.scope = ctx->scope;
+
+	auto fields    = array_make<Entity *>(permanent_allocator(), 0, bf->fields.count);
+	auto bit_sizes = array_make<u8>      (permanent_allocator(), 0, bf->fields.count);
+	auto tags      = array_make<String>  (permanent_allocator(), 0, bf->fields.count);
+
+	u64 maximum_bit_size = 8 * type_size_of(backing_type);
+	u64 total_bit_size = 0;
+
+	for_array(i, bf->fields) {
+		i32 field_src_index = cast(i32)i;
+		Ast *field = bf->fields[i];
+		if (field->kind != Ast_BitFieldField) {
+			error(field, "Invalid AST for a bit_field");
+			continue;
+		}
+		ast_node(f, BitFieldField, field);
+		if (f->name == nullptr || f->name->kind != Ast_Ident) {
+			error(field, "A bit_field's field name must be an identifier");
+			continue;
+		}
+		CommentGroup *docs    = f->docs;
+		CommentGroup *comment = f->comment;
+
+		String name = f->name->Ident.token.string;
+
+		if (f->type == nullptr) {
+			error(field, "A bit_field's field must have a type");
+			continue;
+		}
+
+		Type *type = check_type(ctx, f->type);
+		if (type_size_of(type) > 8) {
+			error(f->type, "The type of a bit_field's field must be <= 8 bytes, got %lld", cast(long long)type_size_of(type));
+		}
+
+		if (is_type_untyped(type)) {
+			gbString s = type_to_string(type);
+			error(f->type, "The type of a bit_field's field must be a typed integer, enum, or boolean, got %s", s);
+			gb_string_free(s);
+		} else if (!(is_type_integer(type) || is_type_enum(type) || is_type_boolean(type))) {
+			gbString s = type_to_string(type);
+			error(f->type, "The type of a bit_field's field must be an integer, enum, or boolean, got %s", s);
+			gb_string_free(s);
+		}
+
+		if (f->bit_size == nullptr) {
+			error(field, "A bit_field's field must have a specified bit size");
+			continue;
+		}
+
+
+		Operand o = {};
+		check_expr(ctx, &o, f->bit_size);
+		if (o.mode != Addressing_Constant) {
+			error(f->bit_size, "A bit_field's specified bit size must be a constant");
+			o.mode = Addressing_Invalid;
+		}
+		if (o.value.kind == ExactValue_Float) {
+			o.value = exact_value_to_integer(o.value);
+		}
+		if (f->bit_size->kind == Ast_BinaryExpr && f->bit_size->BinaryExpr.op.kind == Token_Or) {
+			gbString s = expr_to_string(f->bit_size);
+			error(f->bit_size, "Wrap the expression in parentheses, e.g. (%s)", s);
+			gb_string_free(s);
+		}
+
+		ExactValue bit_size = o.value;
+
+		if (bit_size.kind != ExactValue_Integer) {
+			gbString s = expr_to_string(f->bit_size);
+			error(f->bit_size, "Expected an integer constant value for the specified bit size, got %s", s);
+			gb_string_free(s);
+		}
+
+		if (scope_lookup_current(ctx->scope, name) != nullptr) {
+			error(f->name, "'%.*s' is already declared in this bit_field", LIT(name));
+		} else {
+			i64 bit_size_i64 = exact_value_to_i64(bit_size);
+			u8 bit_size_u8 = 0;
+			if (bit_size_i64 <= 0) {
+				error(f->bit_size, "A bit_field's specified bit size cannot be <= 0, got %lld", cast(long long)bit_size_i64);
+				bit_size_i64 = 1;
+			}
+			if (bit_size_i64 > 64) {
+				error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64);
+				bit_size_i64 = 64;
+			}
+			i64 sz = 8*type_size_of(type);
+			if (bit_size_i64 > sz) {
+				error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz);
+				bit_size_i64 = sz;
+			}
+
+			bit_size_u8 = cast(u8)bit_size_i64;
+
+			Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index);
+			e->Variable.docs    = docs;
+			e->Variable.comment = comment;
+			e->Variable.bit_field_bit_size = bit_size_u8;
+			e->flags |= EntityFlag_BitFieldField;
+
+			add_entity(ctx, ctx->scope, nullptr, e);
+			array_add(&fields, e);
+			array_add(&bit_sizes, bit_size_u8);
+
+			String tag = f->tag.string;
+			if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
+				error(f->tag, "Invalid string literal");
+				tag = {};
+			}
+			array_add(&tags, tag);
+
+			add_entity_use(ctx, field, e);
+		}
+	}
+
+	GB_ASSERT(fields.count <= bf->fields.count);
+
+	auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count);
+	i64 curr_offset = 0;
+	for_array(i, bit_sizes) {
+		bit_offsets[i] = curr_offset;
+		curr_offset += cast(i64)bit_sizes[i];
+	}
+
+	if (total_bit_size > maximum_bit_size) {
+		gbString s = type_to_string(backing_type);
+		error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu",
+		      cast(unsigned long long)total_bit_size,
+		      s,
+		      cast(unsigned long long)maximum_bit_size);
+		gb_string_free(s);
+	}
+
+	if (bit_sizes.count > 0 && is_type_integer(backing_type)) {
+		bool all_booleans = is_type_boolean(fields[0]->type);
+		bool all_ones = bit_sizes[0] == 1;
+		if (all_ones && all_booleans) {
+			for_array(i, bit_sizes) {
+				all_ones = bit_sizes[i] == 1;
+				if (!all_ones) {
+					break;
+				}
+				all_booleans = is_type_boolean(fields[i]->type);
+				if (!all_booleans) {
+					break;
+				}
+			}
+			if (all_ones && all_booleans) {
+				if (build_context.vet_flags & VetFlag_Style) {
+					char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)";
+					error(node, msg);
+				} else {
+					char const *msg = "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer";
+					warning(node, msg);
+				}
+			}
+		}
+	}
+
+
+	bit_field_type->BitField.fields      = slice_from_array(fields);
+	bit_field_type->BitField.bit_sizes   = slice_from_array(bit_sizes);
+	bit_field_type->BitField.bit_offsets = bit_offsets;
+	bit_field_type->BitField.tags        = tags.data;
+}
+
 gb_internal bool is_type_valid_bit_set_range(Type *t) {
 	if (is_type_integer(t)) {
 		return true;
@@ -3051,6 +3249,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
 		return true;
 	case_end;
 
+	case_ast_node(bf, BitFieldType, e);
+		bool ips = ctx->in_polymorphic_specialization;
+		defer (ctx->in_polymorphic_specialization = ips);
+		ctx->in_polymorphic_specialization = false;
+
+		*type = alloc_type_bit_field();
+		set_base_type(named_type, *type);
+		check_open_scope(ctx, e);
+		check_bit_field_type(ctx, *type, named_type, e);
+		check_close_scope(ctx);
+		(*type)->BitField.node = e;
+		return true;
+	case_end;
+
 
 	case_ast_node(pt, ProcType, e);
 		bool ips = ctx->in_polymorphic_specialization;

+ 18 - 0
src/checker.cpp

@@ -313,6 +313,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) {
 	case Ast_StructType:      node->StructType.scope      = scope; break;
 	case Ast_UnionType:       node->UnionType.scope       = scope; break;
 	case Ast_EnumType:        node->EnumType.scope        = scope; break;
+	case Ast_BitFieldType:    node->BitFieldType.scope    = scope; break;
 	default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
 	}
 }
@@ -334,6 +335,7 @@ gb_internal Scope *scope_of_node(Ast *node) {
 	case Ast_StructType:      return node->StructType.scope;
 	case Ast_UnionType:       return node->UnionType.scope;
 	case Ast_EnumType:        return node->EnumType.scope;
+	case Ast_BitFieldType:    return node->BitFieldType.scope;
 	}
 	GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
 	return nullptr;
@@ -355,6 +357,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
 	case Ast_EnumType:
 	case Ast_UnionType:
 	case Ast_BitSetType:
+	case Ast_BitFieldType:
 		scope->flags |= ScopeFlag_Type;
 		break;
 	}
@@ -2060,6 +2063,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		add_type_info_type_internal(c, bt->SoaPointer.elem);
 		break;
 
+	case Type_BitField:
+		add_type_info_type_internal(c, bt->BitField.backing_type);
+		for (Entity *f : bt->BitField.fields) {
+			add_type_info_type_internal(c, f->type);
+		}
+		break;
 
 	case Type_Generic:
 		break;
@@ -2309,6 +2318,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
 		add_min_dep_type_info(c, bt->SoaPointer.elem);
 		break;
 
+	case Type_BitField:
+		add_min_dep_type_info(c, bt->BitField.backing_type);
+		for (Entity *f : bt->BitField.fields) {
+			add_min_dep_type_info(c, f->type);
+		}
+		break;
+
 	default:
 		GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind]));
 		break;
@@ -2907,6 +2923,7 @@ gb_internal void init_core_type_info(Checker *c) {
 	t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer"));
 	t_type_info_matrix           = find_core_type(c, str_lit("Type_Info_Matrix"));
 	t_type_info_soa_pointer      = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
+	t_type_info_bit_field        = find_core_type(c, str_lit("Type_Info_Bit_Field"));
 
 	t_type_info_named_ptr            = alloc_type_pointer(t_type_info_named);
 	t_type_info_integer_ptr          = alloc_type_pointer(t_type_info_integer);
@@ -2936,6 +2953,7 @@ gb_internal void init_core_type_info(Checker *c) {
 	t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer);
 	t_type_info_matrix_ptr           = alloc_type_pointer(t_type_info_matrix);
 	t_type_info_soa_pointer_ptr      = alloc_type_pointer(t_type_info_soa_pointer);
+	t_type_info_bit_field_ptr        = alloc_type_pointer(t_type_info_bit_field);
 }
 
 gb_internal void init_mem_allocator(Checker *c) {

+ 1 - 0
src/checker.hpp

@@ -475,6 +475,7 @@ struct CheckerContext {
 	bool       hide_polymorphic_errors;
 	bool       in_polymorphic_specialization;
 	bool       allow_arrow_right_selector_expr;
+	u8         bit_field_bit_size;
 	Scope *    polymorphic_scope;
 
 	Ast *assignment_lhs_hint;

+ 4 - 0
src/checker_builtin_procs.hpp

@@ -282,6 +282,8 @@ BuiltinProc__type_simple_boolean_end,
 
 	BuiltinProc_type_field_index_of,
 
+	BuiltinProc_type_bit_set_backing_type,
+
 	BuiltinProc_type_equal_proc,
 	BuiltinProc_type_hasher_proc,
 	BuiltinProc_type_map_info,
@@ -586,6 +588,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 	{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
+	{STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("type_equal_proc"),    1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_hasher_proc"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_map_info"),      1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 6 - 3
src/docs_format.cpp

@@ -14,8 +14,8 @@ struct OdinDocVersionType {
 };
 
 #define OdinDocVersionType_Major 0
-#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 4
+#define OdinDocVersionType_Minor 3
+#define OdinDocVersionType_Patch 0
 
 struct OdinDocHeaderBase {
 	u8                 magic[8];
@@ -84,6 +84,7 @@ enum OdinDocTypeKind : u32 {
 	OdinDocType_MultiPointer         = 22,
 	OdinDocType_Matrix               = 23,
 	OdinDocType_SoaPointer           = 24,
+	OdinDocType_BitField             = 25,
 };
 
 enum OdinDocTypeFlag_Basic : u32 {
@@ -170,6 +171,8 @@ enum OdinDocEntityFlag : u64 {
 	OdinDocEntityFlag_Param_NoAlias  = 1ull<<7,
 	OdinDocEntityFlag_Param_AnyInt   = 1ull<<8,
 
+	OdinDocEntityFlag_BitField_Field = 1ull<<19,
+
 	OdinDocEntityFlag_Type_Alias = 1ull<<20,
 
 	OdinDocEntityFlag_Builtin_Pkg_Builtin    = 1ull<<30,
@@ -192,7 +195,7 @@ struct OdinDocEntity {
 	u32                reserved_for_init;
 	OdinDocString      comment; // line comment
 	OdinDocString      docs; // preceding comment
-	i32                field_group_index;
+	i32                field_group_index; // For `bit_field`s this is the "bit_size"
 	OdinDocEntityIndex foreign_library;
 	OdinDocString      link_name;
 	OdinDocArray<OdinDocAttribute> attributes;

+ 24 - 1
src/docs_writer.cpp

@@ -615,6 +615,20 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 		}
 		break;
+	case Type_BitField:
+		doc_type.kind = OdinDocType_BitField;
+		{
+			auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->BitField.fields.count);
+			defer (array_free(&fields));
+
+			for_array(i, type->BitField.fields) {
+				fields[i] = odin_doc_add_entity(w, type->BitField.fields[i]);
+			}
+			doc_type.entities = odin_write_slice(w, fields.data, fields.count);
+			doc_type.types = odin_doc_type_as_slice(w, type->BitField.backing_type);
+		}
+		break;
+
 	case Type_Struct:
 		doc_type.kind = OdinDocType_Struct;
 		if (type->Struct.soa_kind != StructSoa_None) {
@@ -863,6 +877,10 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
 		}
 		break;
 	case Entity_Variable:
+		if (e->flags & EntityFlag_BitFieldField) {
+			flags |= OdinDocEntityFlag_BitField_Field;
+		}
+
 		if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
 		if (e->Variable.is_export)  { flags |= OdinDocEntityFlag_Export;  }
 		if (e->Variable.thread_local_model != "") {
@@ -873,7 +891,12 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
 		if (init_expr == nullptr) {
 			init_expr = e->Variable.init_expr;
 		}
-		field_group_index = e->Variable.field_group_index;
+
+		if (e->flags & EntityFlag_BitFieldField) {
+			field_group_index = -cast(i32)e->Variable.bit_field_bit_size;
+		} else {
+			field_group_index = e->Variable.field_group_index;
+		}
 		break;
 	case Entity_Constant:
 		field_group_index = e->Constant.field_group_index;

+ 2 - 0
src/entity.cpp

@@ -43,6 +43,7 @@ enum EntityFlag : u64 {
 	EntityFlag_NoAlias       = 1ull<<9,
 	EntityFlag_TypeField     = 1ull<<10,
 	EntityFlag_Value         = 1ull<<11,
+	EntityFlag_BitFieldField = 1ull<<12,
 
 
 
@@ -212,6 +213,7 @@ struct Entity {
 			Ast *init_expr; // only used for some variables within procedure bodies
 			i32  field_index;
 			i32  field_group_index;
+			u8   bit_field_bit_size;
 
 			ParameterValue param_value;
 

+ 17 - 55
src/llvm_backend.cpp

@@ -1053,41 +1053,6 @@ struct lbGlobalVariable {
 	bool is_initialized;
 };
 
-gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) {
-	if (build_context.no_rtti) {
-		return nullptr;
-	}
-	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
-
-	lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type);
-	p->is_startup = true;
-	LLVMSetLinkage(p->value, LLVMInternalLinkage);
-
-	lb_add_attribute_to_proc(m, p->value, "nounwind");
-	// lb_add_attribute_to_proc(p->module, p->value, "mustprogress");
-	// lb_add_attribute_to_proc(p->module, p->value, "nofree");
-	// lb_add_attribute_to_proc(p->module, p->value, "norecurse");
-	// lb_add_attribute_to_proc(p->module, p->value, "nosync");
-	// lb_add_attribute_to_proc(p->module, p->value, "willreturn");
-	if (!LB_USE_GIANT_PACKED_STRUCT) {
-		lb_add_attribute_to_proc(m, p->value, "optnone");
-		lb_add_attribute_to_proc(m, p->value, "noinline");
-	}
-
-	lb_begin_procedure_body(p);
-
-	lb_setup_type_info_data(p);
-
-	lb_end_procedure_body(p);
-
-	if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
-		gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
-		LLVMDumpValue(p->value);
-		gb_printf_err("\n\n\n\n");
-		LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
-	}
-	return p;
-}
 
 gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
 	if (build_context.metrics.os != TargetOs_darwin) {
@@ -1129,7 +1094,7 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) {
 	lb_end_procedure_body(p);
 }
 
-gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
+gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
 	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
 
 	lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
@@ -1139,9 +1104,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
 
 	lb_begin_procedure_body(p);
 
-	if (startup_type_info) {
-		LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, startup_type_info->type), startup_type_info->value, nullptr, 0, "");
-	}
+	lb_setup_type_info_data(main_module);
 
 	if (objc_names) {
 		LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
@@ -1201,7 +1164,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
 				lbValue data = lb_emit_struct_ep(p, var.var, 0);
 				lbValue ti   = lb_emit_struct_ep(p, var.var, 1);
 				lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
-				lb_emit_store(p, ti,   lb_type_info(main_module, var_type));
+				lb_emit_store(p, ti,   lb_type_info(p, var_type));
 			} else {
 				LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
 				lbValue src0 = lb_emit_conv(p, var.init, t);
@@ -1426,7 +1389,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
 	}
 
 	if (m == &m->gen->default_module) {
-		lb_llvm_function_pass_per_function_internal(m, m->gen->startup_type_info);
 		lb_llvm_function_pass_per_function_internal(m, m->gen->startup_runtime);
 		lb_llvm_function_pass_per_function_internal(m, m->gen->cleanup_runtime);
 		lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
@@ -2691,17 +2653,19 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 
 		{ // Add type info data
 			isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
-			Type *t = alloc_type_array(t_type_info, max_type_info_count);
+			Type *t = alloc_type_array(t_type_info_ptr, max_type_info_count);
 
 			// IMPORTANT NOTE(bill): As LLVM does not have a union type, an array of unions cannot be initialized
 			// at compile time without cheating in some way. This means to emulate an array of unions is to use
 			// a giant packed struct of "corrected" data types.
 
-			LLVMTypeRef internal_llvm_type = lb_setup_type_info_data_internal_type(m, max_type_info_count);
+			LLVMTypeRef internal_llvm_type = lb_type(m, t);
 
 			LLVMValueRef g = LLVMAddGlobal(m->mod, internal_llvm_type, LB_TYPE_INFO_DATA_NAME);
 			LLVMSetInitializer(g, LLVMConstNull(internal_llvm_type));
 			LLVMSetLinkage(g, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
+			LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
+			LLVMSetGlobalConstant(g, /*true*/false);
 
 			lbValue value = {};
 			value.value = g;
@@ -2710,15 +2674,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 			lb_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(LB_TYPE_INFO_DATA_NAME), t, EntityState_Resolved);
 			lb_add_entity(m, lb_global_type_info_data_entity, value);
 
-			if (LB_USE_GIANT_PACKED_STRUCT) {
-				LLVMSetLinkage(g, LLVMPrivateLinkage);
-				LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
-				LLVMSetGlobalConstant(g, /*true*/false);
-			}
 		}
 		{ // Type info member buffer
 			// NOTE(bill): Removes need for heap allocation by making it global memory
 			isize count = 0;
+			isize offsets_extra = 0;
 
 			for (Type *t : m->info->type_info_types) {
 				isize index = lb_type_info_index(m->info, t, false);
@@ -2736,6 +2696,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 				case Type_Tuple:
 					count += t->Tuple.variables.count;
 					break;
+				case Type_BitField:
+					count += t->BitField.fields.count;
+					// Twice is needed for the bit_offsets
+					offsets_extra += t->BitField.fields.count;
+					break;
 				}
 			}
 
@@ -2744,15 +2709,13 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 				LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
 				LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
 				LLVMSetLinkage(g, LLVMInternalLinkage);
-				if (LB_USE_GIANT_PACKED_STRUCT) {
-					lb_make_global_private_const(g);
-				}
+				lb_make_global_private_const(g);
 				return lb_addr({g, alloc_type_pointer(t)});
 			};
 
 			lb_global_type_info_member_types   = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME,   t_type_info_ptr, count);
 			lb_global_type_info_member_names   = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME,   t_string,        count);
-			lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr,       count);
+			lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr,       count+offsets_extra);
 			lb_global_type_info_member_usings  = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME,  t_bool,          count);
 			lb_global_type_info_member_tags    = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME,    t_string,        count);
 		}
@@ -2915,12 +2878,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 		}
 	}
 
-	TIME_SECTION("LLVM Runtime Type Information Creation");
-	gen->startup_type_info = lb_create_startup_type_info(default_module);
+	TIME_SECTION("LLVM Runtime Objective-C Names Creation");
 	gen->objc_names = lb_create_objc_names(default_module);
 
 	TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))");
-	gen->startup_runtime = lb_create_startup_runtime(default_module, gen->startup_type_info, gen->objc_names, global_variables);
+	gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables);
 
 	TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)");
 	gen->cleanup_runtime = lb_create_cleanup_runtime(default_module);

+ 9 - 3
src/llvm_backend.hpp

@@ -84,6 +84,8 @@ enum lbAddrKind {
 
 	lbAddr_Swizzle,
 	lbAddr_SwizzleLarge,
+
+	lbAddr_BitField,
 };
 
 struct lbAddr {
@@ -118,6 +120,12 @@ struct lbAddr {
 			Type *type;
 			Slice<i32> indices;
 		} swizzle_large;
+		struct {
+			Type *type;
+			i64 index;
+			i64 bit_offset;
+			i64 bit_size;
+		} bitfield;
 	};
 };
 
@@ -217,7 +225,6 @@ struct lbGenerator : LinkerData {
 	std::atomic<u32> global_array_index;
 	std::atomic<u32> global_generated_index;
 
-	lbProcedure *startup_type_info;
 	lbProcedure *startup_runtime;
 	lbProcedure *cleanup_runtime;
 	lbProcedure *objc_names;
@@ -478,7 +485,7 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu
 
 gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len);
 
-gb_internal lbValue lb_type_info(lbModule *m, Type *type);
+gb_internal lbValue lb_type_info(lbProcedure *p, Type *type);
 
 gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
 gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
@@ -579,7 +586,6 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen
 
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_CLEANUP_RUNTIME_PROC_NAME   "__$cleanup_runtime"
-#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
 #define LB_TYPE_INFO_DATA_NAME       "__$type_info_data"
 #define LB_TYPE_INFO_TYPES_NAME      "__$type_info_types_data"
 #define LB_TYPE_INFO_NAMES_NAME      "__$type_info_names_data"

+ 36 - 0
src/llvm_backend_debug.cpp

@@ -461,6 +461,42 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 			lb_debug_type(m, type->Matrix.elem),
 			subscripts, gb_count_of(subscripts));
 	}
+
+	case Type_BitField: {
+		LLVMMetadataRef parent_scope = nullptr;
+		LLVMMetadataRef scope = nullptr;
+		LLVMMetadataRef file = nullptr;
+		unsigned line = 0;
+		u64 size_in_bits = 8*cast(u64)type_size_of(type);
+		u32 align_in_bits = 8*cast(u32)type_align_of(type);
+		LLVMDIFlags flags = LLVMDIFlagZero;
+
+		unsigned element_count = cast(unsigned)type->BitField.fields.count;
+		LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+
+		u64 offset_in_bits = 0;
+		for (unsigned i = 0; i < element_count; i++) {
+			Entity *f = type->BitField.fields[i];
+			u8 bit_size = type->BitField.bit_sizes[i];
+			GB_ASSERT(f->kind == Entity_Variable);
+			String name = f->token.string;
+			unsigned field_line = 0;
+			LLVMDIFlags field_flags = LLVMDIFlagZero;
+			elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
+				bit_size, offset_in_bits, offset_in_bits,
+				field_flags, lb_debug_type(m, f->type)
+			);
+
+			offset_in_bits += bit_size;
+		}
+
+
+		return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line,
+			size_in_bits, align_in_bits, flags,
+			nullptr, elements, element_count, 0, nullptr,
+			"", 0
+		);
+	}
 	}
 
 	GB_PANIC("Invalid type %s", type_to_string(type));

+ 74 - 0
src/llvm_backend_expr.cpp

@@ -1946,6 +1946,24 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		}
 	}
 
+	// bit_field <-> backing type
+	if (is_type_bit_field(src)) {
+		if (are_types_identical(src->BitField.backing_type, dst)) {
+			lbValue res = {};
+			res.type = t;
+			res.value = value.value;
+			return res;
+		}
+	}
+	if (is_type_bit_field(dst)) {
+		if (are_types_identical(src, dst->BitField.backing_type)) {
+			lbValue res = {};
+			res.type = t;
+			res.value = value.value;
+			return res;
+		}
+	}
+
 
 	// Pointer <-> uintptr
 	if (is_type_pointer(src) && is_type_uintptr(dst)) {
@@ -4217,6 +4235,38 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 	switch (bt->kind) {
 	default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
 
+	case Type_BitField:
+		for (Ast *elem : cl->elems) {
+			ast_node(fv, FieldValue, elem);
+			String name = fv->field->Ident.token.string;
+			Selection sel = lookup_field(bt, name, false);
+			GB_ASSERT(sel.is_bit_field);
+			GB_ASSERT(!sel.indirect);
+			GB_ASSERT(sel.index.count == 1);
+			GB_ASSERT(sel.entity != nullptr);
+
+			i64 index = sel.index[0];
+			i64 bit_offset = 0;
+			i64 bit_size = -1;
+			for_array(i, bt->BitField.fields) {
+				Entity *f = bt->BitField.fields[i];
+				if (f == sel.entity) {
+					bit_offset = bt->BitField.bit_offsets[i];
+					bit_size   = bt->BitField.bit_sizes[i];
+					break;
+				}
+			}
+			GB_ASSERT(bit_size > 0);
+
+			Type *field_type = sel.entity->type;
+			lbValue field_expr = lb_build_expr(p, fv->value);
+			field_expr = lb_emit_conv(p, field_expr, field_type);
+
+			lbAddr field_addr = lb_addr_bit_field(v.addr, field_type, index, bit_offset, bit_size);
+			lb_addr_store(p, field_addr, field_expr);
+		}
+		return v;
+
 	case Type_Struct: {
 		// TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
 		// NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR
@@ -4634,6 +4684,30 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 				return lb_addr(lb_find_value_from_entity(p->module, e));
 			}
 
+			if (sel.is_bit_field) {
+				lbAddr addr = lb_build_addr(p, se->expr);
+
+				Selection sub_sel = sel;
+				sub_sel.index.count -= 1;
+
+				lbValue ptr = lb_addr_get_ptr(p, addr);
+				if (sub_sel.index.count > 0) {
+					ptr = lb_emit_deep_field_gep(p, ptr, sub_sel);
+				}
+
+				Type *bf_type = type_deref(ptr.type);
+				bf_type = base_type(type_deref(bf_type));
+				GB_ASSERT(bf_type->kind == Type_BitField);
+
+				i32 index = sel.index[sel.index.count-1];
+
+				Entity *f = bf_type->BitField.fields[index];
+				u8 bit_size = bf_type->BitField.bit_sizes[index];
+				i64 bit_offset = bf_type->BitField.bit_offsets[index];
+
+				return lb_addr_bit_field(ptr, f->type, index, bit_offset, bit_size);
+			}
+
 			{
 				lbAddr addr = lb_build_addr(p, se->expr);
 				if (addr.kind == lbAddr_Map) {

+ 52 - 3
src/llvm_backend_general.cpp

@@ -451,6 +451,20 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice<i
 	return v;
 }
 
+gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 index, i64 bit_offset, i64 bit_size) {
+	GB_ASSERT(is_type_pointer(addr.type));
+	Type *mt = type_deref(addr.type);
+	GB_ASSERT(is_type_bit_field(mt));
+
+	lbAddr v = {lbAddr_BitField, addr};
+	v.bitfield.type       = type;
+	v.bitfield.index      = index;
+	v.bitfield.bit_offset = bit_offset;
+	v.bitfield.bit_size   = bit_size;
+	return v;
+}
+
+
 gb_internal Type *lb_addr_type(lbAddr const &addr) {
 	if (addr.addr.value == nullptr) {
 		return nullptr;
@@ -759,7 +773,17 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
 		addr = lb_addr(lb_address_from_load(p, lb_addr_load(p, addr)));
 	}
 
-	if (addr.kind == lbAddr_RelativePointer) {
+	if (addr.kind == lbAddr_BitField) {
+		lbValue dst = addr.addr;
+
+		auto args = array_make<lbValue>(temporary_allocator(), 4);
+		args[0] = dst;
+		args[1] = lb_address_from_load_or_generate_local(p, value);
+		args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+		args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+		lb_emit_runtime_call(p, "__write_bits", args);
+		return;
+	} else if (addr.kind == lbAddr_RelativePointer) {
 		Type *rel_ptr = base_type(lb_addr_type(addr));
 		GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
 		          rel_ptr->kind == Type_RelativeMultiPointer);
@@ -1074,8 +1098,31 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) {
 gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
 	GB_ASSERT(addr.addr.value != nullptr);
 
+	if (addr.kind == lbAddr_BitField) {
+		lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true);
+		lbValue src = addr.addr;
 
-	if (addr.kind == lbAddr_RelativePointer) {
+		auto args = array_make<lbValue>(temporary_allocator(), 4);
+		args[0] = dst.addr;
+		args[1] = src;
+		args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+		args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+		lb_emit_runtime_call(p, "__read_bits", args);
+
+		lbValue r = lb_addr_load(p, dst);
+
+		if (!is_type_unsigned(core_type(addr.bitfield.type))) {
+			// Sign extension
+			// m := 1<<(bit_size-1)
+			// r = (r XOR m) - m
+			Type *t = addr.bitfield.type;
+			lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1));
+			r = lb_emit_arith(p, Token_Xor, r, m, t);
+			r = lb_emit_arith(p, Token_Sub, r, m, t);
+		}
+
+		return r;
+	} else if (addr.kind == lbAddr_RelativePointer) {
 		Type *rel_ptr = base_type(lb_addr_type(addr));
 		Type *base_integer = nullptr;
 		Type *pointer_type = nullptr;
@@ -2216,7 +2263,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 			}
 			return LLVMStructTypeInContext(ctx, fields, field_count, false);
 		}
-	
+
+	case Type_BitField:
+		return lb_type_internal(m, type->BitField.backing_type);
 	}
 
 	GB_PANIC("Invalid type %s", type_to_string(type));

+ 3 - 3
src/llvm_backend_proc.cpp

@@ -1755,7 +1755,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 		TypeAndValue tav = type_and_value_of_expr(arg);
 		if (tav.mode == Addressing_Type) {
 			Type *t = default_type(type_of_expr(arg));
-			return lb_type_info(p->module, t);
+			return lb_type_info(p, t);
 		}
 		GB_ASSERT(is_type_typeid(tav.type));
 
@@ -3361,9 +3361,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 					for (Ast *var_arg : variadic) {
 						lbValue arg = lb_build_expr(p, var_arg);
 						if (is_type_any(elem_type)) {
-							array_add(&args, lb_emit_conv(p, arg, default_type(arg.type)));
+							array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(default_type(arg.type))));
 						} else {
-							array_add(&args, lb_emit_conv(p, arg, elem_type));
+							array_add(&args, lb_emit_conv(p, arg, c_vararg_promote_type(elem_type)));
 						}
 					}
 					break;

+ 18 - 5
src/llvm_backend_stmt.cpp

@@ -748,7 +748,7 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_
 	i64 enum_count = t->Enum.fields.count;
 	lbValue max_count = lb_const_int(m, t_int, enum_count);
 
-	lbValue ti          = lb_type_info(m, t);
+	lbValue ti          = lb_type_info(p, t);
 	lbValue variant     = lb_emit_struct_ep(p, ti, 4);
 	lbValue eti_ptr     = lb_emit_conv(p, variant, t_type_info_enum_ptr);
 	lbValue values      = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
@@ -1843,7 +1843,11 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
 
 		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
 
-		LLVMBuildRetVoid(p->builder);
+		// Check for terminator in the defer stmts
+		LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+		if (!lb_is_instr_terminating(instr)) {
+			LLVMBuildRetVoid(p->builder);
+		}
 	} else {
 		LLVMValueRef ret_val = res.value;
 		LLVMTypeRef ret_type = p->abi_function_type->ret.type;
@@ -1868,7 +1872,12 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
 		}
 
 		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
-		LLVMBuildRet(p->builder, ret_val);
+
+		// Check for terminator in the defer stmts
+		LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+		if (!lb_is_instr_terminating(instr)) {
+			LLVMBuildRet(p->builder, ret_val);
+		}
 	}
 }
 gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
@@ -1887,8 +1896,12 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
 		// No return values
 
 		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
-
-		LLVMBuildRetVoid(p->builder);
+		
+		// Check for terminator in the defer stmts
+		LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+		if (!lb_is_instr_terminating(instr)) {
+			LLVMBuildRetVoid(p->builder);
+		}
 		return;
 	} else if (return_count == 1) {
 		Entity *e = tuple->variables[0];

+ 160 - 837
src/llvm_backend_type.cpp

@@ -62,6 +62,7 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
 	case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
 	case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
 	case Type_SoaPointer:      kind = Typeid_SoaPointer;       break;
+	case Type_BitField:        kind = Typeid_Bit_Field;        break;
 	}
 
 	return kind;
@@ -110,16 +111,19 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
 	return res;
 }
 
-gb_internal lbValue lb_type_info(lbModule *m, Type *type) {
+gb_internal lbValue lb_type_info(lbProcedure *p, Type *type) {
 	GB_ASSERT(!build_context.no_rtti);
 
 	type = default_type(type);
+	lbModule *m = p->module;
 
 	isize index = lb_type_info_index(m->info, type);
 	GB_ASSERT(index >= 0);
 
-	lbValue data = lb_global_type_info_data_ptr(m);
-	return lb_emit_array_epi(m, data, index);
+	lbValue global = lb_global_type_info_data_ptr(m);
+
+	lbValue ptr = lb_emit_array_epi(p, global, index);
+	return lb_emit_load(p, ptr);
 }
 
 gb_internal LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
@@ -180,16 +184,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbModule *m, isize count, i6
 	return offset;
 }
 
-// enum {LB_USE_GIANT_PACKED_STRUCT = LB_USE_NEW_PASS_SYSTEM};
-enum {LB_USE_GIANT_PACKED_STRUCT = 0};
-
-gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize max_type_info_count) {
-	if (!LB_USE_GIANT_PACKED_STRUCT) {
-		Type *t = alloc_type_array(t_type_info, max_type_info_count);
-		return lb_type(m, t);
-	}
-	CheckerInfo *info = m->gen->info;
-
+gb_internal LLVMTypeRef *lb_setup_modified_types_for_type_info(lbModule *m, isize max_type_info_count) {
 	LLVMTypeRef *element_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, max_type_info_count);
 	defer (gb_free(heap_allocator(), element_types));
 
@@ -219,8 +214,8 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
 		variant_index = 4;
 	}
 
-	LLVMTypeRef modified_types[32] = {};
-	GB_ASSERT(gb_count_of(modified_types) >= ut->Union.variants.count);
+	LLVMTypeRef *modified_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, Typeid__COUNT);
+	GB_ASSERT(Typeid__COUNT == ut->Union.variants.count);
 	modified_types[0] = element_types[0];
 
 	i64 tag_offset = ut->Union.variant_block_size;
@@ -242,40 +237,24 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
 		modified_types[i] = modified_type;
 	}
 
-	for_array(type_info_type_index, info->type_info_types) {
-		Type *t = info->type_info_types[type_info_type_index];
-		if (t == nullptr || t == t_invalid) {
-			continue;
-		}
-		isize entry_index = lb_type_info_index(info, t, false);
-		if (entry_index <= 0) {
-			continue;
-		}
-
-		if (entries_handled[entry_index]) {
-			continue;
-		}
-		entries_handled[entry_index] = true;
-
-
-		if (t->kind == Type_Named) {
-			element_types[entry_index] = modified_types[0];
-		} else {
-			i64 variant_index = lb_typeid_kind(m, t);
-			element_types[entry_index] = modified_types[variant_index];
-		}
-
-		GB_ASSERT(element_types[entry_index] != nullptr);
-	}
-
-	for_array(i, entries_handled) {
-		GB_ASSERT(entries_handled[i]);
+	for (isize i = 0; i < Typeid__COUNT; i++) {
+		GB_ASSERT_MSG(modified_types[i] != nullptr, "%td", ut->Union.variants.count);
 	}
 
-	return LLVMStructType(element_types, cast(unsigned)max_type_info_count, true);
+	return modified_types;
 }
 
-gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 global_type_info_data_entity_count, lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_type_info_data_entity_count) { // NOTE(bill): Setup type_info data
+	auto const &ADD_GLOBAL_TYPE_INFO_ENTRY = [](lbModule *m, LLVMTypeRef type, isize index) -> LLVMValueRef {
+		char name[64] = {};
+		gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
+		LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
+		LLVMSetLinkage(g, LLVMInternalLinkage);
+		LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
+		LLVMSetGlobalConstant(g, true);
+		return g;
+	};
+
 	CheckerInfo *info = m->info;
 
 	// Useful types
@@ -292,19 +271,49 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 	defer (gb_free(heap_allocator(), entries_handled.data));
 	entries_handled[0] = true;
 
-	LLVMValueRef giant_struct = lb_global_type_info_data_ptr(m).value;
-	LLVMTypeRef giant_struct_type = LLVMGlobalGetValueType(giant_struct);
-	GB_ASSERT(LLVMGetTypeKind(giant_struct_type) == LLVMStructTypeKind);
-
 	LLVMValueRef *giant_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, global_type_info_data_entity_count);
 	defer (gb_free(heap_allocator(), giant_const_values));
 
-	giant_const_values[0] = LLVMConstNull(LLVMStructGetTypeAtIndex(giant_struct_type, 0));
+	// zero value is just zero data
+	giant_const_values[0] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, lb_type(m, t_type_info), 0);
+	LLVMSetInitializer(giant_const_values[0], LLVMConstNull(lb_type(m, t_type_info)));
+
+
+	LLVMTypeRef *modified_types = lb_setup_modified_types_for_type_info(m, global_type_info_data_entity_count);
+	defer (gb_free(heap_allocator(), modified_types));
+	for_array(type_info_type_index, info->type_info_types) {
+		Type *t = info->type_info_types[type_info_type_index];
+		if (t == nullptr || t == t_invalid) {
+			continue;
+		}
+
+		isize entry_index = lb_type_info_index(info, t, false);
+		if (entry_index <= 0) {
+			continue;
+		}
+
+		if (entries_handled[entry_index]) {
+			continue;
+		}
+		entries_handled[entry_index] = true;
+
+
+		LLVMTypeRef stype = nullptr;
+		if (t->kind == Type_Named) {
+			stype = modified_types[0];
+		} else {
+			stype = modified_types[lb_typeid_kind(m, t)];
+		}
+		giant_const_values[entry_index] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, stype, entry_index);
+	}
+	for (isize i = 1; i < global_type_info_data_entity_count; i++) {
+		entries_handled[i] = false;
+	}
+
 
 	LLVMValueRef *small_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, 6);
 	defer (gb_free(heap_allocator(), small_const_values));
 
-
 	#define type_info_allocate_values(name) \
 		LLVMValueRef *name##_values = gb_alloc_array(heap_allocator(), LLVMValueRef, type_deref(name.addr.type)->Array.count); \
 		defer (gb_free(heap_allocator(), name##_values));                                                                      \
@@ -316,7 +325,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 					(name##_values)[i] = LLVMConstNull(elem);                                                      \
 				}                                                                                                      \
 			}                                                                                                              \
-			LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count));                    \
+			LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count));                   \
 		})
 
 	type_info_allocate_values(lb_global_type_info_member_types);
@@ -326,27 +335,13 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 	type_info_allocate_values(lb_global_type_info_member_tags);
 
 
-	i64 const type_info_struct_size = type_size_of(t_type_info);
-	LLVMTypeRef llvm_u8 = lb_type(m, t_u8);
-	LLVMTypeRef llvm_int = lb_type(m, t_int);
-	// LLVMTypeRef llvm_type_info_ptr = lb_type(m, t_type_info_ptr);
-
 	auto const get_type_info_ptr = [&](lbModule *m, Type *type) -> LLVMValueRef {
 		type = default_type(type);
 
 		isize index = lb_type_info_index(m->info, type);
 		GB_ASSERT(index >= 0);
 
-		u64 offset = cast(u64)(index * type_info_struct_size);
-
-		LLVMValueRef indices[1] = {
-			LLVMConstInt(llvm_int, offset, false)
-		};
-
-		// LLVMValueRef ptr = LLVMConstInBoundsGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices));
-		LLVMValueRef ptr = LLVMConstGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices));
-		return ptr;
-		// return LLVMConstPointerCast(ptr, llvm_type_info_ptr);
+		return giant_const_values[index];
 	};
 
 	for_array(type_info_type_index, info->type_info_types) {
@@ -366,7 +361,12 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 		entries_handled[entry_index] = true;
 
 
-		LLVMTypeRef stype = LLVMStructGetTypeAtIndex(giant_struct_type, cast(unsigned)entry_index);
+		LLVMTypeRef stype = nullptr;
+		if (t->kind == Type_Named) {
+			stype = modified_types[0];
+		} else {
+			stype = modified_types[lb_typeid_kind(m, t)];
+		}
 
 		i64 size = type_size_of(t);
 		i64 align = type_align_of(t);
@@ -376,6 +376,10 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 
 		lbValue type_info_flags = lb_const_int(m, t_type_info_flags, flags);
 
+		for (isize i = 0; i < 6; i++) {
+			small_const_values[i] = nullptr;
+		}
+
 		small_const_values[0] = LLVMConstInt(lb_type(m, t_int), size, true);
 		small_const_values[1] = LLVMConstInt(lb_type(m, t_int), align, true);
 		small_const_values[2] = type_info_flags.value;
@@ -986,6 +990,69 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 				variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
 			}
 			break;
+
+		case Type_BitField:
+			{
+				tag_type = t_type_info_bit_field;
+
+				LLVMValueRef vals[6] = {};
+				vals[0] = get_type_info_ptr(m, t->BitField.backing_type);
+				isize count = t->BitField.fields.count;
+				if (count > 0) {
+					i64 names_offset       = 0;
+					i64 types_offset       = 0;
+					i64 bit_sizes_offset   = 0;
+					i64 bit_offsets_offset = 0;
+					i64 tags_offset        = 0;
+					lbValue memory_names       = lb_type_info_member_names_offset  (m, count, &names_offset);
+					lbValue memory_types       = lb_type_info_member_types_offset  (m, count, &types_offset);
+					lbValue memory_bit_sizes   = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset);
+					lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset);
+					lbValue memory_tags        = lb_type_info_member_tags_offset   (m, count, &tags_offset);
+
+					u64 bit_offset = 0;
+					for (isize source_index = 0; source_index < count; source_index++) {
+						Entity *f = t->BitField.fields[source_index];
+						u64 bit_size = cast(u64)t->BitField.bit_sizes[source_index];
+
+						lbValue index = lb_const_int(m, t_int, source_index);
+						if (f->token.string.len > 0) {
+							lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value;
+						}
+
+						lb_global_type_info_member_types_values[types_offset+source_index] = get_type_info_ptr(m, f->type);
+
+						lb_global_type_info_member_offsets_values[bit_sizes_offset+source_index] = lb_const_int(m, t_uintptr, bit_size).value;
+						lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value;
+
+						if (t->BitField.tags) {
+							String tag = t->BitField.tags[source_index];
+							if (tag.len > 0) {
+								lb_global_type_info_member_tags_values[tags_offset+source_index] = lb_const_string(m, tag).value;
+							}
+						}
+
+						bit_offset += bit_size;
+					}
+
+					lbValue cv = lb_const_int(m, t_int, count);
+					vals[1] = llvm_const_slice(m, memory_names,       cv);
+					vals[2] = llvm_const_slice(m, memory_types,       cv);
+					vals[3] = llvm_const_slice(m, memory_bit_sizes,   cv);
+					vals[4] = llvm_const_slice(m, memory_bit_offsets, cv);
+					vals[5] = llvm_const_slice(m, memory_tags,        cv);
+				}
+
+
+				for (isize i = 0; i < gb_count_of(vals); i++) {
+					if (vals[i] == nullptr) {
+						vals[i]  = LLVMConstNull(lb_type(m, get_struct_field_type(tag_type, i)));
+					}
+				}
+
+				variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
+				break;
+			}
 		}
 
 
@@ -994,6 +1061,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 		if (tag_type != nullptr) {
 			tag_index = union_variant_index(ut, tag_type);
 		}
+		GB_ASSERT(tag_index <= Typeid__COUNT);
 
 		LLVMValueRef full_variant_values[3] = {};
 
@@ -1024,788 +1092,43 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
 
 		small_const_values[variant_index] = full_variant_value;
 
-		giant_const_values[entry_index] = LLVMConstNamedStruct(stype, small_const_values, variant_index+1);
+		LLVMSetInitializer(giant_const_values[entry_index], LLVMConstNamedStruct(stype, small_const_values, variant_index+1));
 	}
+	for (isize i = 0; i < global_type_info_data_entity_count; i++) {
+		giant_const_values[i] = LLVMConstPointerCast(giant_const_values[i], lb_type(m, t_type_info_ptr));
+	}
+
 
-	LLVMValueRef giant_const = LLVMConstNamedStruct(giant_struct_type, giant_const_values, cast(unsigned)global_type_info_data_entity_count);
-	LLVMSetInitializer(giant_struct, giant_const);
+	LLVMValueRef giant_const = LLVMConstArray(lb_type(m, t_type_info_ptr), giant_const_values, cast(unsigned)global_type_info_data_entity_count);
+	LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
+	LLVMSetInitializer(giant_array, giant_const);
 }
 
 
-gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data(lbModule *m) { // NOTE(bill): Setup type_info data
 	if (build_context.no_rtti) {
 		return;
 	}
 
-	lbModule *m = p->module;
-	CheckerInfo *info = m->info;
 
 	i64 global_type_info_data_entity_count = 0;
-	{
-		// NOTE(bill): Set the type_table slice with the global backing array
-		lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
-		Type *type = base_type(lb_global_type_info_data_entity->type);
-		GB_ASSERT(type->kind == Type_Array);
-		global_type_info_data_entity_count = type->Array.count;
-
-		LLVMValueRef data = lb_global_type_info_data_ptr(m).value;
-		data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem)));
-		LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true);
-		Type *t = type_deref(global_type_table.type);
-		GB_ASSERT(is_type_slice(t));
-		LLVMValueRef slice = llvm_const_slice_internal(m, data, len);
-
-		LLVMSetInitializer(global_type_table.value, slice);
-	}
-
-	if (LB_USE_GIANT_PACKED_STRUCT) {
-		lb_setup_type_info_data_giant_packed_struct(m, global_type_info_data_entity_count, p);
-		return;
-	}
-
-	// Useful types
-	Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
-	Type *t_type_info_flags = type_info_flags_entity->type;
-
-	
-	auto entries_handled = slice_make<bool>(heap_allocator(), cast(isize)global_type_info_data_entity_count);
-	defer (gb_free(heap_allocator(), entries_handled.data));
-	entries_handled[0] = true;
-	
-	for_array(type_info_type_index, info->type_info_types) {
-		Type *t = info->type_info_types[type_info_type_index];
-		if (t == nullptr || t == t_invalid) {
-			continue;
-		}
-
-		isize entry_index = lb_type_info_index(info, t, false);
-		if (entry_index <= 0) {
-			continue;
-		}
-
-		if (entries_handled[entry_index]) {
-			continue;
-		}
-		entries_handled[entry_index] = true;
-
-		lbValue global_data_ptr = lb_global_type_info_data_ptr(m);
-		lbValue tag = {};
-		lbValue ti_ptr = lb_emit_array_epi(p, global_data_ptr, cast(i32)entry_index);
-		
-		i64 size = type_size_of(t);
-		i64 align = type_align_of(t);
-		u32 flags = type_info_flags_of_type(t);
-		lbValue id = lb_typeid(m, t);
-		GB_ASSERT_MSG(align != 0, "%lld %s", align, type_to_string(t));
-		
-		lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, flags);
-		
-		lbValue size_ptr  = lb_emit_struct_ep(p, ti_ptr, 0);
-		lbValue align_ptr = lb_emit_struct_ep(p, ti_ptr, 1);
-		lbValue flags_ptr = lb_emit_struct_ep(p, ti_ptr, 2);
-		lbValue id_ptr    = lb_emit_struct_ep(p, ti_ptr, 3);
-				
-		lb_emit_store(p, size_ptr,  lb_const_int(m, t_int, size));
-		lb_emit_store(p, align_ptr, lb_const_int(m, t_int, align));
-		lb_emit_store(p, flags_ptr, type_info_flags);
-		lb_emit_store(p, id_ptr,    id);
-		
-		lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4);
-
-		switch (t->kind) {
-		case Type_Named: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr);
-
-			LLVMValueRef pkg_name = nullptr;
-			if (t->Named.type_name->pkg) {
-				pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value;
-			} else {
-				pkg_name = LLVMConstNull(lb_type(m, t_string));
-			}
-
-			String proc_name = {};
-			if (t->Named.type_name->parent_proc_decl) {
-				DeclInfo *decl = t->Named.type_name->parent_proc_decl;
-				if (decl->entity && decl->entity->kind == Entity_Procedure) {
-					proc_name = decl->entity->token.string;
-				}
-			}
-			TokenPos pos = t->Named.type_name->token.pos;
-
-			lbValue loc = lb_emit_source_code_location_const(p, proc_name, pos);
-
-			LLVMValueRef vals[4] = {
-				lb_const_string(p->module, t->Named.type_name->token.string).value,
-				lb_type_info(m, t->Named.base).value,
-				pkg_name,
-				loc.value
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-
-		case Type_Basic:
-			switch (t->Basic.kind) {
-			case Basic_bool:
-			case Basic_b8:
-			case Basic_b16:
-			case Basic_b32:
-			case Basic_b64:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_boolean_ptr);
-				break;
-
-			case Basic_i8:
-			case Basic_u8:
-			case Basic_i16:
-			case Basic_u16:
-			case Basic_i32:
-			case Basic_u32:
-			case Basic_i64:
-			case Basic_u64:
-			case Basic_i128:
-			case Basic_u128:
-
-			case Basic_i16le:
-			case Basic_u16le:
-			case Basic_i32le:
-			case Basic_u32le:
-			case Basic_i64le:
-			case Basic_u64le:
-			case Basic_i128le:
-			case Basic_u128le:
-			case Basic_i16be:
-			case Basic_u16be:
-			case Basic_i32be:
-			case Basic_u32be:
-			case Basic_i64be:
-			case Basic_u64be:
-			case Basic_i128be:
-			case Basic_u128be:
-
-			case Basic_int:
-			case Basic_uint:
-			case Basic_uintptr: {
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_integer_ptr);
 
-				lbValue is_signed = lb_const_bool(m, t_bool, (t->Basic.flags & BasicFlag_Unsigned) == 0);
-				// NOTE(bill): This is matches the runtime layout
-				u8 endianness_value = 0;
-				if (t->Basic.flags & BasicFlag_EndianLittle) {
-					endianness_value = 1;
-				} else if (t->Basic.flags & BasicFlag_EndianBig) {
-					endianness_value = 2;
-				}
-				lbValue endianness = lb_const_int(m, t_u8, endianness_value);
-
-				LLVMValueRef vals[2] = {
-					is_signed.value,
-					endianness.value,
-				};
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-				break;
-			}
-
-			case Basic_rune:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr);
-				break;
-
-			case Basic_f16:
-			case Basic_f32:
-			case Basic_f64:
-			case Basic_f16le:
-			case Basic_f32le:
-			case Basic_f64le:
-			case Basic_f16be:
-			case Basic_f32be:
-			case Basic_f64be:
-				{
-					tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
-
-					// NOTE(bill): This is matches the runtime layout
-					u8 endianness_value = 0;
-					if (t->Basic.flags & BasicFlag_EndianLittle) {
-						endianness_value = 1;
-					} else if (t->Basic.flags & BasicFlag_EndianBig) {
-						endianness_value = 2;
-					}
-					lbValue endianness = lb_const_int(m, t_u8, endianness_value);
-
-					LLVMValueRef vals[1] = {
-						endianness.value,
-					};
-
-					lbValue res = {};
-					res.type = type_deref(tag.type);
-					res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-					lb_emit_store(p, tag, res);
-				}
-				break;
-
-			case Basic_complex32:
-			case Basic_complex64:
-			case Basic_complex128:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr);
-				break;
-
-			case Basic_quaternion64:
-			case Basic_quaternion128:
-			case Basic_quaternion256:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr);
-				break;
+	// NOTE(bill): Set the type_table slice with the global backing array
+	lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
+	Type *type = base_type(lb_global_type_info_data_entity->type);
+	GB_ASSERT(type->kind == Type_Array);
+	global_type_info_data_entity_count = type->Array.count;
 
-			case Basic_rawptr:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
-				break;
+	if (true) {
+		lb_setup_type_info_data_giant_array(m, global_type_info_data_entity_count);
+	}
 
-			case Basic_string:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
-				break;
+	LLVMValueRef data = lb_global_type_info_data_ptr(m).value;
+	data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem)));
+	LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true);
+	Type *t = type_deref(global_type_table.type);
+	GB_ASSERT(is_type_slice(t));
+	LLVMValueRef slice = llvm_const_slice_internal(m, data, len);
 
-			case Basic_cstring:
-				{
-					tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
-					LLVMValueRef vals[1] = {
-						lb_const_bool(m, t_bool, true).value,
-					};
-
-					lbValue res = {};
-					res.type = type_deref(tag.type);
-					res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-					lb_emit_store(p, tag, res);
-				}
-				break;
-
-			case Basic_any:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_any_ptr);
-				break;
-
-			case Basic_typeid:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_typeid_ptr);
-				break;
-			}
-			break;
-
-		case Type_Pointer: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
-			lbValue gep = lb_type_info(m, t->Pointer.elem);
-
-			LLVMValueRef vals[1] = {
-				gep.value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_MultiPointer: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_multi_pointer_ptr);
-			lbValue gep = lb_type_info(m, t->MultiPointer.elem);
-
-			LLVMValueRef vals[1] = {
-				gep.value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_SoaPointer: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr);
-			lbValue gep = lb_type_info(m, t->SoaPointer.elem);
-
-			LLVMValueRef vals[1] = {
-				gep.value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_Array: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr);
-			i64 ez = type_size_of(t->Array.elem);
-
-			LLVMValueRef vals[3] = {
-				lb_type_info(m, t->Array.elem).value,
-				lb_const_int(m, t_int, ez).value,
-				lb_const_int(m, t_int, t->Array.count).value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_EnumeratedArray: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
-
-			LLVMValueRef vals[7] = {
-				lb_type_info(m, t->EnumeratedArray.elem).value,
-				lb_type_info(m, t->EnumeratedArray.index).value,
-				lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
-				lb_const_int(m, t_int, t->EnumeratedArray.count).value,
-
-				// Unions
-				LLVMConstNull(lb_type(m, t_type_info_enum_value)),
-				LLVMConstNull(lb_type(m, t_type_info_enum_value)),
-
-				lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-
-			// NOTE(bill): Union assignment
-			lbValue min_value = lb_emit_struct_ep(p, tag, 4);
-			lbValue max_value = lb_emit_struct_ep(p, tag, 5);
-
-			lbValue min_v = lb_const_value(m, t_i64, *t->EnumeratedArray.min_value);
-			lbValue max_v = lb_const_value(m, t_i64, *t->EnumeratedArray.max_value);
-
-			lb_emit_store(p, min_value, min_v);
-			lb_emit_store(p, max_value, max_v);
-			break;
-		}
-		case Type_DynamicArray: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
-
-			LLVMValueRef vals[2] = {
-				lb_type_info(m, t->DynamicArray.elem).value,
-				lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_Slice: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
-
-			LLVMValueRef vals[2] = {
-				lb_type_info(m, t->Slice.elem).value,
-				lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_Proc: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_procedure_ptr);
-
-			LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
-			LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
-			if (t->Proc.params != nullptr) {
-				params = lb_type_info(m, t->Proc.params).value;
-			}
-			if (t->Proc.results != nullptr) {
-				results = lb_type_info(m, t->Proc.results).value;
-			}
-
-			LLVMValueRef vals[4] = {
-				params,
-				results,
-				lb_const_bool(m, t_bool, t->Proc.variadic).value,
-				lb_const_int(m, t_u8, t->Proc.calling_convention).value,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-		case Type_Tuple: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_parameters_ptr);
-
-			lbValue memory_types = lb_type_info_member_types_offset(m, t->Tuple.variables.count);
-			lbValue memory_names = lb_type_info_member_names_offset(m, t->Tuple.variables.count);
-
-
-			for_array(i, t->Tuple.variables) {
-				// NOTE(bill): offset is not used for tuples
-				Entity *f = t->Tuple.variables[i];
-
-				lbValue index     = lb_const_int(m, t_int, i);
-				lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
-
-				// TODO(bill): Make this constant if possible, 'lb_const_store' does not work
-				lb_emit_store(p, type_info, lb_type_info(m, f->type));
-				if (f->token.string.len > 0) {
-					lbValue name = lb_emit_ptr_offset(p, memory_names, index);
-					lb_emit_store(p, name, lb_const_string(m, f->token.string));
-				}
-			}
-
-			lbValue count = lb_const_int(m, t_int, t->Tuple.variables.count);
-
-			LLVMValueRef types_slice = llvm_const_slice(m, memory_types, count);
-			LLVMValueRef names_slice = llvm_const_slice(m, memory_names, count);
-
-			LLVMValueRef vals[2] = {
-				types_slice,
-				names_slice,
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-
-			break;
-		}
-
-		case Type_Enum:
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enum_ptr);
-
-			{
-				GB_ASSERT(t->Enum.base_type != nullptr);
-				// GB_ASSERT_MSG(type_size_of(t_type_info_enum_value) == 16, "%lld == 16", cast(long long)type_size_of(t_type_info_enum_value));
-
-
-				LLVMValueRef vals[3] = {};
-				vals[0] = lb_type_info(m, t->Enum.base_type).value;
-				if (t->Enum.fields.count > 0) {
-					auto fields = t->Enum.fields;
-					lbValue name_array  = lb_generate_global_array(m, t_string, fields.count,
-					                                        str_lit("$enum_names"), cast(i64)entry_index);
-					lbValue value_array = lb_generate_global_array(m, t_type_info_enum_value, fields.count,
-					                                        str_lit("$enum_values"), cast(i64)entry_index);
-
-
-					LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
-					LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
-
-					GB_ASSERT(is_type_integer(t->Enum.base_type));
-
-					for_array(i, fields) {
-						name_values[i] = lb_const_string(m, fields[i]->token.string).value;
-						value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value;
-					}
-
-					LLVMValueRef name_init  = llvm_const_array(lb_type(m, t_string),               name_values,  cast(unsigned)fields.count);
-					LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
-					LLVMSetInitializer(name_array.value,  name_init);
-					LLVMSetInitializer(value_array.value, value_init);
-					LLVMSetGlobalConstant(name_array.value, true);
-					LLVMSetGlobalConstant(value_array.value, true);
-
-					lbValue v_count = lb_const_int(m, t_int, fields.count);
-
-					vals[1] = llvm_const_slice(m, lb_array_elem(p, name_array), v_count);
-					vals[2] = llvm_const_slice(m, lb_array_elem(p, value_array), v_count);
-				} else {
-					vals[1] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[1]->type));
-					vals[2] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[2]->type));
-				}
-
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-			break;
-
-		case Type_Union: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
-
-			{
-				LLVMValueRef vals[7] = {};
-
-				isize variant_count = gb_max(0, t->Union.variants.count);
-				lbValue memory_types = lb_type_info_member_types_offset(m, variant_count);
-
-				// NOTE(bill): Zeroth is nil so ignore it
-				for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
-					Type *vt = t->Union.variants[variant_index];
-					lbValue tip = lb_type_info(m, vt);
-
-					lbValue index     = lb_const_int(m, t_int, variant_index);
-					lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
-					lb_emit_store(p, type_info, lb_type_info(m, vt));
-				}
-
-				lbValue count = lb_const_int(m, t_int, variant_count);
-				vals[0] = llvm_const_slice(m, memory_types, count);
-
-				i64 tag_size = union_tag_size(t);
-				if (tag_size > 0) {
-					i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
-					vals[1] = lb_const_int(m, t_uintptr, tag_offset).value;
-					vals[2] = lb_type_info(m, union_tag_type(t)).value;
-				} else {
-					vals[1] = lb_const_int(m, t_uintptr, 0).value;
-					vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr));
-				}
-
-				if (is_type_comparable(t) && !is_type_simple_compare(t)) {
-					vals[3] = lb_equal_proc_for_type(m, t).value;
-				}
-
-				vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
-				vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
-				vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
-
-				for (isize i = 0; i < gb_count_of(vals); i++) {
-					if (vals[i] == nullptr) {
-						vals[i]  = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
-					}
-				}
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-
-			break;
-		}
-
-		case Type_Struct: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr);
-
-			LLVMValueRef vals[13] = {};
-
-
-			{
-				lbValue is_packed       = lb_const_bool(m, t_bool, t->Struct.is_packed);
-				lbValue is_raw_union    = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
-				lbValue is_no_copy      = lb_const_bool(m, t_bool, t->Struct.is_no_copy);
-				lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
-				vals[5] = is_packed.value;
-				vals[6] = is_raw_union.value;
-				vals[7] = is_no_copy.value;
-				vals[8] = is_custom_align.value;
-				if (is_type_comparable(t) && !is_type_simple_compare(t)) {
-					vals[9] = lb_equal_proc_for_type(m, t).value;
-				}
-
-
-				if (t->Struct.soa_kind != StructSoa_None) {
-					lbValue kind = lb_emit_struct_ep(p, tag, 10);
-					Type *kind_type = type_deref(kind.type);
-
-					lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
-					lbValue soa_type = lb_type_info(m, t->Struct.soa_elem);
-					lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
-
-					vals[10] = soa_kind.value;
-					vals[11] = soa_type.value;
-					vals[12] = soa_len.value;
-				}
-			}
-			
-			isize count = t->Struct.fields.count;
-			if (count > 0) {
-				lbValue memory_types   = lb_type_info_member_types_offset  (m, count);
-				lbValue memory_names   = lb_type_info_member_names_offset  (m, count);
-				lbValue memory_offsets = lb_type_info_member_offsets_offset(m, count);
-				lbValue memory_usings  = lb_type_info_member_usings_offset (m, count);
-				lbValue memory_tags    = lb_type_info_member_tags_offset   (m, count);
-
-				type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
-				for (isize source_index = 0; source_index < count; source_index++) {
-					Entity *f = t->Struct.fields[source_index];
-					lbValue tip = lb_type_info(m, f->type);
-					i64 foffset = 0;
-					if (!t->Struct.is_raw_union) {
-						GB_ASSERT(t->Struct.offsets != nullptr);
-						GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count);
-						foffset = t->Struct.offsets[source_index];
-					}
-					GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
-
-					lbValue index     = lb_const_int(m, t_int, source_index);
-					lbValue type_info = lb_emit_ptr_offset(p, memory_types,   index);
-					lbValue offset    = lb_emit_ptr_offset(p, memory_offsets, index);
-					lbValue is_using  = lb_emit_ptr_offset(p, memory_usings,  index);
-
-					lb_emit_store(p, type_info, lb_type_info(m, f->type));
-					if (f->token.string.len > 0) {
-						lbValue name = lb_emit_ptr_offset(p, memory_names,   index);
-						lb_emit_store(p, name, lb_const_string(m, f->token.string));
-					}
-					lb_emit_store(p, offset, lb_const_int(m, t_uintptr, foffset));
-					lb_emit_store(p, is_using, lb_const_bool(m, t_bool, (f->flags&EntityFlag_Using) != 0));
-
-					if (t->Struct.tags != nullptr) {
-						String tag_string = t->Struct.tags[source_index];
-						if (tag_string.len > 0) {
-							lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index);
-							lb_emit_store(p, tag_ptr, lb_const_string(m, tag_string));
-						}
-					}
-
-				}
-
-				lbValue cv = lb_const_int(m, t_int, count);
-				vals[0] = llvm_const_slice(m, memory_types,   cv);
-				vals[1] = llvm_const_slice(m, memory_names,   cv);
-				vals[2] = llvm_const_slice(m, memory_offsets, cv);
-				vals[3] = llvm_const_slice(m, memory_usings,  cv);
-				vals[4] = llvm_const_slice(m, memory_tags,    cv);
-			}
-			for (isize i = 0; i < gb_count_of(vals); i++) {
-				if (vals[i] == nullptr) {
-					vals[i]  = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
-				}
-			}
-			
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-
-			break;
-		}
-
-		case Type_Map: {
-			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
-			init_map_internal_types(t);
-
-			LLVMValueRef vals[3] = {
-				lb_type_info(m, t->Map.key).value,
-				lb_type_info(m, t->Map.value).value,
-				lb_gen_map_info_ptr(p->module, t).value
-			};
-
-			lbValue res = {};
-			res.type = type_deref(tag.type);
-			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-			lb_emit_store(p, tag, res);
-			break;
-		}
-
-		case Type_BitSet:
-			{
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_set_ptr);
-
-				GB_ASSERT(is_type_typed(t->BitSet.elem));
-
-
-				LLVMValueRef vals[4] = {
-					lb_type_info(m, t->BitSet.elem).value,
-					LLVMConstNull(lb_type(m, t_type_info_ptr)),
-					lb_const_int(m, t_i64, t->BitSet.lower).value,
-					lb_const_int(m, t_i64, t->BitSet.upper).value,
-				};
-				if (t->BitSet.underlying != nullptr) {
-					vals[1] =lb_type_info(m, t->BitSet.underlying).value;
-				}
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-			break;
-
-		case Type_SimdVector:
-			{
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
-
-				LLVMValueRef vals[3] = {};
-
-				vals[0] = lb_type_info(m, t->SimdVector.elem).value;
-				vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
-				vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-			break;
-
-		case Type_RelativePointer:
-			{
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_pointer_ptr);
-				LLVMValueRef vals[2] = {
-					lb_type_info(m, t->RelativePointer.pointer_type).value,
-					lb_type_info(m, t->RelativePointer.base_integer).value,
-				};
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-			break;
-
-		case Type_RelativeMultiPointer:
-			{
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_multi_pointer_ptr);
-				LLVMValueRef vals[2] = {
-					lb_type_info(m, t->RelativeMultiPointer.pointer_type).value,
-					lb_type_info(m, t->RelativeMultiPointer.base_integer).value,
-				};
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-			break;
-
-		case Type_Matrix: 
-			{
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);
-				i64 ez = type_size_of(t->Matrix.elem);
-
-				LLVMValueRef vals[5] = {
-					lb_type_info(m, t->Matrix.elem).value,
-					lb_const_int(m, t_int, ez).value,
-					lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
-					lb_const_int(m, t_int, t->Matrix.row_count).value,
-					lb_const_int(m, t_int, t->Matrix.column_count).value,
-				};
-
-				lbValue res = {};
-				res.type = type_deref(tag.type);
-				res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
-				lb_emit_store(p, tag, res);
-			}
-			break;
-		}
-
-
-		if (tag.value != nullptr) {
-			Type *tag_type = type_deref(tag.type);
-			GB_ASSERT(is_type_named(tag_type));
-			// lb_emit_store_union_variant(p, variant_ptr, lb_emit_load(p, tag), tag_type);
-			lb_emit_store_union_variant_tag(p, variant_ptr, tag_type);
-		} else {
-			if (t != t_llvm_bool) {
-				GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t));
-			}
-		}
-	}
-	
-	for_array(i, entries_handled) {
-		if (!entries_handled[i]) {
-			GB_PANIC("UNHANDLED ENTRY %td (%td)", i, entries_handled.count);
-		}
-	}
+	LLVMSetInitializer(global_type_table.value, slice);
 }

+ 1 - 1
src/llvm_backend_utility.cpp

@@ -1332,7 +1332,7 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
 				if (index == 0) {
 					type = t_rawptr;
 				} else if (index == 1) {
-					type = t_type_info_ptr;
+					type = t_typeid;
 				}
 				e = lb_emit_struct_ep(p, e, index);
 				break;

+ 97 - 0
src/parser.cpp

@@ -350,6 +350,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
 		n->Field.names = clone_ast_array(n->Field.names, f);
 		n->Field.type  = clone_ast(n->Field.type, f);
 		break;
+	case Ast_BitFieldField:
+		n->BitFieldField.name     = clone_ast(n->BitFieldField.name, f);
+		n->BitFieldField.type     = clone_ast(n->BitFieldField.type, f);
+		n->BitFieldField.bit_size = clone_ast(n->BitFieldField.bit_size, f);
+		break;
 	case Ast_FieldList:
 		n->FieldList.list = clone_ast_array(n->FieldList.list, f);
 		break;
@@ -406,6 +411,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
 		n->BitSetType.elem       = clone_ast(n->BitSetType.elem, f);
 		n->BitSetType.underlying = clone_ast(n->BitSetType.underlying, f);
 		break;
+	case Ast_BitFieldType:
+		n->BitFieldType.backing_type = clone_ast(n->BitFieldType.backing_type, f);
+		n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields, f);
+		break;
 	case Ast_MapType:
 		n->MapType.count = clone_ast(n->MapType.count, f);
 		n->MapType.key   = clone_ast(n->MapType.key, f);
@@ -1045,6 +1054,18 @@ gb_internal Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast
 	return result;
 }
 
+gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, Token tag,
+                                     CommentGroup *docs, CommentGroup *comment) {
+	Ast *result = alloc_ast_node(f, Ast_BitFieldField);
+	result->BitFieldField.name     = name;
+	result->BitFieldField.type     = type;
+	result->BitFieldField.bit_size = bit_size;
+	result->BitFieldField.tag      = tag;
+	result->BitFieldField.docs     = docs;
+	result->BitFieldField.comment  = comment;
+	return result;
+}
+
 gb_internal Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> const &list) {
 	Ast *result = alloc_ast_node(f, Ast_FieldList);
 	result->FieldList.token = token;
@@ -1178,6 +1199,17 @@ gb_internal Ast *ast_bit_set_type(AstFile *f, Token token, Ast *elem, Ast *under
 	return result;
 }
 
+gb_internal Ast *ast_bit_field_type(AstFile *f, Token token, Ast *backing_type, Token open, Array<Ast *> const &fields, Token close) {
+	Ast *result = alloc_ast_node(f, Ast_BitFieldType);
+	result->BitFieldType.token        = token;
+	result->BitFieldType.backing_type = backing_type;
+	result->BitFieldType.open         = open;
+	result->BitFieldType.fields       = slice_from_array(fields);
+	result->BitFieldType.close        = close;
+	return result;
+}
+
+
 gb_internal Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) {
 	Ast *result = alloc_ast_node(f, Ast_MapType);
 	result->MapType.token = token;
@@ -2549,6 +2581,66 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
 		return ast_matrix_type(f, token, row_count, column_count, type);
 	} break;
 
+	case Token_bit_field: {
+		Token token = expect_token(f, Token_bit_field);
+		isize prev_level;
+
+		prev_level = f->expr_level;
+		f->expr_level = -1;
+
+		Ast *backing_type = parse_type_or_ident(f);
+		if (backing_type == nullptr) {
+			Token token = advance_token(f);
+			syntax_error(token, "Expected a backing type for a 'bit_field'");
+			backing_type = ast_bad_expr(f, token, f->curr_token);
+		}
+
+		skip_possible_newline_for_literal(f);
+		Token open = expect_token_after(f, Token_OpenBrace, "bit_field");
+
+
+		auto fields = array_make<Ast *>(ast_allocator(f), 0, 0);
+
+		while (f->curr_token.kind != Token_CloseBrace &&
+		       f->curr_token.kind != Token_EOF) {
+			CommentGroup *docs = nullptr;
+			CommentGroup *comment = nullptr;
+
+			Ast *name = parse_ident(f);
+			bool err_once = false;
+			while (allow_token(f, Token_Comma)) {
+				Ast *dummy_name = parse_ident(f);
+				if (!err_once) {
+					error(dummy_name, "'bit_field' fields do not support multiple names per field");
+					err_once = true;
+				}
+			}
+			expect_token(f, Token_Colon);
+			Ast *type = parse_type(f);
+			expect_token(f, Token_Or);
+			Ast *bit_size = parse_expr(f, true);
+
+			Token tag = {};
+			if (f->curr_token.kind == Token_String) {
+				tag = expect_token(f, Token_String);
+			}
+
+			Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, tag, docs, comment);
+			array_add(&fields, bf_field);
+
+			if (!allow_field_separator(f)) {
+				break;
+			}
+		}
+
+		Token close = expect_closing_brace_of_field_list(f);
+
+		f->expr_level = prev_level;
+
+		return ast_bit_field_type(f, token, backing_type, open, fields, close);
+	}
+
+
 	case Token_struct: {
 		Token    token = expect_token(f, Token_struct);
 		Ast *polymorphic_params = nullptr;
@@ -3923,6 +4015,10 @@ gb_internal Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> li
 		case Ast_Ident:
 		case Ast_BadExpr:
 			break;
+		case Ast_Implicit:
+			syntax_error(ident, "Expected an identifier, '%.*s' which is a keyword", LIT(ident->Implicit.string));
+			ident = ast_ident(f, blank_token);
+			break;
 
 		case Ast_PolyType:
 			if (allow_poly_names) {
@@ -3936,6 +4032,7 @@ gb_internal Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> li
 			}
 			/*fallthrough*/
 
+
 		default:
 			syntax_error(ident, "Expected an identifier");
 			ident = ast_ident(f, blank_token);

+ 17 - 0
src/parser.hpp

@@ -429,6 +429,7 @@ AST_KIND(_ExprBegin,  "",  bool) \
 		Ast *expr, *selector; \
 		u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \
 		u8 swizzle_indices; /*2 bits per component*/ \
+		bool is_bit_field; \
 	}) \
 	AST_KIND(ImplicitSelectorExpr, "implicit selector expression",    struct { Token token; Ast *selector; }) \
 	AST_KIND(SelectorCallExpr, "selector call expression", struct { \
@@ -650,6 +651,14 @@ AST_KIND(_DeclEnd,   "", bool) \
 		CommentGroup *   docs;      \
 		CommentGroup *   comment;   \
 	}) \
+	AST_KIND(BitFieldField, "bit field field", struct { \
+		Ast *         name;     \
+		Ast *         type;     \
+		Ast *         bit_size; \
+		Token         tag;      \
+		CommentGroup *docs;     \
+		CommentGroup *comment;  \
+	}) \
 	AST_KIND(FieldList, "field list", struct { \
 		Token token;       \
 		Slice<Ast *> list; \
@@ -742,6 +751,14 @@ AST_KIND(_TypeBegin, "", bool) \
 		Ast * elem;  \
 		Ast * underlying; \
 	}) \
+	AST_KIND(BitFieldType, "bit field type", struct { \
+		Scope *scope; \
+		Token token; \
+		Ast * backing_type;  \
+		Token open; \
+		Slice<Ast *> fields; /* BitFieldField */ \
+		Token close; \
+	}) \
 	AST_KIND(MapType, "map type", struct { \
 		Token token; \
 		Ast *count; \

+ 3 - 0
src/parser_pos.cpp

@@ -111,6 +111,7 @@ gb_internal Token ast_token(Ast *node) {
 	case Ast_UnionType:        return node->UnionType.token;
 	case Ast_EnumType:         return node->EnumType.token;
 	case Ast_BitSetType:       return node->BitSetType.token;
+	case Ast_BitFieldType:     return node->BitFieldType.token;
 	case Ast_MapType:          return node->MapType.token;
 	case Ast_MatrixType:       return node->MatrixType.token;
 	}
@@ -364,6 +365,8 @@ Token ast_end_token(Ast *node) {
 			return ast_end_token(node->BitSetType.underlying);
 		}
 		return ast_end_token(node->BitSetType.elem);
+	case Ast_BitFieldType:
+		return node->BitFieldType.close;
 	case Ast_MapType:          return ast_end_token(node->MapType.value);
 	case Ast_MatrixType:       return ast_end_token(node->MatrixType.elem);
 	}

+ 1 - 0
src/tokenizer.cpp

@@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
 	TOKEN_KIND(Token_union,       "union"),       \
 	TOKEN_KIND(Token_enum,        "enum"),        \
 	TOKEN_KIND(Token_bit_set,     "bit_set"),     \
+	TOKEN_KIND(Token_bit_field,   "bit_field"),   \
 	TOKEN_KIND(Token_map,         "map"),         \
 	TOKEN_KIND(Token_dynamic,     "dynamic"),     \
 	TOKEN_KIND(Token_auto_cast,   "auto_cast"),   \

+ 185 - 5
src/types.cpp

@@ -282,6 +282,15 @@ struct TypeProc {
 		Type *generic_column_count;                       \
 		i64   stride_in_bytes;                            \
 	})                                                        \
+	TYPE_KIND(BitField, struct {                              \
+		Scope *         scope;                            \
+		Type *          backing_type;                     \
+		Slice<Entity *> fields;                           \
+		String *        tags; /*count == fields.count*/   \
+		Slice<u8>       bit_sizes;                        \
+		Slice<i64>      bit_offsets;                      \
+		Ast *           node;                             \
+	})                                                        \
 	TYPE_KIND(SoaPointer, struct { Type *elem; })
 
 
@@ -355,6 +364,10 @@ enum Typeid_Kind : u8 {
 	Typeid_Relative_Multi_Pointer,
 	Typeid_Matrix,
 	Typeid_SoaPointer,
+	Typeid_Bit_Field,
+
+	Typeid__COUNT
+
 };
 
 // IMPORTANT NOTE(bill): This must match the same as the in core.odin
@@ -376,6 +389,9 @@ enum : int {
 
 gb_internal bool is_type_comparable(Type *t);
 gb_internal bool is_type_simple_compare(Type *t);
+gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false);
+gb_internal Type *base_type(Type *t);
+gb_internal Type *alloc_type_multi_pointer(Type *elem);
 
 gb_internal u32 type_info_flags_of_type(Type *type) {
 	if (type == nullptr) {
@@ -400,6 +416,7 @@ struct Selection {
 	bool       indirect; // Set if there was a pointer deref anywhere down the line
 	u8 swizzle_count;    // maximum components = 4
 	u8 swizzle_indices;  // 2 bits per component, representing which swizzle index
+	bool is_bit_field;
 	bool pseudo_field;
 };
 gb_global Selection const empty_selection = {0};
@@ -548,6 +565,14 @@ gb_global Type *t_f16             = &basic_types[Basic_f16];
 gb_global Type *t_f32             = &basic_types[Basic_f32];
 gb_global Type *t_f64             = &basic_types[Basic_f64];
 
+gb_global Type *t_f16be           = &basic_types[Basic_f16be];
+gb_global Type *t_f32be           = &basic_types[Basic_f32be];
+gb_global Type *t_f64be           = &basic_types[Basic_f64be];
+
+gb_global Type *t_f16le           = &basic_types[Basic_f16le];
+gb_global Type *t_f32le           = &basic_types[Basic_f32le];
+gb_global Type *t_f64le           = &basic_types[Basic_f64le];
+
 gb_global Type *t_complex32       = &basic_types[Basic_complex32];
 gb_global Type *t_complex64       = &basic_types[Basic_complex64];
 gb_global Type *t_complex128      = &basic_types[Basic_complex128];
@@ -641,6 +666,7 @@ gb_global Type *t_type_info_relative_pointer     = nullptr;
 gb_global Type *t_type_info_relative_multi_pointer = nullptr;
 gb_global Type *t_type_info_matrix               = nullptr;
 gb_global Type *t_type_info_soa_pointer          = nullptr;
+gb_global Type *t_type_info_bit_field            = nullptr;
 
 gb_global Type *t_type_info_named_ptr            = nullptr;
 gb_global Type *t_type_info_integer_ptr          = nullptr;
@@ -670,6 +696,7 @@ gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
 gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr;
 gb_global Type *t_type_info_matrix_ptr           = nullptr;
 gb_global Type *t_type_info_soa_pointer_ptr      = nullptr;
+gb_global Type *t_type_info_bit_field_ptr        = nullptr;
 
 gb_global Type *t_allocator                      = nullptr;
 gb_global Type *t_allocator_ptr                  = nullptr;
@@ -750,7 +777,6 @@ gb_internal bool  is_type_proc(Type *t);
 gb_internal bool  is_type_slice(Type *t);
 gb_internal bool  is_type_integer(Type *t);
 gb_internal bool  type_set_offsets(Type *t);
-gb_internal Type *base_type(Type *t);
 
 gb_internal i64 type_size_of_internal(Type *t, TypePath *path);
 gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
@@ -1040,6 +1066,11 @@ gb_internal Type *alloc_type_enum() {
 	return t;
 }
 
+gb_internal Type *alloc_type_bit_field() {
+	Type *t = alloc_type(Type_BitField);
+	return t;
+}
+
 gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) {
 	GB_ASSERT(is_type_pointer(pointer_type));
 	GB_ASSERT(is_type_integer(base_integer));
@@ -1140,7 +1171,7 @@ gb_internal Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_co
 ////////////////////////////////////////////////////////////////
 
 
-gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) {
+gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) {
 	if (t != nullptr) {
 		Type *bt = base_type(t);
 		if (bt == nullptr) {
@@ -1707,6 +1738,10 @@ gb_internal bool is_type_bit_set(Type *t) {
 	t = base_type(t);
 	return (t->kind == Type_BitSet);
 }
+gb_internal bool is_type_bit_field(Type *t) {
+	t = base_type(t);
+	return (t->kind == Type_BitField);
+}
 gb_internal bool is_type_map(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_Map;
@@ -2795,6 +2830,49 @@ gb_internal Type *default_type(Type *type) {
 	return type;
 }
 
+// See https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions
+gb_internal Type *c_vararg_promote_type(Type *type) {
+	GB_ASSERT(type != nullptr);
+
+	Type *core = core_type(type);
+
+	if (core->kind == Type_BitSet) {
+		core = core_type(bit_set_to_int(core));
+	}
+
+	if (core->kind == Type_Basic) {
+		switch (core->Basic.kind) {
+		case Basic_f32:
+		case Basic_UntypedFloat:
+			return t_f64;
+		case Basic_f32le:
+			return t_f64le;
+		case Basic_f32be:
+			return t_f64be;
+
+		case Basic_UntypedBool:
+		case Basic_bool:
+		case Basic_b8:
+		case Basic_b16:
+		case Basic_i8:
+		case Basic_i16:
+		case Basic_u8:
+		case Basic_u16:
+			return t_i32;
+
+		case Basic_i16le:
+		case Basic_u16le:
+			return t_i32le;
+
+		case Basic_i16be:
+		case Basic_u16be:
+			return t_i32be;
+		}
+	}
+
+	return type;
+}
+
 gb_internal bool union_variant_index_types_equal(Type *v, Type *vt) {
 	if (are_types_identical(v, vt)) {
 		return true;
@@ -3168,6 +3246,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
 			else if (field_name == "a") mapped_field_name = str_lit("w");
 			return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident);
 		}
+	} else if (type->kind == Type_BitField) {
+		for_array(i, type->BitField.fields) {
+			Entity *f = type->BitField.fields[i];
+			if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
+				continue;
+			}
+			String str = f->token.string;
+			if (field_name == str) {
+				selection_add_index(&sel, i);  // HACK(bill): Leaky memory
+				sel.entity = f;
+				sel.is_bit_field = true;
+				return sel;
+			}
+		}
+
 	} else if (type->kind == Type_Basic) {
 		switch (type->Basic.kind) {
 		case Basic_any: {
@@ -3568,6 +3661,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
 	case Type_Slice:
 		return build_context.int_size;
 
+	case Type_BitField:
+		return type_align_of_internal(t->BitField.backing_type, path);
 
 	case Type_Tuple: {
 		i64 max = 1;
@@ -3943,6 +4038,9 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
 		return stride_in_bytes * t->Matrix.column_count;
 	}
 
+	case Type_BitField:
+		return type_size_of_internal(t->BitField.backing_type, path);
+
 	case Type_RelativePointer:
 		return type_size_of_internal(t->RelativePointer.base_integer, path);
 	case Type_RelativeMultiPointer:
@@ -4114,8 +4212,10 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi
 		}
 		if (allow_polymorphic && dst_is_polymorphic) {
 			Type *fb = base_type(type_deref(f->type));
-			if (fb->kind == Type_Struct && fb->Struct.polymorphic_parent == dst) {
-				return true;
+			if (fb->kind == Type_Struct) {
+				if (fb->Struct.polymorphic_parent == dst) {
+					return true;
+				}
 			}
 		}
 
@@ -4217,7 +4317,70 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_
 	return t;
 }
 
-
+// gb_internal Type *type_from_selection(Type *type, Selection const &sel) {
+// 	for (i32 index : sel.index) {
+// 		Type *bt = base_type(type_deref(type));
+// 		switch (bt->kind) {
+// 		case Type_Struct:
+// 			type = bt->Struct.fields[index]->type;
+// 			break;
+// 		case Type_Tuple:
+// 			type = bt->Tuple.variables[index]->type;
+// 			break;
+// 		case Type_BitField:
+// 			type = bt->BitField.fields[index]->type;
+// 			break;
+// 		case Type_Array:
+// 			type = bt->Array.elem;
+// 			break;
+// 		case Type_EnumeratedArray:
+// 			type = bt->Array.elem;
+// 			break;
+// 		case Type_Slice:
+// 			switch (index) {
+// 			case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break;
+// 			case 1: type = t_int;                                    break;
+// 			}
+// 			break;
+// 		case Type_DynamicArray:
+// 			switch (index) {
+// 			case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break;
+// 			case 1: type = t_int;                                           break;
+// 			case 2: type = t_int;                                           break;
+// 			case 3: type = t_allocator;                                     break;
+// 			}
+// 			break;
+// 		case Type_Map:
+// 			switch (index) {
+// 			case 0: type = t_uintptr;   break;
+// 			case 1: type = t_int;       break;
+// 			case 2: type = t_allocator; break;
+// 			}
+// 			break;
+// 		case Type_Basic:
+// 			if (is_type_complex_or_quaternion(bt)) {
+// 				type = base_complex_elem_type(bt);
+// 			} else {
+// 				switch (type->Basic.kind) {
+// 				case Basic_any:
+// 					switch (index) {
+// 					case 0: type = t_rawptr; break;
+// 					case 1: type = t_typeid; break;
+// 					}
+// 					break;
+// 				case Basic_string:
+// 					switch (index) {
+// 					case 0: type = t_u8_multi_ptr; break;
+// 					case 1: type = t_int;          break;
+// 					}
+// 					break;
+// 				}
+// 			}
+// 			break;
+// 		}
+// 	}
+// 	return type;
+// }
 
 gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
 	if (type == nullptr) {
@@ -4525,6 +4688,23 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
 		str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count));
 		str = write_type_to_string(str, type->Matrix.elem);
 		break;
+
+	case Type_BitField:
+		str = gb_string_appendc(str, "bit_field ");
+		str = write_type_to_string(str, type->BitField.backing_type);
+		str = gb_string_appendc(str, " {");
+		for (isize i = 0; i < type->BitField.fields.count; i++) {
+			Entity *f = type->BitField.fields[i];
+			if (i > 0) {
+				str = gb_string_appendc(str, ", ");
+			}
+			str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
+			str = gb_string_appendc(str, ": ");
+			str = write_type_to_string(str, f->type);
+			str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]);
+		}
+		str = gb_string_appendc(str, " }");
+		break;
 	}
 
 	return str;

+ 28 - 22
tests/core/Makefile

@@ -1,8 +1,11 @@
 ODIN=../../odin
 PYTHON=$(shell which python3)
+COMMON=-vet -strict-style
+COLLECTION=-collection:tests=..
 
 all: c_libc_test \
 	 compress_test \
+	 container_test \
 	 crypto_test \
 	 download_test_assets \
 	 encoding_test \
@@ -27,64 +30,67 @@ download_test_assets:
 	$(PYTHON) download_assets.py
 
 image_test:
-	$(ODIN) run image/test_core_image.odin -file -out:test_core_image
+	$(ODIN) run image $(COMMON) -out:test_core_image
 
 compress_test:
-	$(ODIN) run compress/test_core_compress.odin -file -out:test_core_compress
+	$(ODIN) run compress $(COMMON) -out:test_core_compress
+
+container_test:
+	$(ODIN) run container $(COMMON) $(COLLECTION) -out:test_core_container
 
 strings_test:
-	$(ODIN) run strings/test_core_strings.odin -file -out:test_core_strings
+	$(ODIN) run strings $(COMMON) -out:test_core_strings
 
 hash_test:
-	$(ODIN) run hash -o:speed -no-bounds-check -out:test_hash 
+	$(ODIN) run hash $(COMMON) -o:speed -no-bounds-check -out:test_hash
 
 crypto_test:
-	$(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto
+	$(ODIN) run crypto $(COMMON) -o:speed -no-bounds-check -out:test_crypto
 
 noise_test:
-	$(ODIN) run math/noise -out:test_noise
+	$(ODIN) run math/noise $(COMMON) -out:test_noise
 
 encoding_test:
-	$(ODIN) run encoding/hxa    -out:test_hxa -collection:tests=..
-	$(ODIN) run encoding/json   -out:test_json
-	$(ODIN) run encoding/varint -out:test_varint
-	$(ODIN) run encoding/xml    -out:test_xml
+	$(ODIN) run encoding/hxa    $(COMMON) $(COLLECTION) -out:test_hxa
+	$(ODIN) run encoding/json   $(COMMON) -out:test_json
+	$(ODIN) run encoding/varint $(COMMON) -out:test_varint
+	$(ODIN) run encoding/xml    $(COMMON) -out:test_xml
 
 math_test:
-	$(ODIN) run math/test_core_math.odin -file -collection:tests=.. -out:test_core_math
+	$(ODIN) run math $(COMMON) $(COLLECTION) -out:test_core_math
 
 linalg_glsl_math_test:
-	$(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -collection:tests=.. -out:test_linalg_glsl_math
+	$(ODIN) run math/linalg/glsl $(COMMON) $(COLLECTION) -out:test_linalg_glsl_math
 
 filepath_test:
-	$(ODIN) run path/filepath/test_core_filepath.odin -file -collection:tests=.. -out:test_core_filepath
+	$(ODIN) run path/filepath $(COMMON) $(COLLECTION) -out:test_core_filepath
 
 reflect_test:
-	$(ODIN) run reflect/test_core_reflect.odin -file -collection:tests=.. -out:test_core_reflect
+	$(ODIN) run reflect $(COMMON) $(COLLECTION) -out:test_core_reflect
 
 slice_test:
-	$(ODIN) run slice/test_core_slice.odin -file -out:test_core_slice
+	$(ODIN) run slice $(COMMON) -out:test_core_slice
 
 os_exit_test:
 	$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0
 
 i18n_test:
-	$(ODIN) run text/i18n -out:test_core_i18n
+	$(ODIN) run text/i18n $(COMMON) -out:test_core_i18n
 
 match_test:
-	$(ODIN) run text/match -out:test_core_match
+	$(ODIN) run text/match $(COMMON) -out:test_core_match
 
 c_libc_test:
-	$(ODIN) run c/libc -out:test_core_libc
+	$(ODIN) run c/libc $(COMMON) -out:test_core_libc
 
 net_test:
-	$(ODIN) run net -out:test_core_net
+	$(ODIN) run net $(COMMON) -out:test_core_net
 
 fmt_test:
-	$(ODIN) run fmt -out:test_core_fmt
+	$(ODIN) run fmt $(COMMON) -out:test_core_fmt
 
 thread_test:
-	$(ODIN) run thread -out:test_core_thread
+	$(ODIN) run thread $(COMMON) -out:test_core_thread
 
 runtime_test:
-	$(ODIN) run runtime -out:test_core_runtime
+	$(ODIN) run runtime $(COMMON) -out:test_core_runtime

+ 0 - 1
tests/core/c/libc/test_core_libc.odin

@@ -2,7 +2,6 @@ package test_core_libc
 
 import "core:fmt"
 import "core:os"
-import "core:strings"
 import "core:testing"
 
 TEST_count := 0

+ 161 - 0
tests/core/container/test_core_avl.odin

@@ -0,0 +1,161 @@
+package test_core_container
+
+import "core:container/avl"
+import "core:math/rand"
+import "core:slice"
+import "core:testing"
+
+import tc "tests:common"
+
+@(test)
+test_avl :: proc(t: ^testing.T) {
+	tc.log(t, "Testing avl")
+
+	// Initialization.
+	tree: avl.Tree(int)
+	avl.init(&tree, slice.cmp_proc(int))
+	tc.expect(t, avl.len(&tree) == 0, "empty: len should be 0")
+	tc.expect(t, avl.first(&tree) == nil, "empty: first should be nil")
+	tc.expect(t, avl.last(&tree) == nil, "empty: last should be nil")
+
+	iter := avl.iterator(&tree, avl.Direction.Forward)
+	tc.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil")
+
+	// Test insertion.
+	NR_INSERTS :: 32 + 1 // Ensure at least 1 collision.
+	inserted_map := make(map[int]^avl.Node(int))
+	for i := 0; i < NR_INSERTS; i += 1 {
+		v := int(rand.uint32() & 0x1f)
+		existing_node, in_map := inserted_map[v]
+
+		n, ok, _ := avl.find_or_insert(&tree, v)
+		tc.expect(t, in_map != ok, "insert: ok should match inverse of map lookup")
+		if ok {
+			inserted_map[v] = n
+		} else {
+			tc.expect(t, existing_node == n, "insert: expecting existing node")
+		}
+	}
+	nrEntries := len(inserted_map)
+	tc.expect(t, avl.len(&tree) == nrEntries, "insert: len after")
+	tree_validate(t, &tree)
+
+	// Ensure that all entries can be found.
+	for k, v in inserted_map {
+		tc.expect(t, v == avl.find(&tree, k), "Find(): Node")
+		tc.expect(t, k == v.value, "Find(): Node value")
+	}
+
+	// Test the forward/backward iterators.
+	inserted_values: [dynamic]int
+	for k in inserted_map {
+		append(&inserted_values, k)
+	}
+	slice.sort(inserted_values[:])
+
+	iter = avl.iterator(&tree, avl.Direction.Forward)
+	visited: int
+	for node in avl.iterator_next(&iter) {
+		v, idx := node.value, visited
+		tc.expect(t, inserted_values[idx] == v, "iterator/forward: value")
+		tc.expect(t, node == avl.iterator_get(&iter), "iterator/forward: get")
+		visited += 1
+	}
+	tc.expect(t, visited == nrEntries, "iterator/forward: visited")
+
+	slice.reverse(inserted_values[:])
+	iter = avl.iterator(&tree, avl.Direction.Backward)
+	visited = 0
+	for node in avl.iterator_next(&iter) {
+		v, idx := node.value, visited
+		tc.expect(t, inserted_values[idx] == v, "iterator/backward: value")
+		visited += 1
+	}
+	tc.expect(t, visited == nrEntries, "iterator/backward: visited")
+
+	// Test removal.
+	rand.shuffle(inserted_values[:])
+	for v, i in inserted_values {
+		node := avl.find(&tree, v)
+		tc.expect(t, node != nil, "remove: find (pre)")
+
+		ok := avl.remove(&tree, v)
+		tc.expect(t, ok, "remove: succeeds")
+		tc.expect(t, nrEntries - (i + 1) == avl.len(&tree), "remove: len (post)")
+		tree_validate(t, &tree)
+
+		tc.expect(t, nil == avl.find(&tree, v), "remove: find (post")
+	}
+	tc.expect(t, avl.len(&tree) == 0, "remove: len should be 0")
+	tc.expect(t, avl.first(&tree) == nil, "remove: first should be nil")
+	tc.expect(t, avl.last(&tree) == nil, "remove: last should be nil")
+
+	// Refill the tree.
+	for v in inserted_values {
+		avl.find_or_insert(&tree, v)
+	}
+
+	// Test that removing the node doesn't break the iterator.
+	iter = avl.iterator(&tree, avl.Direction.Forward)
+	if node := avl.iterator_get(&iter); node != nil {
+		v := node.value
+
+		ok := avl.iterator_remove(&iter)
+		tc.expect(t, ok, "iterator/remove: success")
+
+		ok = avl.iterator_remove(&iter)
+		tc.expect(t, !ok, "iterator/remove: redundant removes should fail")
+
+		tc.expect(t, avl.find(&tree, v) == nil, "iterator/remove: node should be gone")
+		tc.expect(t, avl.iterator_get(&iter) == nil, "iterator/remove: get should return nil")
+
+		// Ensure that iterator_next still works.
+		node, ok = avl.iterator_next(&iter)
+		tc.expect(t, ok == (avl.len(&tree) > 0), "iterator/remove: next should return false")
+		tc.expect(t, node == avl.first(&tree), "iterator/remove: next should return first")
+
+		tree_validate(t, &tree)
+	}
+	tc.expect(t, avl.len(&tree) == nrEntries - 1, "iterator/remove: len should drop by 1")
+
+	avl.destroy(&tree)
+	tc.expect(t, avl.len(&tree) == 0, "destroy: len should be 0")
+}
+
+@(private)
+tree_validate :: proc(t: ^testing.T, tree: ^avl.Tree($Value)) {
+	tree_check_invariants(t, tree, tree._root, nil)
+}
+
+@(private)
+tree_check_invariants :: proc(
+	t: ^testing.T,
+	tree: ^avl.Tree($Value),
+	node, parent: ^avl.Node(Value),
+) -> int {
+	if node == nil {
+		return 0
+	}
+
+	// Validate the parent pointer.
+	tc.expect(t, parent == node._parent, "invalid parent pointer")
+
+	// Validate that the balance factor is -1, 0, 1.
+	tc.expect(
+		t,
+		node._balance == -1 || node._balance == 0 || node._balance == 1,
+		"invalid balance factor",
+	)
+
+	// Recursively derive the height of the left and right sub-trees.
+	l_height := tree_check_invariants(t, tree, node._left, node)
+	r_height := tree_check_invariants(t, tree, node._right, node)
+
+	// Validate the AVL invariant and the balance factor.
+	tc.expect(t, int(node._balance) == r_height - l_height, "AVL balance factor invariant violated")
+	if l_height > r_height {
+		return l_height + 1
+	}
+
+	return r_height + 1
+}

+ 26 - 0
tests/core/container/test_core_container.odin

@@ -0,0 +1,26 @@
+package test_core_container
+
+import "core:fmt"
+import "core:testing"
+
+import tc "tests:common"
+
+expect_equal :: proc(t: ^testing.T, the_slice, expected: []int, loc := #caller_location) {
+    _eq :: proc(a, b: []int) -> bool {
+        if len(a) != len(b) do return false
+        for a, i in a {
+            if b[i] != a do return false
+        }
+        return true
+    }
+    tc.expect(t, _eq(the_slice, expected), fmt.tprintf("Expected %v, got %v\n", the_slice, expected), loc)
+}
+
+main :: proc() {
+	t := testing.T{}
+
+	test_avl(&t)
+	test_small_array(&t)
+
+	tc.report(&t)
+}

+ 9 - 19
tests/core/container/test_core_small_array.odin

@@ -1,29 +1,19 @@
-package test_core_compress
+package test_core_container
 
-import "core:fmt"
 import "core:testing"
 import "core:container/small_array"
+
 import tc "tests:common"
 
-main :: proc() {
-    t := testing.T{}
-    test_small_array_removes(&t)
-    test_small_array_inject_at(&t)
-	tc.report(&t)
-}
+@(test)
+test_small_array :: proc(t: ^testing.T) {
+	tc.log(t, "Testing small_array")
 
-expect_equal :: proc(t: ^testing.T, the_slice, expected: []int, loc := #caller_location) {
-    _eq :: proc(a, b: []int) -> bool {
-        if len(a) != len(b) do return false
-        for a, i in a {
-            if b[i] != a do return false
-        }
-        return true
-    }
-    tc.expect(t, _eq(the_slice, expected), fmt.tprintf("Expected %v, got %v\n", the_slice, expected), loc)
+    test_small_array_removes(t)
+    test_small_array_inject_at(t)
 }
 
-@test
+@(test)
 test_small_array_removes :: proc(t: ^testing.T) {
     array: small_array.Small_Array(10, int)
     small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
@@ -42,7 +32,7 @@ test_small_array_removes :: proc(t: ^testing.T) {
     expect_equal(t, small_array.slice(&array), []int { 9, 2, 7, 4 })
 }
 
-@test
+@(test)
 test_small_array_inject_at :: proc(t: ^testing.T) {
     array: small_array.Small_Array(13, int)
     small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

+ 84 - 132
tests/core/net/test_core_net.odin

@@ -15,6 +15,7 @@ import "core:mem"
 import "core:fmt"
 import "core:net"
 import "core:strconv"
+import "core:sync"
 import "core:time"
 import "core:thread"
 import "core:os"
@@ -62,11 +63,7 @@ main :: proc() {
 
 	address_parsing_test(t)
 
-	when ODIN_OS != .Windows {
-		fmt.printf("IMPORTANT: `core:thread` seems to still be a bit wonky on Linux and MacOS, so we can't run tests relying on them.\n", ODIN_OS)
-	} else {
-		tcp_tests(t)
-	}
+	tcp_tests(t)
 
 	split_url_test(t)
 	join_url_test(t)
@@ -338,174 +335,129 @@ IP_Address_Parsing_Test_Vectors :: []IP_Address_Parsing_Test_Vector{
 	{ .IP6, "c0a8",                    "", ""},
 }
 
-
-ENDPOINT := net.Endpoint{
-	net.IP4_Address{127, 0, 0, 1},
-	9999,
-}
-
-CONTENT := "Hellope!"
-
-SEND_TIMEOUT :: time.Duration(1 * time.Second)
-RECV_TIMEOUT :: time.Duration(1 * time.Second)
-
-Thread_Data :: struct {
-	skt: net.Any_Socket,
-	err: net.Network_Error,
-	tid: ^thread.Thread,
-
-	no_accept: bool,  // Tell the server proc not to accept.
-
-	data:   [1024]u8, // Received data and its length
-	length: int,
-}
-
-thread_data := [3]Thread_Data{}
-
-/*
-	This runs a bunch of socket tests using threads:
-	- two servers trying to bind the same endpoint
-	- client trying to connect to closed port
-	- client trying to connect to an open port with a non-accepting server
-	- client sending server data and server sending client data
-	- etc.
-*/
 tcp_tests :: proc(t: ^testing.T) {
 	fmt.println("Testing two servers trying to bind to the same endpoint...")
 	two_servers_binding_same_endpoint(t)
 	fmt.println("Testing client connecting to a closed port...")
 	client_connects_to_closed_port(t)
-	fmt.println("Testing client connecting to port that doesn't accept...")
-	client_connects_to_open_but_non_accepting_port(t)
 	fmt.println("Testing client sending server data...")
 	client_sends_server_data(t)
 }
 
-tcp_client :: proc(retval: rawptr) {
-	send :: proc(content: []u8) -> (err: net.Network_Error) {
-		skt := net.dial_tcp(ENDPOINT) or_return
-		defer net.close(skt)
-
-		net.set_option(skt, .Send_Timeout,    SEND_TIMEOUT)
-		net.set_option(skt, .Receive_Timeout, RECV_TIMEOUT)
-
-		_, err = net.send(skt, content)
-		return
-	}
-
-	r := transmute(^Thread_Data)retval
-	r.err = send(transmute([]u8)CONTENT)
-	return
+ENDPOINT := net.Endpoint{
+	net.IP4_Address{127, 0, 0, 1},
+	9999,
 }
 
-tcp_server :: proc(retval: rawptr) {
-	r := transmute(^Thread_Data)retval
-
-	if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil {
-		return
-	}
-	defer net.close(r.skt)
-
-	if r.no_accept {
-		// Don't accept any connections, just listen.
-		return
-	}
-
-	client: net.TCP_Socket
-	if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil {
-		return
-	}
-	defer net.close(client)
-
+@(test)
+two_servers_binding_same_endpoint :: proc(t: ^testing.T) {
+	skt1, err1 := net.listen_tcp(ENDPOINT)
+	defer net.close(skt1)
+	skt2, err2 := net.listen_tcp(ENDPOINT)
+	defer net.close(skt2)
 
-	r.length, r.err = net.recv_tcp(client, r.data[:])
-	return
+	expect(t, err1 == nil, "expected first server binding to endpoint to do so without error")
+	expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use")
 }
 
-cleanup_thread :: proc(data: Thread_Data) {
-	net.close(data.skt)
-
-	thread.terminate(data.tid, 1)
-	thread.destroy(data.tid)
+@(test)
+client_connects_to_closed_port :: proc(t: ^testing.T) {
+	skt, err := net.dial_tcp(ENDPOINT)
+	defer net.close(skt)
+	expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused")
 }
 
-two_servers_binding_same_endpoint :: proc(t: ^testing.T) {
-	thread_data = {}
+@(test)
+client_sends_server_data :: proc(t: ^testing.T) {
+	CONTENT: string: "Hellope!"
 
-	thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
-	thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_server, context)
+	SEND_TIMEOUT :: time.Duration(1 * time.Second)
+	RECV_TIMEOUT :: time.Duration(1 * time.Second)
 
-	defer {
-		cleanup_thread(thread_data[0])
-		cleanup_thread(thread_data[1])
-	}
+	Thread_Data :: struct {
+		t: ^testing.T,
+		skt: net.Any_Socket,
+		err: net.Network_Error,
+		tid: ^thread.Thread,
 
-	// Give the two servers enough time to try and bind the same endpoint
-	time.sleep(1 * time.Second)
+		data:   [1024]u8, // Received data and its length
+		length: int,
+		wg:     ^sync.Wait_Group,
+	}
 
-	first_won  := thread_data[0].err == nil && thread_data[1].err == net.Bind_Error.Address_In_Use
-	second_won := thread_data[1].err == nil && thread_data[0].err == net.Bind_Error.Address_In_Use
+	tcp_client :: proc(thread_data: rawptr) {
+		r := transmute(^Thread_Data)thread_data
 
-	okay := first_won || second_won
-	msg  := fmt.tprintf("Expected servers to return `nil` and `Address_In_Use`, got %v and %v", thread_data[0].err, thread_data[1].err)
-	expect(t, okay, msg)
-}
+		defer sync.wait_group_done(r.wg)
 
-client_connects_to_closed_port :: proc(t: ^testing.T) {
-	thread_data = {}
+		if r.skt, r.err = net.dial_tcp(ENDPOINT); r.err != nil {
+			log(r.t, r.err)
+			return
+		}
 
-	thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_client, context)
+		net.set_option(r.skt, .Send_Timeout, SEND_TIMEOUT)
 
-	defer {
-		cleanup_thread(thread_data[0])
+		_, r.err = net.send(r.skt, transmute([]byte)CONTENT)
 	}
 
-	// Give the socket enough time to return `Refused`
-	time.sleep(4 * time.Second)
+	tcp_server :: proc(thread_data: rawptr) {
+		r := transmute(^Thread_Data)thread_data
 
-	okay := thread_data[0].err == net.Dial_Error.Refused
-	msg  := fmt.tprintf("Expected client to return `Refused` connecting to closed port, got %v", thread_data[0].err)
-	expect(t, okay, msg)
-}
+		defer sync.wait_group_done(r.wg)
 
-client_connects_to_open_but_non_accepting_port :: proc(t: ^testing.T) {
-	thread_data = {}
+		log(r.t, "tcp_server listen")
+		if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil {
+			sync.wait_group_done(r.wg)
+			log(r.t, r.err)
+			return
+		}
 
-	// Tell server proc not to accept
-	thread_data[0].no_accept = true
+		sync.wait_group_done(r.wg)
 
-	thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
-	thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context)
+		log(r.t, "tcp_server accept")
+		client: net.TCP_Socket
+		if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil {
+			log(r.t, r.err)
+			return
+		}
+		defer net.close(client)
 
-	defer {
-		cleanup_thread(thread_data[0])
-		cleanup_thread(thread_data[1])
-	}
+		net.set_option(client, .Receive_Timeout, RECV_TIMEOUT)
 
-	// Give the two servers enough time to try and bind the same endpoint
-	time.sleep(4 * time.Second)
+		r.length, r.err = net.recv_tcp(client, r.data[:])
+		return
+	}
+	
+	thread_data := [2]Thread_Data{}
 
-	okay := thread_data[0].err == nil && thread_data[1].err == net.Dial_Error.Refused
-	msg  := fmt.tprintf("Expected server and client to return `nil` and `Refused`, got %v and %v", thread_data[0].err, thread_data[1].err)
-	expect(t, okay, msg)
-}
+	wg: sync.Wait_Group
+	sync.wait_group_add(&wg, 1)
 
-client_sends_server_data :: proc(t: ^testing.T) {
-	thread_data = {}
+	thread_data[0].t = t
+	thread_data[0].wg = &wg
+	thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
+	
+	log(t, "waiting for server to start listening")
+	sync.wait_group_wait(&wg)
+	log(t, "starting up client")
 
-	// Tell server proc not to accept
-	// thread_data[0].no_accept = true
+	sync.wait_group_add(&wg, 2)
 
-	thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
+	thread_data[1].t = t
+	thread_data[1].wg = &wg
 	thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context)
 
 	defer {
-		cleanup_thread(thread_data[0])
-		cleanup_thread(thread_data[1])
+		net.close(thread_data[0].skt)
+		thread.destroy(thread_data[0].tid)
+
+		net.close(thread_data[1].skt)
+		thread.destroy(thread_data[1].tid)
 	}
 
-	// Give the two servers enough time to try and bind the same endpoint
-	time.sleep(1 * time.Second)
+	log(t, "waiting for threads to finish")
+	sync.wait_group_wait(&wg)
+	log(t, "threads finished")
 
 	okay := thread_data[0].err == nil && thread_data[1].err == nil
 	msg  := fmt.tprintf("Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err)

+ 5 - 2
tests/core/text/match/test_core_text_match.odin

@@ -202,8 +202,11 @@ test_captures :: proc(t: ^testing.T) {
 	// match all captures
 	compare_captures :: proc(t: ^testing.T, test: ^Temp, haystack: string, comp: []string, loc := #caller_location) {
 		length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures)
-		if failed(t, len(comp) == length) {
-			logf(t, "Captures Compare Failed -> Lengths %d != %d\n", len(comp), length)
+		result := len(comp) == length && err == .OK
+		if failed(t, result == true) {
+			logf(t, "Captures Compare Failed!\n")
+			logf(t, "\tErr: %v\n", err)
+			logf(t, "\tLengths: %v != %v\n", len(comp), length)
 		}
 
 		for i in 0..<length {

+ 61 - 0
vendor/egl/egl.odin

@@ -0,0 +1,61 @@
+//+build linux
+package egl
+
+NativeDisplayType :: distinct rawptr
+NativeWindowType  :: distinct rawptr
+Display :: distinct rawptr
+Surface :: distinct rawptr
+Config  :: distinct rawptr
+Context :: distinct rawptr
+
+NO_DISPLAY :: Display(uintptr(0))
+NO_CONTEXT :: Context(uintptr(0))
+NO_SURFACE :: Surface(uintptr(0))
+
+CONTEXT_OPENGL_CORE_PROFILE_BIT :: 0x00000001
+WINDOW_BIT        :: 0x0004
+OPENGL_BIT        :: 0x0008
+
+BLUE_SIZE         :: 0x3022
+GREEN_SIZE        :: 0x3023
+RED_SIZE          :: 0x3024
+DEPTH_SIZE        :: 0x3025
+STENCIL_SIZE      :: 0x3026
+
+SURFACE_TYPE      :: 0x3033
+NONE              :: 0x3038
+COLOR_BUFFER_TYPE :: 0x303F
+RENDERABLE_TYPE   :: 0x3040
+CONFORMANT        :: 0x3042
+
+BACK_BUFFER          :: 0x3084
+RENDER_BUFFER        :: 0x3086
+GL_COLORSPACE_SRGB   :: 0x3089
+GL_COLORSPACE_LINEAR :: 0x308A
+RGB_BUFFER           :: 0x308E
+GL_COLORSPACE        :: 0x309D
+
+CONTEXT_MAJOR_VERSION       :: 0x3098
+CONTEXT_MINOR_VERSION       :: 0x30FB
+CONTEXT_OPENGL_PROFILE_MASK :: 0x30FD
+
+OPENGL_API        :: 0x30A2
+
+foreign import egl "system:EGL"
+@(default_calling_convention="c", link_prefix="egl")
+foreign egl {
+	GetDisplay          :: proc(display: NativeDisplayType) -> Display ---
+	Initialize          :: proc(display: Display, major: ^i32, minor: ^i32) -> i32 ---
+	BindAPI             :: proc(api: u32) -> i32 ---
+	ChooseConfig        :: proc(display: Display, attrib_list: ^i32, configs: ^Context, config_size: i32, num_config: ^i32) -> i32 ---
+	CreateWindowSurface :: proc(display: Display, config: Config, native_window: NativeWindowType, attrib_list: ^i32) -> Surface ---
+	CreateContext       :: proc(display: Display, config: Config, share_context: Context, attrib_list: ^i32) -> Context ---
+	MakeCurrent         :: proc(display: Display, draw: Surface, read: Surface, ctx: Context) -> i32 ---
+	SwapInterval        :: proc(display: Display, interval: i32) -> i32 ---
+	SwapBuffers         :: proc(display: Display, surface: Surface) -> i32 ---
+	GetProcAddress      :: proc(name: cstring) -> rawptr ---
+}
+
+gl_set_proc_address :: proc(p: rawptr, name: cstring) {
+	(^rawptr)(p)^ = GetProcAddress(name)
+}

+ 3 - 2
vendor/glfw/wrapper.odin

@@ -149,8 +149,9 @@ WaitEvents        :: glfw.WaitEvents
 WaitEventsTimeout :: glfw.WaitEventsTimeout
 PostEmptyEvent    :: glfw.PostEmptyEvent
 
-GetInputMode :: glfw.GetInputMode
-SetInputMode :: glfw.SetInputMode
+RawMouseMotionSupported :: glfw.RawMouseMotionSupported
+GetInputMode            :: glfw.GetInputMode
+SetInputMode            :: glfw.SetInputMode
 
 GetMouseButton :: glfw.GetMouseButton
 GetCursorPos :: proc "c" (window: WindowHandle) -> (xpos, ypos: f64) {

+ 6 - 6
vendor/raylib/raylib.odin

@@ -318,11 +318,11 @@ GlyphInfo :: struct {
 // Font type, includes texture and charSet array data
 Font :: struct {
 	baseSize:     c.int,          // Base size (default chars height)
-	charsCount:   c.int,          // Number of characters
-	charsPadding: c.int,          // Padding around the chars
+	glyphCount:   c.int,          // Number of characters
+	glyphPadding: c.int,          // Padding around the chars
 	texture:      Texture2D,      // Characters texture atlas
 	recs:         [^]Rectangle,   // Characters rectangles in texture
-	chars:        [^]GlyphInfo,    // Characters info data
+	glyphs:       [^]GlyphInfo,    // Characters info data
 }
 
 // Camera type, defines a camera position/orientation in 3d space
@@ -404,7 +404,7 @@ BoneInfo :: struct {
 }
 
 // Model type
-Model :: struct {
+Model :: struct #align(align_of(uintptr)) {
 	transform: Matrix,            // Local transform matrix
 
 	meshCount: c.int,             // Number of meshes
@@ -425,6 +425,7 @@ ModelAnimation :: struct {
 	frameCount: c.int,            // Number of animation frames
 	bones:      [^]BoneInfo,      // Bones information (skeleton)
 	framePoses: [^][^]Transform,  // Poses array by frame
+	name:       [32]byte,           // Animation name
 }
 
 // Ray type (useful for raycast)
@@ -490,7 +491,6 @@ VrDeviceInfo :: struct {
 	vResolution:            c.int,    // Vertical resolution in pixels
 	hScreenSize:            f32,      // Horizontal size in meters
 	vScreenSize:            f32,      // Vertical size in meters
-	vScreenCenter:          f32,      // Screen center in meters
 	eyeToScreenDistance:    f32,      // Distance between eye and display in meters
 	lensSeparationDistance: f32,      // Lens separation distance in meters
 	interpupillaryDistance: f32,      // IPD (distance between pupils) in meters
@@ -499,7 +499,7 @@ VrDeviceInfo :: struct {
 }
 
 // VR Stereo rendering configuration for simulator
-VrStereoConfig :: struct {
+VrStereoConfig :: struct #align(4) {
 	projection:        [2]Matrix,     // VR projection matrices (per eye)
 	viewOffset:        [2]Matrix,     // VR view offset matrices (per eye)
 	leftLensCenter:    [2]f32,        // VR left lens center

+ 1 - 1
vendor/vulkan/_gen/create_vulkan_odin_wrapper.py

@@ -565,7 +565,7 @@ def parse_structs(f):
                 # 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(["bit_field", bit_field_type, comment]))
+                    ffields.append(tuple(["bitfield", bit_field_type, comment]))
                     break
                     
 

+ 14 - 14
vendor/vulkan/structs.odin

@@ -7032,7 +7032,7 @@ WaylandSurfaceCreateInfoKHR :: struct {
 }
 
 VideoH264SpsVuiFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH264HrdParameters :: struct {
@@ -7069,7 +7069,7 @@ VideoH264SequenceParameterSetVui :: struct {
 }
 
 VideoH264SpsFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH264ScalingLists :: struct {
@@ -7108,7 +7108,7 @@ VideoH264SequenceParameterSet :: struct {
 }
 
 VideoH264PpsFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH264PictureParameterSet :: struct {
@@ -7140,7 +7140,7 @@ VideoH265SubLayerHrdParameters :: struct {
 }
 
 VideoH265HrdFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265HrdParameters :: struct {
@@ -7162,11 +7162,11 @@ VideoH265HrdParameters :: struct {
 }
 
 VideoH265VpsFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265ProfileTierLevelFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265ProfileTierLevel :: struct {
@@ -7200,7 +7200,7 @@ VideoH265ScalingLists :: struct {
 }
 
 VideoH265SpsVuiFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265SequenceParameterSetVui :: struct {
@@ -7237,11 +7237,11 @@ VideoH265PredictorPaletteEntries :: struct {
 }
 
 VideoH265SpsFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265ShortTermRefPicSetFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265ShortTermRefPicSet :: struct {
@@ -7309,7 +7309,7 @@ VideoH265SequenceParameterSet :: struct {
 }
 
 VideoH265PpsFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoH265PictureParameterSet :: struct {
@@ -7352,7 +7352,7 @@ VideoH265PictureParameterSet :: struct {
 }
 
 VideoDecodeH264PictureInfoFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoDecodeH264PictureInfo :: struct {
@@ -7367,7 +7367,7 @@ VideoDecodeH264PictureInfo :: struct {
 }
 
 VideoDecodeH264ReferenceInfoFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoDecodeH264ReferenceInfo :: struct {
@@ -7378,7 +7378,7 @@ VideoDecodeH264ReferenceInfo :: struct {
 }
 
 VideoDecodeH265PictureInfoFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoDecodeH265PictureInfo :: struct {
@@ -7396,7 +7396,7 @@ VideoDecodeH265PictureInfo :: struct {
 }
 
 VideoDecodeH265ReferenceInfoFlags :: struct {
-	bit_field: u32,
+	bitfield: u32,
 }
 
 VideoDecodeH265ReferenceInfo :: struct {