Browse Source

Merge branch 'master' into stdlib-parser-fixes

gingerBill 2 years ago
parent
commit
77e5854a16
100 changed files with 16329 additions and 2041 deletions
  1. 14 1
      build.bat
  2. 7 4
      build_odin.sh
  3. 1 0
      core/compress/zlib/zlib.odin
  4. 17 20
      core/encoding/entity/entity.odin
  5. 1 1
      core/encoding/json/marshal.odin
  6. 21 23
      core/encoding/xml/debug_print.odin
  7. 3 3
      core/encoding/xml/example/xml_example.odin
  8. 17 12
      core/encoding/xml/helpers.odin
  9. 16 16
      core/encoding/xml/tokenizer.odin
  10. 15 27
      core/encoding/xml/xml_reader.odin
  11. 14 35
      core/fmt/fmt.odin
  12. 8 7
      core/image/netpbm/helpers.odin
  13. 1 0
      core/image/netpbm/netpbm.odin
  14. 3 4
      core/image/png/helpers.odin
  15. 8 8
      core/image/png/png.odin
  16. 7 5
      core/math/fixed/fixed.odin
  17. 12 12
      core/math/math.odin
  18. 52 52
      core/mem/allocators.odin
  19. 35 35
      core/net/url.odin
  20. 25 25
      core/odin/doc-format/doc_format.odin
  21. 1 1
      core/odin/parser/parser.odin
  22. 86 92
      core/odin/printer/visit.odin
  23. 16 16
      core/odin/tokenizer/tokenizer.odin
  24. 59 60
      core/reflect/reflect.odin
  25. 7 8
      core/reflect/types.odin
  26. 5 5
      core/runtime/core.odin
  27. 5 2
      core/runtime/default_allocators_general.odin
  28. 118 98
      core/runtime/dynamic_map_internal.odin
  29. 9 9
      core/runtime/error_checks.odin
  30. 11 2
      core/runtime/internal.odin
  31. 6 9
      core/runtime/os_specific_windows.odin
  32. 17 9
      core/runtime/print.odin
  33. 2 2
      core/runtime/procs.odin
  34. 6 0
      core/sys/windows/types.odin
  35. 3 0
      core/sys/windows/user32.odin
  36. 2 0
      core/text/i18n/i18n.odin
  37. 34 8
      core/text/i18n/qt_linguist.odin
  38. 2 2
      core/thread/thread_windows.odin
  39. 19 17
      core/time/time.odin
  40. 28 6
      examples/demo/demo.odin
  41. 11 0
      src/array.cpp
  42. 41 3
      src/build_settings.cpp
  43. 5 2
      src/check_builtin.cpp
  44. 77 90
      src/check_decl.cpp
  45. 184 150
      src/check_expr.cpp
  46. 11 6
      src/check_stmt.cpp
  47. 10 4
      src/check_type.cpp
  48. 123 44
      src/checker.cpp
  49. 3 2
      src/checker.hpp
  50. 25 25
      src/docs_format.cpp
  51. 4 4
      src/docs_writer.cpp
  52. 11 8
      src/entity.cpp
  53. 2 2
      src/error.cpp
  54. 2 5
      src/exact_value.cpp
  55. 460 0
      src/linker.cpp
  56. 3 10
      src/llvm_backend.hpp
  57. 67 69
      src/llvm_backend_const.cpp
  58. 5 12
      src/llvm_backend_debug.cpp
  59. 27 17
      src/llvm_backend_expr.cpp
  60. 87 155
      src/llvm_backend_general.cpp
  61. 12 9
      src/llvm_backend_proc.cpp
  62. 1 4
      src/llvm_backend_stmt.cpp
  63. 7 6
      src/llvm_backend_type.cpp
  64. 1 6
      src/llvm_backend_utility.cpp
  65. 139 460
      src/main.cpp
  66. 139 34
      src/parser.cpp
  67. 3 0
      src/parser.hpp
  68. 813 0
      src/tilde.cpp
  69. 373 0
      src/tilde.hpp
  70. 1101 0
      src/tilde/tb.h
  71. BIN
      src/tilde/tb.lib
  72. 76 0
      src/tilde/tb_arena.h
  73. 330 0
      src/tilde/tb_coff.h
  74. 170 0
      src/tilde/tb_elf.h
  75. 132 0
      src/tilde/tb_formats.h
  76. 90 0
      src/tilde/tb_x64.h
  77. 443 0
      src/tilde_builtin.cpp
  78. 1040 0
      src/tilde_const.cpp
  79. 482 0
      src/tilde_debug.cpp
  80. 3871 0
      src/tilde_expr.cpp
  81. 1307 0
      src/tilde_proc.cpp
  82. 2614 0
      src/tilde_stmt.cpp
  83. 983 0
      src/tilde_type_info.cpp
  84. 2 2
      src/tokenizer.cpp
  85. 127 70
      src/types.cpp
  86. 14 20
      tests/core/encoding/hxa/test_core_hxa.odin
  87. 33 36
      tests/core/encoding/xml/test_core_xml.odin
  88. 4 10
      tests/core/math/linalg/glsl/test_linalg_glsl_math.odin
  89. 73 94
      tests/core/math/test_core_math.odin
  90. 6 12
      tests/core/path/filepath/test_core_filepath.odin
  91. 16 20
      tests/core/reflect/test_core_reflect.odin
  92. 2 6
      tests/core/text/i18n/test_core_text_i18n.odin
  93. 1 0
      tests/issues/run.bat
  94. 1 0
      tests/issues/run.sh
  95. 26 0
      tests/issues/test_issue_2666.odin
  96. 2 2
      vendor/OpenGL/helpers.odin
  97. 5 3
      vendor/OpenGL/wrappers.odin
  98. 2 3
      vendor/darwin/Foundation/NSNumber.odin
  99. 14 0
      vendor/directx/d3d11/d3d11.odin
  100. 3 0
      vendor/directx/d3d_compiler/d3d_compiler.odin

+ 14 - 1
build.bat

@@ -51,7 +51,10 @@ set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
 set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
 set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
 
 
 if not exist .git\ goto skip_git_hash
 if not exist .git\ goto skip_git_hash
-for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
+for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
+	set odin_version_raw=dev-%%i
+	set GIT_SHA=%%j
+)
 if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
 if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
 :skip_git_hash
 :skip_git_hash
 
 
@@ -77,6 +80,16 @@ set libs= ^
 	Synchronization.lib ^
 	Synchronization.lib ^
 	bin\llvm\windows\LLVM-C.lib
 	bin\llvm\windows\LLVM-C.lib
 
 
+rem DO NOT TOUCH!
+rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
+set tilde_backend=0
+if %tilde_backend% EQU 1 (
+	set libs=%libs% src\tilde\tb.lib
+	set compiler_defines=%compiler_defines% -DODIN_TILDE_BACKEND
+)
+rem DO NOT TOUCH!
+
+
 set linker_flags= -incremental:no -opt:ref -subsystem:console
 set linker_flags= -incremental:no -opt:ref -subsystem:console
 
 
 if %release_mode% EQU 0 ( rem Debug
 if %release_mode% EQU 0 ( rem Debug

+ 7 - 4
build_odin.sh

@@ -8,17 +8,20 @@ set -eu
 : ${ODIN_VERSION=dev-$(date +"%Y-%m")}
 : ${ODIN_VERSION=dev-$(date +"%Y-%m")}
 : ${GIT_SHA=}
 : ${GIT_SHA=}
 
 
-CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
 CXXFLAGS="$CXXFLAGS -std=c++14"
 CXXFLAGS="$CXXFLAGS -std=c++14"
 LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
 LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
 
 
-if [ -d ".git" ]; then
-	GIT_SHA=$(git rev-parse --short HEAD || :)
-	if [ "$GIT_SHA" ]; then
+if [ -d ".git" ] && [ $(which git) ]; then
+	versionTag=( $(git show --pretty='%cd %h' --date=format:%Y-%m --no-patch --no-notes HEAD) )
+	if [ $? -eq 0 ]; then
+		ODIN_VERSION="dev-${versionTag[0]}"
+		GIT_SHA="${versionTag[1]}"
 		CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
 		CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
 	fi
 	fi
 fi
 fi
 
 
+CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
+
 DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
 DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
 OS=$(uname)
 OS=$(uname)
 
 

+ 1 - 0
core/compress/zlib/zlib.odin

@@ -1,3 +1,4 @@
+//+vet !using-param
 package zlib
 package zlib
 
 
 /*
 /*

+ 17 - 20
core/encoding/entity/entity.odin

@@ -184,28 +184,26 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
 
 
 advance :: proc(t: ^Tokenizer) -> (err: Error) {
 advance :: proc(t: ^Tokenizer) -> (err: Error) {
 	if t == nil { return .Tokenizer_Is_Nil }
 	if t == nil { return .Tokenizer_Is_Nil }
-	using t
-
 	#no_bounds_check {
 	#no_bounds_check {
-		if read_offset < len(src) {
-			offset = read_offset
-			r, w   = rune(src[read_offset]), 1
+		if t.read_offset < len(t.src) {
+			t.offset = t.read_offset
+			t.r, t.w   = rune(t.src[t.read_offset]), 1
 			switch {
 			switch {
-			case r == 0:
+			case t.r == 0:
 				return .Illegal_NUL_Character
 				return .Illegal_NUL_Character
-			case r >= utf8.RUNE_SELF:
-				r, w = utf8.decode_rune_in_string(src[read_offset:])
-				if r == utf8.RUNE_ERROR && w == 1 {
+			case t.r >= utf8.RUNE_SELF:
+				t.r, t.w = utf8.decode_rune_in_string(t.src[t.read_offset:])
+				if t.r == utf8.RUNE_ERROR && t.w == 1 {
 					return .Illegal_UTF_Encoding
 					return .Illegal_UTF_Encoding
-				} else if r == utf8.RUNE_BOM && offset > 0 {
+				} else if t.r == utf8.RUNE_BOM && t.offset > 0 {
 					return .Illegal_BOM
 					return .Illegal_BOM
 				}
 				}
 			}
 			}
-			read_offset += w
+			t.read_offset += t.w
 			return .None
 			return .None
 		} else {
 		} else {
-			offset = len(src)
-			r = -1
+			t.offset = len(t.src)
+			t.r = -1
 			return
 			return
 		}
 		}
 	}
 	}
@@ -273,26 +271,25 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
 		All of these would be in the ASCII range.
 		All of these would be in the ASCII range.
 		Even if one is not, it doesn't matter. All characters we need to compare to extract are.
 		Even if one is not, it doesn't matter. All characters we need to compare to extract are.
 	*/
 	*/
-	using t
 
 
 	length := len(t.src)
 	length := len(t.src)
 	found  := false
 	found  := false
 
 
 	#no_bounds_check {
 	#no_bounds_check {
-		for read_offset < length {
-			if src[read_offset] == ';' {
+		for t.read_offset < length {
+			if t.src[t.read_offset] == ';' {
+				t.read_offset += 1
 				found = true
 				found = true
-				read_offset += 1
 				break
 				break
 			}
 			}
-			read_offset += 1
+			t.read_offset += 1
 		}
 		}
 	}
 	}
 
 
 	if found {
 	if found {
-		return string(src[offset + 1 : read_offset - 1]), .None
+		return string(t.src[t.offset + 1 : t.read_offset - 1]), .None
 	}
 	}
-	return string(src[offset : read_offset]), .Invalid_Entity_Encoding
+	return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding
 }
 }
 
 
 /*
 /*

+ 1 - 1
core/encoding/json/marshal.odin

@@ -207,7 +207,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 	case runtime.Type_Info_Relative_Pointer:
 	case runtime.Type_Info_Relative_Pointer:
 		return .Unsupported_Type
 		return .Unsupported_Type
 
 
-	case runtime.Type_Info_Relative_Slice:
+	case runtime.Type_Info_Relative_Multi_Pointer:
 		return .Unsupported_Type
 		return .Unsupported_Type
 		
 		
 	case runtime.Type_Info_Matrix:
 	case runtime.Type_Info_Matrix:

+ 21 - 23
core/encoding/xml/debug_print.odin

@@ -19,43 +19,39 @@ import "core:fmt"
 */
 */
 print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
 print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
 	if doc == nil { return }
 	if doc == nil { return }
-	using fmt
-
-	written += wprintf(writer, "[XML Prolog]\n")
+	written += fmt.wprintf(writer, "[XML Prolog]\n")
 
 
 	for attr in doc.prologue {
 	for attr in doc.prologue {
-		written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
+		written += fmt.wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
 	}
 	}
 
 
-	written += wprintf(writer, "[Encoding] %v\n", doc.encoding)
+	written += fmt.wprintf(writer, "[Encoding] %v\n", doc.encoding)
 
 
 	if len(doc.doctype.ident) > 0 {
 	if len(doc.doctype.ident) > 0 {
-		written += wprintf(writer, "[DOCTYPE]  %v\n", doc.doctype.ident)
+		written += fmt.wprintf(writer, "[DOCTYPE]  %v\n", doc.doctype.ident)
 
 
 		if len(doc.doctype.rest) > 0 {
 		if len(doc.doctype.rest) > 0 {
-		 	wprintf(writer, "\t%v\n", doc.doctype.rest)
+		 	fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
 		}
 		}
 	}
 	}
 
 
 	for comment in doc.comments {
 	for comment in doc.comments {
-		written += wprintf(writer, "[Pre-root comment]  %v\n", comment)
+		written += fmt.wprintf(writer, "[Pre-root comment]  %v\n", comment)
 	}
 	}
 
 
 	if len(doc.elements) > 0 {
 	if len(doc.elements) > 0 {
-	 	wprintln(writer, " --- ")
+	 	fmt.wprintln(writer, " --- ")
 	 	print_element(writer, doc, 0)
 	 	print_element(writer, doc, 0)
-	 	wprintln(writer, " --- ")
+	 	fmt.wprintln(writer, " --- ")
 	 }
 	 }
 
 
 	return written, .None
 	return written, .None
 }
 }
 
 
 print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) {
 print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) {
-	using fmt
-
 	tab :: proc(writer: io.Writer, indent: int) {
 	tab :: proc(writer: io.Writer, indent: int) {
 		for _ in 0..=indent {
 		for _ in 0..=indent {
-			wprintf(writer, "\t")
+			fmt.wprintf(writer, "\t")
 		}
 		}
 	}
 	}
 
 
@@ -64,22 +60,24 @@ print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID,
 	element := doc.elements[element_id]
 	element := doc.elements[element_id]
 
 
 	if element.kind == .Element {
 	if element.kind == .Element {
-		wprintf(writer, "<%v>\n", element.ident)
-		if len(element.value) > 0 {
-			tab(writer, indent + 1)
-			wprintf(writer, "[Value] %v\n", element.value)
+		fmt.wprintf(writer, "<%v>\n", element.ident)
+
+		for value in element.value {
+			switch v in value {
+			case string:
+				tab(writer, indent + 1)
+				fmt.wprintf(writer, "[Value] %v\n", v)
+			case Element_ID:
+				print_element(writer, doc, v, indent + 1)
+			}
 		}
 		}
 
 
 		for attr in element.attribs {
 		for attr in element.attribs {
 			tab(writer, indent + 1)
 			tab(writer, indent + 1)
-			wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
-		}
-
-		for child in element.children {
-			print_element(writer, doc, child, indent + 1)
+			fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
 		}
 		}
 	} else if element.kind == .Comment {
 	} else if element.kind == .Comment {
-		wprintf(writer, "[COMMENT] %v\n", element.value)
+		fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
 	}
 	}
 
 
 	return written, .None
 	return written, .None

+ 3 - 3
core/encoding/xml/example/xml_example.odin

@@ -72,10 +72,10 @@ example :: proc() {
 	 	return
 	 	return
 	}
 	}
 
 
-	printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].children), docs[0].element_count)
+	printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].value), docs[0].element_count)
 
 
-	crc32 := doc_hash(docs[0])
-	printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32)
+	crc32 := doc_hash(docs[0], false)
+	printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0x420dbac5 else "🤬", crc32)
 
 
 	for round in 0..<N {
 	for round in 0..<N {
 		defer xml.destroy(docs[round])
 		defer xml.destroy(docs[round])

+ 17 - 12
core/encoding/xml/helpers.odin

@@ -13,20 +13,25 @@ find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string
 	tag := doc.elements[parent_id]
 	tag := doc.elements[parent_id]
 
 
 	count := 0
 	count := 0
-	for child_id in tag.children {
-		child := doc.elements[child_id]
-		/*
-			Skip commments. They have no name.
-		*/
-		if child.kind  != .Element                { continue }
+	for v in tag.value {
+		switch child_id in v {
+		case string: continue
+		case Element_ID:
+			child := doc.elements[child_id]
+			/*
+				Skip commments. They have no name.
+			*/
+			if child.kind  != .Element                { continue }
 
 
-		/*
-			If the ident matches and it's the nth such child, return it.
-		*/
-		if child.ident == ident {
-			if count == nth                       { return child_id, true }
-			count += 1
+			/*
+				If the ident matches and it's the nth such child, return it.
+			*/
+			if child.ident == ident {
+				if count == nth                       { return child_id, true }
+				count += 1
+			}
 		}
 		}
+
 	}
 	}
 	return 0, false
 	return 0, false
 }
 }

+ 16 - 16
core/encoding/xml/tokenizer.odin

@@ -125,38 +125,38 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
 }
 }
 
 
 @(optimization_mode="speed")
 @(optimization_mode="speed")
-advance_rune :: proc(using t: ^Tokenizer) {
+advance_rune :: proc(t: ^Tokenizer) {
 	#no_bounds_check {
 	#no_bounds_check {
 		/*
 		/*
 			Already bounds-checked here.
 			Already bounds-checked here.
 		*/
 		*/
-		if read_offset < len(src) {
-			offset = read_offset
-			if ch == '\n' {
-				line_offset = offset
-				line_count += 1
+		if t.read_offset < len(t.src) {
+			t.offset = t.read_offset
+			if t.ch == '\n' {
+				t.line_offset = t.offset
+				t.line_count += 1
 			}
 			}
-			r, w := rune(src[read_offset]), 1
+			r, w := rune(t.src[t.read_offset]), 1
 			switch {
 			switch {
 			case r == 0:
 			case r == 0:
 				error(t, t.offset, "illegal character NUL")
 				error(t, t.offset, "illegal character NUL")
 			case r >= utf8.RUNE_SELF:
 			case r >= utf8.RUNE_SELF:
-				r, w = #force_inline utf8.decode_rune_in_string(src[read_offset:])
+				r, w = #force_inline utf8.decode_rune_in_string(t.src[t.read_offset:])
 				if r == utf8.RUNE_ERROR && w == 1 {
 				if r == utf8.RUNE_ERROR && w == 1 {
 					error(t, t.offset, "illegal UTF-8 encoding")
 					error(t, t.offset, "illegal UTF-8 encoding")
-				} else if r == utf8.RUNE_BOM && offset > 0 {
+				} else if r == utf8.RUNE_BOM && t.offset > 0 {
 					error(t, t.offset, "illegal byte order mark")
 					error(t, t.offset, "illegal byte order mark")
 				}
 				}
 			}
 			}
-			read_offset += w
-			ch = r
+			t.read_offset += w
+			t.ch = r
 		} else {
 		} else {
-			offset = len(src)
-			if ch == '\n' {
-				line_offset = offset
-				line_count += 1
+			t.offset = len(t.src)
+			if t.ch == '\n' {
+				t.line_offset = t.offset
+				t.line_count += 1
 			}
 			}
-			ch = -1
+			t.ch = -1
 		}
 		}
 	}
 	}
 }
 }

+ 15 - 27
core/encoding/xml/xml_reader.odin

@@ -125,16 +125,19 @@ Document :: struct {
 
 
 Element :: struct {
 Element :: struct {
 	ident:   string,
 	ident:   string,
-	value:   string,
+	value:   [dynamic]Value,
 	attribs: Attributes,
 	attribs: Attributes,
 
 
 	kind: enum {
 	kind: enum {
 		Element = 0,
 		Element = 0,
 		Comment,
 		Comment,
 	},
 	},
-
 	parent:   Element_ID,
 	parent:   Element_ID,
-	children: [dynamic]Element_ID,
+}
+
+Value :: union {
+	string,
+	Element_ID,
 }
 }
 
 
 Attribute :: struct {
 Attribute :: struct {
@@ -247,9 +250,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 
 
 	err =            .Unexpected_Token
 	err =            .Unexpected_Token
 	element, parent: Element_ID
 	element, parent: Element_ID
-
-	tag_is_open   := false
-	first_element := true
 	open: Token
 	open: Token
 
 
 	/*
 	/*
@@ -275,16 +275,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 					e.g. <odin - Start of new element.
 					e.g. <odin - Start of new element.
 				*/
 				*/
 				element = new_element(doc)
 				element = new_element(doc)
-				tag_is_open = true
-
-				if first_element {
-					/*
-						First element.
-					*/
-					parent   = element
-					first_element = false
+				if element == 0 { // First Element
+					parent = element
 				} else {
 				} else {
-					append(&doc.elements[parent].children, element)
+					append(&doc.elements[parent].value, element)
 				}
 				}
 
 
 				doc.elements[element].parent = parent
 				doc.elements[element].parent = parent
@@ -324,7 +318,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 					expect(t, .Gt) or_return
 					expect(t, .Gt) or_return
 					parent      = doc.elements[element].parent
 					parent      = doc.elements[element].parent
 					element     = parent
 					element     = parent
-					tag_is_open = false
 
 
 				case:
 				case:
 					error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
 					error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
@@ -344,7 +337,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 				}
 				}
 				parent      = doc.elements[element].parent
 				parent      = doc.elements[element].parent
 				element     = parent
 				element     = parent
-				tag_is_open = false
 
 
 			} else if open.kind == .Exclaim {
 			} else if open.kind == .Exclaim {
 				/*
 				/*
@@ -392,8 +384,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 							el := new_element(doc)
 							el := new_element(doc)
 							doc.elements[el].parent = element
 							doc.elements[el].parent = element
 							doc.elements[el].kind   = .Comment
 							doc.elements[el].kind   = .Comment
-							doc.elements[el].value  = comment
-							append(&doc.elements[element].children, el)
+							append(&doc.elements[el].value, comment)
+							append(&doc.elements[element].value, el)
 						}
 						}
 					}
 					}
 
 
@@ -436,9 +428,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 			/*
 			/*
 				End of file.
 				End of file.
 			*/
 			*/
-			if tag_is_open {
-				return doc, .Premature_EOF
-			}
 			break loop
 			break loop
 
 
 		case:
 		case:
@@ -450,7 +439,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 			needs_processing |= .Decode_SGML_Entities in opts.flags
 			needs_processing |= .Decode_SGML_Entities in opts.flags
 
 
 			if !needs_processing {
 			if !needs_processing {
-				doc.elements[element].value = body_text
+				append(&doc.elements[element].value, body_text)
 				continue
 				continue
 			}
 			}
 
 
@@ -472,10 +461,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
 
 
 			decoded, decode_err := entity.decode_xml(body_text, decode_opts)
 			decoded, decode_err := entity.decode_xml(body_text, decode_opts)
 			if decode_err == .None {
 			if decode_err == .None {
-				doc.elements[element].value = decoded
+				append(&doc.elements[element].value, decoded)
 				append(&doc.strings_to_free, decoded)
 				append(&doc.strings_to_free, decoded)
 			} else {
 			} else {
-				doc.elements[element].value = body_text
+				append(&doc.elements[element].value, body_text)
 			}
 			}
 		}
 		}
 	}
 	}
@@ -518,7 +507,7 @@ destroy :: proc(doc: ^Document) {
 
 
 	for el in doc.elements {
 	for el in doc.elements {
 		delete(el.attribs)
 		delete(el.attribs)
-		delete(el.children)
+		delete(el.value)
 	}
 	}
 	delete(doc.elements)
 	delete(doc.elements)
 
 
@@ -710,6 +699,5 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) {
 
 
 	cur := doc.element_count
 	cur := doc.element_count
 	doc.element_count += 1
 	doc.element_count += 1
-
 	return cur
 	return cur
 }
 }

+ 14 - 35
core/fmt/fmt.odin

@@ -835,22 +835,22 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
 // - fi: A pointer to an Info structure
 // - fi: A pointer to an Info structure
 // - verb: The invalid format verb
 // - verb: The invalid format verb
 //
 //
-fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
+fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
 	prev_in_bad := fi.in_bad
 	prev_in_bad := fi.in_bad
 	defer fi.in_bad = prev_in_bad
 	defer fi.in_bad = prev_in_bad
 	fi.in_bad = true
 	fi.in_bad = true
 
 
-	io.write_string(writer, "%!", &fi.n)
-	io.write_rune(writer, verb, &fi.n)
-	io.write_byte(writer, '(', &fi.n)
-	if arg.id != nil {
-		reflect.write_typeid(writer, arg.id, &fi.n)
-		io.write_byte(writer, '=', &fi.n)
-		fmt_value(fi, arg, 'v')
+	io.write_string(fi.writer, "%!", &fi.n)
+	io.write_rune(fi.writer, verb, &fi.n)
+	io.write_byte(fi.writer, '(', &fi.n)
+	if fi.arg.id != nil {
+		reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
+		io.write_byte(fi.writer, '=', &fi.n)
+		fmt_value(fi, fi.arg, 'v')
 	} else {
 	} else {
-		io.write_string(writer, "<nil>", &fi.n)
+		io.write_string(fi.writer, "<nil>", &fi.n)
 	}
 	}
-	io.write_byte(writer, ')', &fi.n)
+	io.write_byte(fi.writer, ')', &fi.n)
 }
 }
 // Formats a boolean value according to the specified format verb
 // Formats a boolean value according to the specified format verb
 //
 //
@@ -859,7 +859,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
 // - b: The boolean value to format
 // - b: The boolean value to format
 // - verb: The format verb
 // - verb: The format verb
 //
 //
-fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
+fmt_bool :: proc(fi: ^Info, b: bool, verb: rune) {
 	switch verb {
 	switch verb {
 	case 't', 'v':
 	case 't', 'v':
 		fmt_string(fi, b ? "true" : "false", 's')
 		fmt_string(fi, b ? "true" : "false", 's')
@@ -2535,32 +2535,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 
 
 		fmt_value(fi, absolute_ptr, verb)
 		fmt_value(fi, absolute_ptr, verb)
 
 
-	case runtime.Type_Info_Relative_Slice:
+	case runtime.Type_Info_Relative_Multi_Pointer:
 		ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
 		ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
+		absolute_ptr := any{ptr, info.pointer.id}
 
 
-		if verb == 'p' {
-			fmt_pointer(fi, ptr, 'p')
-		} else if ptr == nil {
-			io.write_string(fi.writer, "[]", &fi.n)
-		} else {
-			len_ptr := uintptr(v.data) + uintptr(info.base_integer.size)
-			len_any := any{rawptr(len_ptr), info.base_integer.id}
-			len, _ := reflect.as_int(len_any)
-			slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice)
-
-			fi.record_level += 1
-			defer fi.record_level -= 1
-
-			io.write_byte(fi.writer, '[', &fi.n)
-			defer io.write_byte(fi.writer, ']', &fi.n)
-
-			for i in 0..<len {
-				if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
-
-				data := uintptr(ptr) + uintptr(i*slice_type.elem_size)
-				fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb)
-			}
-		}
+		fmt_value(fi, absolute_ptr, verb)
 
 
 	case runtime.Type_Info_Matrix:
 	case runtime.Type_Info_Matrix:
 		fmt_matrix(fi, v, verb, info)
 		fmt_matrix(fi, v, verb, info)

+ 8 - 7
core/image/netpbm/helpers.odin

@@ -4,13 +4,14 @@ import "core:bytes"
 import "core:image"
 import "core:image"
 
 
 destroy :: proc(img: ^image.Image) -> bool {
 destroy :: proc(img: ^image.Image) -> bool {
-	if img == nil do return false
+	if img == nil {
+		return false
+	}
 
 
 	defer free(img)
 	defer free(img)
 	bytes.buffer_destroy(&img.pixels)
 	bytes.buffer_destroy(&img.pixels)
 
 
-	info, ok := img.metadata.(^image.Netpbm_Info)
-	if !ok do return false
+	info := img.metadata.(^image.Netpbm_Info) or_return
 
 
 	header_destroy(&info.header)
 	header_destroy(&info.header)
 	free(info)
 	free(info)
@@ -19,9 +20,9 @@ destroy :: proc(img: ^image.Image) -> bool {
 	return true
 	return true
 }
 }
 
 
-header_destroy :: proc(using header: ^Header) {
-	if format == .P7 && tupltype != "" {
-		delete(tupltype)
-		tupltype = ""
+header_destroy :: proc(header: ^Header) {
+	if header.format == .P7 && header.tupltype != "" {
+		delete(header.tupltype)
+		header.tupltype = ""
 	}
 	}
 }
 }

+ 1 - 0
core/image/netpbm/netpbm.odin

@@ -1,3 +1,4 @@
+//+vet !using-stmt
 package netpbm
 package netpbm
 
 
 import "core:bytes"
 import "core:bytes"

+ 3 - 4
core/image/png/helpers.odin

@@ -80,11 +80,10 @@ time :: proc(c: image.PNG_Chunk) -> (res: tIME, ok: bool) {
 }
 }
 
 
 core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
 core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
-	if png_time, png_ok := time(c); png_ok {
-		using png_time
+	if t, png_ok := time(c); png_ok {
 		return coretime.datetime_to_time(
 		return coretime.datetime_to_time(
-			int(year), int(month), int(day),
-			int(hour), int(minute), int(second),
+			int(t.year), int(t.month),  int(t.day),
+			int(t.hour), int(t.minute), int(t.second),
 		)
 		)
 	} else {
 	} else {
 		return {}, false
 		return {}, false

+ 8 - 8
core/image/png/png.odin

@@ -11,6 +11,7 @@
 // package png implements a PNG image reader
 // package png implements a PNG image reader
 //
 //
 // The PNG specification is at https://www.w3.org/TR/PNG/.
 // The PNG specification is at https://www.w3.org/TR/PNG/.
+//+vet !using-stmt
 package png
 package png
 
 
 import "core:compress"
 import "core:compress"
@@ -444,15 +445,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 			img.width  = int(header.width)
 			img.width  = int(header.width)
 			img.height = int(header.height)
 			img.height = int(header.height)
 
 
-			using header
 			h := image.PNG_IHDR{
 			h := image.PNG_IHDR{
-				width              = width,
-				height             = height,
-				bit_depth          = bit_depth,
-				color_type         = color_type,
-				compression_method = compression_method,
-				filter_method      = filter_method,
-				interlace_method   = interlace_method,
+				width              = header.width,
+				height             = header.height,
+				bit_depth          = header.bit_depth,
+				color_type         = header.color_type,
+				compression_method = header.compression_method,
+				filter_method      = header.filter_method,
+				interlace_method   = header.interlace_method,
 			}
 			}
 			info.header = h
 			info.header = h
 
 

+ 7 - 5
core/math/fixed/fixed.odin

@@ -29,13 +29,13 @@ Fixed52_12 :: distinct Fixed(i64, 12)
 
 
 
 
 init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
 init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
-	i, f := math.modf(val)
+	i, f := math.modf(math.abs(val))
 	x.i  = Backing(f * (1<<Fraction_Width))
 	x.i  = Backing(f * (1<<Fraction_Width))
 	x.i &= 1<<Fraction_Width - 1
 	x.i &= 1<<Fraction_Width - 1
 	x.i |= Backing(i) << Fraction_Width
 	x.i |= Backing(i) << Fraction_Width
+	if val < 0 do x.i *= -1
 }
 }
 
 
-
 init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
 init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
 	i, f := math.modf(val)
 	i, f := math.modf(val)
 	x.i  = fraction
 	x.i  = fraction
@@ -44,9 +44,11 @@ init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fracti
 }
 }
 
 
 to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
 to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
-	res := f64(x.i >> Fraction_Width)
-	res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
-	return res
+	sign := -1.0 if x.i < 0 else 1.0
+	num := math.abs(x.i)
+	res := f64(num >> Fraction_Width)
+	res += f64(num & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
+	return res * sign
 }
 }
 
 
 
 

+ 12 - 12
core/math/math.odin

@@ -2286,20 +2286,20 @@ F64_MASK  :: 0x7ff
 F64_SHIFT :: 64 - 12
 F64_SHIFT :: 64 - 12
 F64_BIAS  :: 0x3ff
 F64_BIAS  :: 0x3ff
 
 
-INF_F16     :f16: 0h7C00
-NEG_INF_F16 :f16: 0hFC00
+INF_F16     :: f16(0h7C00)
+NEG_INF_F16 :: f16(0hFC00)
 
 
-SNAN_F16    :f16: 0h7C01
-QNAN_F16    :f16: 0h7E01
+SNAN_F16    :: f16(0h7C01)
+QNAN_F16    :: f16(0h7E01)
 
 
-INF_F32     :f32: 0h7F80_0000
-NEG_INF_F32 :f32: 0hFF80_0000
+INF_F32     :: f32(0h7F80_0000)
+NEG_INF_F32 :: f32(0hFF80_0000)
 
 
-SNAN_F32    :f32: 0hFF80_0001
-QNAN_F32    :f32: 0hFFC0_0001
+SNAN_F32    :: f32(0hFF80_0001)
+QNAN_F32    :: f32(0hFFC0_0001)
 
 
-INF_F64     :f64: 0h7FF0_0000_0000_0000
-NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
+INF_F64     :: f64(0h7FF0_0000_0000_0000)
+NEG_INF_F64 :: f64(0hFFF0_0000_0000_0000)
 
 
-SNAN_F64    :f64: 0h7FF0_0000_0000_0001
-QNAN_F64    :f64: 0h7FF8_0000_0000_0001
+SNAN_F64    :: f64(0h7FF0_0000_0000_0001)
+QNAN_F64    :: f64(0h7FF8_0000_0000_0001)

+ 52 - 52
core/mem/allocators.odin

@@ -111,11 +111,11 @@ begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
 	return tmp
 	return tmp
 }
 }
 
 
-end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
-	assert(arena.offset >= prev_offset)
-	assert(arena.temp_count > 0)
-	arena.offset = prev_offset
-	arena.temp_count -= 1
+end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {
+	assert(tmp.arena.offset >= tmp.prev_offset)
+	assert(tmp.arena.temp_count > 0)
+	tmp.arena.offset = tmp.prev_offset
+	tmp.arena.temp_count -= 1
 }
 }
 
 
 
 
@@ -702,11 +702,11 @@ dynamic_pool_init :: proc(pool: ^Dynamic_Pool,
 	pool.         used_blocks.allocator = array_allocator
 	pool.         used_blocks.allocator = array_allocator
 }
 }
 
 
-dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
+dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) {
 	dynamic_pool_free_all(pool)
 	dynamic_pool_free_all(pool)
-	delete(unused_blocks)
-	delete(used_blocks)
-	delete(out_band_allocations)
+	delete(pool.unused_blocks)
+	delete(pool.used_blocks)
+	delete(pool.out_band_allocations)
 
 
 	zero(pool, size_of(pool^))
 	zero(pool, size_of(pool^))
 }
 }
@@ -719,90 +719,90 @@ dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocato
 }
 }
 
 
 @(require_results)
 @(require_results)
-dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
-	cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
-		if block_allocator.procedure == nil {
+dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
+	cycle_new_block :: proc(p: ^Dynamic_Pool) -> (err: Allocator_Error) {
+		if p.block_allocator.procedure == nil {
 			panic("You must call pool_init on a Pool before using it")
 			panic("You must call pool_init on a Pool before using it")
 		}
 		}
 
 
-		if current_block != nil {
-			append(&used_blocks, current_block)
+		if p.current_block != nil {
+			append(&p.used_blocks, p.current_block)
 		}
 		}
 
 
 		new_block: rawptr
 		new_block: rawptr
-		if len(unused_blocks) > 0 {
-			new_block = pop(&unused_blocks)
+		if len(p.unused_blocks) > 0 {
+			new_block = pop(&p.unused_blocks)
 		} else {
 		} else {
 			data: []byte
 			data: []byte
-			data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
-			                                           block_size, alignment,
-			                                           nil, 0)
+			data, err = p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
+			                                        p.block_size, p.alignment,
+			                                        nil, 0)
 			new_block = raw_data(data)
 			new_block = raw_data(data)
 		}
 		}
 
 
-		bytes_left = block_size
-		current_pos = new_block
-		current_block = new_block
+		p.bytes_left    = p.block_size
+		p.current_pos   = new_block
+		p.current_block = new_block
 		return
 		return
 	}
 	}
 
 
 	n := bytes
 	n := bytes
-	extra := alignment - (n % alignment)
+	extra := p.alignment - (n % p.alignment)
 	n += extra
 	n += extra
-	if n >= out_band_size {
-		assert(block_allocator.procedure != nil)
-		memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
-			                                block_size, alignment,
-			                                nil, 0)
+	if n >= p.out_band_size {
+		assert(p.block_allocator.procedure != nil)
+		memory, err := p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
+		                                           p.block_size, p.alignment,
+		                                           nil, 0)
 		if memory != nil {
 		if memory != nil {
-			append(&out_band_allocations, raw_data(memory))
+			append(&p.out_band_allocations, raw_data(memory))
 		}
 		}
 		return memory, err
 		return memory, err
 	}
 	}
 
 
-	if bytes_left < n {
-		err := cycle_new_block(pool)
+	if p.bytes_left < n {
+		err := cycle_new_block(p)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if current_block == nil {
+		if p.current_block == nil {
 			return nil, .Out_Of_Memory
 			return nil, .Out_Of_Memory
 		}
 		}
 	}
 	}
 
 
-	memory := current_pos
-	current_pos = ptr_offset((^byte)(current_pos), n)
-	bytes_left -= n
-	return byte_slice(memory, bytes), nil
+	memory := p.current_pos
+	p.current_pos = ([^]byte)(p.current_pos)[n:]
+	p.bytes_left -= n
+	return ([^]byte)(memory)[:bytes], nil
 }
 }
 
 
 
 
-dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
-	if current_block != nil {
-		append(&unused_blocks, current_block)
-		current_block = nil
+dynamic_pool_reset :: proc(p: ^Dynamic_Pool) {
+	if p.current_block != nil {
+		append(&p.unused_blocks, p.current_block)
+		p.current_block = nil
 	}
 	}
 
 
-	for block in used_blocks {
-		append(&unused_blocks, block)
+	for block in p.used_blocks {
+		append(&p.unused_blocks, block)
 	}
 	}
-	clear(&used_blocks)
+	clear(&p.used_blocks)
 
 
-	for a in out_band_allocations {
-		free(a, block_allocator)
+	for a in p.out_band_allocations {
+		free(a, p.block_allocator)
 	}
 	}
-	clear(&out_band_allocations)
+	clear(&p.out_band_allocations)
 
 
-	bytes_left = 0 // Make new allocations call `cycle_new_block` again.
+	p.bytes_left = 0 // Make new allocations call `cycle_new_block` again.
 }
 }
 
 
-dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
-	dynamic_pool_reset(pool)
+dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) {
+	dynamic_pool_reset(p)
 
 
-	for block in unused_blocks {
-		free(block, block_allocator)
+	for block in p.unused_blocks {
+		free(block, p.block_allocator)
 	}
 	}
-	clear(&unused_blocks)
+	clear(&p.unused_blocks)
 }
 }
 
 
 
 

+ 35 - 35
core/net/url.odin

@@ -63,100 +63,100 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
 }
 }
 
 
 join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
 join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
-	using strings
+	b := strings.builder_make(allocator)
+	strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
 
 
-	b := builder_make(allocator)
-	builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
-
-	write_string(&b, scheme)
-	write_string(&b, "://")
-	write_string(&b, trim_space(host))
+	strings.write_string(&b, scheme)
+	strings.write_string(&b, "://")
+	strings.write_string(&b, strings.trim_space(host))
 
 
 	if path != "" {
 	if path != "" {
-		if path[0] != '/' do write_string(&b, "/")
-		write_string(&b, trim_space(path))
+		if path[0] != '/' {
+			strings.write_string(&b, "/")
+		}
+		strings.write_string(&b, strings.trim_space(path))
 	}
 	}
 
 
 
 
 	query_length := len(queries)
 	query_length := len(queries)
-	if query_length > 0 do write_string(&b, "?")
+	if query_length > 0 {
+		strings.write_string(&b, "?")
+	}
 	i := 0
 	i := 0
 	for query_name, query_value in queries {
 	for query_name, query_value in queries {
-		write_string(&b, query_name)
+		strings.write_string(&b, query_name)
 		if query_value != "" {
 		if query_value != "" {
-			write_string(&b, "=")
-			write_string(&b, query_value)
+			strings.write_string(&b, "=")
+			strings.write_string(&b, query_value)
 		}
 		}
 		if i < query_length - 1 {
 		if i < query_length - 1 {
-			write_string(&b, "&")
+			strings.write_string(&b, "&")
 		}
 		}
 		i += 1
 		i += 1
 	}
 	}
 
 
-	return to_string(b)
+	return strings.to_string(b)
 }
 }
 
 
 percent_encode :: proc(s: string, allocator := context.allocator) -> string {
 percent_encode :: proc(s: string, allocator := context.allocator) -> string {
-	using strings
-
-	b := builder_make(allocator)
-	builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
+	b := strings.builder_make(allocator)
+	strings.builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
 
 
 	for ch in s {
 	for ch in s {
 		switch ch {
 		switch ch {
 		case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~':
 		case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~':
-			write_rune(&b, ch)
+			strings.write_rune(&b, ch)
 		case:
 		case:
 			bytes, n := utf8.encode_rune(ch)
 			bytes, n := utf8.encode_rune(ch)
 			for byte in bytes[:n] {
 			for byte in bytes[:n] {
 				buf: [2]u8 = ---
 				buf: [2]u8 = ---
 				t := strconv.append_int(buf[:], i64(byte), 16)
 				t := strconv.append_int(buf[:], i64(byte), 16)
-				write_rune(&b, '%')
-				write_string(&b, t)
+				strings.write_rune(&b, '%')
+				strings.write_string(&b, t)
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	return to_string(b)
+	return strings.to_string(b)
 }
 }
 
 
 percent_decode :: proc(encoded_string: string, allocator := context.allocator) -> (decoded_string: string, ok: bool) {
 percent_decode :: proc(encoded_string: string, allocator := context.allocator) -> (decoded_string: string, ok: bool) {
-	using strings
-
-	b := builder_make(allocator)
-	builder_grow(&b, len(encoded_string))
-	defer if !ok do builder_destroy(&b)
+	b := strings.builder_make(allocator)
+	strings.builder_grow(&b, len(encoded_string))
+	defer if !ok do strings.builder_destroy(&b)
 
 
 	s := encoded_string
 	s := encoded_string
 
 
 	for len(s) > 0 {
 	for len(s) > 0 {
-		i := index_byte(s, '%')
+		i := strings.index_byte(s, '%')
 		if i == -1 {
 		if i == -1 {
-			write_string(&b, s) // no '%'s; the string is already decoded
+			strings.write_string(&b, s) // no '%'s; the string is already decoded
 			break
 			break
 		}
 		}
 
 
-		write_string(&b, s[:i])
+		strings.write_string(&b, s[:i])
 		s = s[i:]
 		s = s[i:]
 
 
 		if len(s) == 0 do return // percent without anything after it
 		if len(s) == 0 do return // percent without anything after it
 		s = s[1:]
 		s = s[1:]
 
 
 		if s[0] == '%' {
 		if s[0] == '%' {
-			write_byte(&b, '%')
+			strings.write_byte(&b, '%')
 			s = s[1:]
 			s = s[1:]
 			continue
 			continue
 		}
 		}
 
 
-		if len(s) < 2 do return // percent without encoded value
+		if len(s) < 2 {
+			return // percent without encoded value
+		}
 
 
 		val := hex.decode_sequence(s[:2]) or_return
 		val := hex.decode_sequence(s[:2]) or_return
-		write_byte(&b, val)
+		strings.write_byte(&b, val)
 		s = s[2:]
 		s = s[2:]
 	}
 	}
 
 
 	ok = true
 	ok = true
-	decoded_string = to_string(b)
+	decoded_string = strings.to_string(b)
 	return
 	return
 }
 }
 
 

+ 25 - 25
core/odin/doc-format/doc_format.odin

@@ -162,31 +162,31 @@ Attribute :: struct {
 }
 }
 
 
 Type_Kind :: enum u32le {
 Type_Kind :: enum u32le {
-	Invalid            = 0,
-	Basic              = 1,
-	Named              = 2,
-	Generic            = 3,
-	Pointer            = 4,
-	Array              = 5,
-	Enumerated_Array   = 6,
-	Slice              = 7,
-	Dynamic_Array      = 8,
-	Map                = 9,
-	Struct             = 10,
-	Union              = 11,
-	Enum               = 12,
-	Tuple              = 13,
-	Proc               = 14,
-	Bit_Set            = 15,
-	Simd_Vector        = 16,
-	SOA_Struct_Fixed   = 17,
-	SOA_Struct_Slice   = 18,
-	SOA_Struct_Dynamic = 19,
-	Relative_Pointer   = 20,
-	Relative_Slice     = 21,
-	Multi_Pointer      = 22,
-	Matrix             = 23,
-	Soa_Pointer        = 24,
+	Invalid                = 0,
+	Basic                  = 1,
+	Named                  = 2,
+	Generic                = 3,
+	Pointer                = 4,
+	Array                  = 5,
+	Enumerated_Array       = 6,
+	Slice                  = 7,
+	Dynamic_Array          = 8,
+	Map                    = 9,
+	Struct                 = 10,
+	Union                  = 11,
+	Enum                   = 12,
+	Tuple                  = 13,
+	Proc                   = 14,
+	Bit_Set                = 15,
+	Simd_Vector            = 16,
+	SOA_Struct_Fixed       = 17,
+	SOA_Struct_Slice       = 18,
+	SOA_Struct_Dynamic     = 19,
+	Relative_Pointer       = 20,
+	Relative_Multi_Pointer = 21,
+	Multi_Pointer          = 22,
+	Matrix                 = 23,
+	Soa_Pointer            = 24,
 }
 }
 
 
 Type_Elems_Cap :: 4
 Type_Elems_Cap :: 4

+ 1 - 1
core/odin/parser/parser.odin

@@ -2943,7 +2943,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
 
 
 	seen_ellipsis := false
 	seen_ellipsis := false
 	for p.curr_tok.kind != .Close_Paren &&
 	for p.curr_tok.kind != .Close_Paren &&
-	    p.curr_tok.kind != .EOF {
+		p.curr_tok.kind != .EOF {
 
 
 		if p.curr_tok.kind == .Comma {
 		if p.curr_tok.kind == .Comma {
 			error(p, p.curr_tok.pos, "expected an expression not ,")
 			error(p, p.curr_tok.pos, "expected an expression not ,")

+ 86 - 92
core/odin/printer/visit.odin

@@ -336,22 +336,20 @@ hint_current_line :: proc(p: ^Printer, hint: Line_Type) {
 
 
 @(private)
 @(private)
 visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
-	using ast
-
 	if decl == nil {
 	if decl == nil {
 		return
 		return
 	}
 	}
 
 
 	#partial switch v in decl.derived_stmt {
 	#partial switch v in decl.derived_stmt {
-	case ^Expr_Stmt:
+	case ^ast.Expr_Stmt:
 		move_line(p, decl.pos)
 		move_line(p, decl.pos)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		if p.config.semicolons {
 		if p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 			push_generic_token(p, .Semicolon, 0)
 		}
 		}
-	case ^When_Stmt:
-		visit_stmt(p, cast(^Stmt)decl)
-	case ^Foreign_Import_Decl:
+	case ^ast.When_Stmt:
+		visit_stmt(p, cast(^ast.Stmt)decl)
+	case ^ast.Foreign_Import_Decl:
 		if len(v.attributes) > 0 {
 		if len(v.attributes) > 0 {
 			sort.sort(sort_attribute(&v.attributes))
 			sort.sort(sort_attribute(&v.attributes))
 			move_line(p, v.attributes[0].pos)
 			move_line(p, v.attributes[0].pos)
@@ -370,7 +368,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 		for path in v.fullpaths {
 		for path in v.fullpaths {
 			push_ident_token(p, path, 0)
 			push_ident_token(p, path, 0)
 		}
 		}
-	case ^Foreign_Block_Decl:
+	case ^ast.Foreign_Block_Decl:
 		if len(v.attributes) > 0 {
 		if len(v.attributes) > 0 {
 			sort.sort(sort_attribute(&v.attributes))
 			sort.sort(sort_attribute(&v.attributes))
 			move_line(p, v.attributes[0].pos)
 			move_line(p, v.attributes[0].pos)
@@ -383,7 +381,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 
 
 		visit_expr(p, v.foreign_library)
 		visit_expr(p, v.foreign_library)
 		visit_stmt(p, v.body)
 		visit_stmt(p, v.body)
-	case ^Import_Decl:
+	case ^ast.Import_Decl:
 		move_line(p, decl.pos)
 		move_line(p, decl.pos)
 
 
 		if v.name.text != "" {
 		if v.name.text != "" {
@@ -395,7 +393,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 			push_ident_token(p, v.fullpath, 1)
 			push_ident_token(p, v.fullpath, 1)
 		}
 		}
 
 
-	case ^Value_Decl:
+	case ^ast.Value_Decl:
 		if len(v.attributes) > 0 {
 		if len(v.attributes) > 0 {
 			sort.sort(sort_attribute(&v.attributes))
 			sort.sort(sort_attribute(&v.attributes))
 			move_line(p, v.attributes[0].pos)
 			move_line(p, v.attributes[0].pos)
@@ -447,9 +445,9 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 
 
 		for value in v.values {
 		for value in v.values {
 			#partial switch a in value.derived {
 			#partial switch a in value.derived {
-			case ^Union_Type, ^Enum_Type, ^Struct_Type:
+			case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type:
 				add_semicolon = false || called_in_stmt
 				add_semicolon = false || called_in_stmt
-			case ^Proc_Lit:
+			case ^ast.Proc_Lit:
 				add_semicolon = false
 				add_semicolon = false
 			}
 			}
 		}
 		}
@@ -510,40 +508,38 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) {
 
 
 @(private)
 @(private)
 visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) {
 visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) {
-	using ast
-
 	if stmt == nil {
 	if stmt == nil {
 		return
 		return
 	}
 	}
 
 
 
 
 	switch v in stmt.derived_stmt {
 	switch v in stmt.derived_stmt {
-	case ^Bad_Stmt:
-	case ^Bad_Decl:
-	case ^Package_Decl:
+	case ^ast.Bad_Stmt:
+	case ^ast.Bad_Decl:
+	case ^ast.Package_Decl:
 
 
-	case ^Empty_Stmt:
+	case ^ast.Empty_Stmt:
 		push_generic_token(p, .Semicolon, 0)
 		push_generic_token(p, .Semicolon, 0)
-	case ^Tag_Stmt:
+	case ^ast.Tag_Stmt:
 		push_generic_token(p, .Hash, 1)
 		push_generic_token(p, .Hash, 1)
 		push_generic_token(p, v.op.kind, 1, v.op.text)
 		push_generic_token(p, v.op.kind, 1, v.op.text)
 		visit_stmt(p, v.stmt)
 		visit_stmt(p, v.stmt)
 
 
 
 
-	case ^Import_Decl:
-		visit_decl(p, cast(^Decl)stmt, true)
+	case ^ast.Import_Decl:
+		visit_decl(p, cast(^ast.Decl)stmt, true)
 		return
 		return
-	case ^Value_Decl:
-		visit_decl(p, cast(^Decl)stmt, true)
+	case ^ast.Value_Decl:
+		visit_decl(p, cast(^ast.Decl)stmt, true)
 		return
 		return
-	case ^Foreign_Import_Decl:
-		visit_decl(p, cast(^Decl)stmt, true)
+	case ^ast.Foreign_Import_Decl:
+		visit_decl(p, cast(^ast.Decl)stmt, true)
 		return
 		return
-	case ^Foreign_Block_Decl:
-		visit_decl(p, cast(^Decl)stmt, true)
+	case ^ast.Foreign_Block_Decl:
+		visit_decl(p, cast(^ast.Decl)stmt, true)
 		return
 		return
 
 
-	case ^Using_Stmt:
+	case ^ast.Using_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		push_generic_token(p, .Using, 1)
 		push_generic_token(p, .Using, 1)
@@ -553,7 +549,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if p.config.semicolons {
 		if p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 			push_generic_token(p, .Semicolon, 0)
 		}
 		}
-	case ^Block_Stmt:
+	case ^ast.Block_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		if v.pos.line == v.end.line {
 		if v.pos.line == v.end.line {
@@ -583,7 +579,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 				visit_end_brace(p, v.end)
 				visit_end_brace(p, v.end)
 			}
 			}
 		}
 		}
-	case ^If_Stmt:
+	case ^ast.If_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		if v.label != nil {
 		if v.label != nil {
@@ -606,7 +602,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 
 		uses_do := false
 		uses_do := false
 
 
-		if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do {
+		if check_stmt, ok := v.body.derived.(^ast.Block_Stmt); ok && check_stmt.uses_do {
 			uses_do = true
 			uses_do = true
 		}
 		}
 
 
@@ -637,7 +633,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 
 			visit_stmt(p, v.else_stmt)
 			visit_stmt(p, v.else_stmt)
 		}
 		}
-	case ^Switch_Stmt:
+	case ^ast.Switch_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		if v.label != nil {
 		if v.label != nil {
@@ -665,7 +661,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 
 		visit_expr(p, v.cond)
 		visit_expr(p, v.cond)
 		visit_stmt(p, v.body)
 		visit_stmt(p, v.body)
-	case ^Case_Clause:
+	case ^ast.Case_Clause:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		if !p.config.indent_cases {
 		if !p.config.indent_cases {
@@ -689,7 +685,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if !p.config.indent_cases {
 		if !p.config.indent_cases {
 			indent(p)
 			indent(p)
 		}
 		}
-	case ^Type_Switch_Stmt:
+	case ^ast.Type_Switch_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		hint_current_line(p, {.Switch_Stmt})
 		hint_current_line(p, {.Switch_Stmt})
@@ -707,7 +703,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 
 		visit_stmt(p, v.tag)
 		visit_stmt(p, v.tag)
 		visit_stmt(p, v.body)
 		visit_stmt(p, v.body)
-	case ^Assign_Stmt:
+	case ^ast.Assign_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		hint_current_line(p, {.Assign})
 		hint_current_line(p, {.Assign})
@@ -721,13 +717,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if block_stmt && p.config.semicolons {
 		if block_stmt && p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 			push_generic_token(p, .Semicolon, 0)
 		}
 		}
-	case ^Expr_Stmt:
+	case ^ast.Expr_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		if block_stmt && p.config.semicolons {
 		if block_stmt && p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 			push_generic_token(p, .Semicolon, 0)
 		}
 		}
-	case ^For_Stmt:
+	case ^ast.For_Stmt:
 		// this should be simplified
 		// this should be simplified
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
@@ -764,7 +760,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 
 		visit_stmt(p, v.body)
 		visit_stmt(p, v.body)
 
 
-	case ^Inline_Range_Stmt:
+	case ^ast.Inline_Range_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		if v.label != nil {
 		if v.label != nil {
@@ -790,7 +786,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		visit_stmt(p, v.body)
 		visit_stmt(p, v.body)
 
 
-	case ^Range_Stmt:
+	case ^ast.Range_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		if v.label != nil {
 		if v.label != nil {
@@ -816,7 +812,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 
 
 		visit_stmt(p, v.body)
 		visit_stmt(p, v.body)
-	case ^Return_Stmt:
+	case ^ast.Return_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		push_generic_token(p, .Return, 1)
 		push_generic_token(p, .Return, 1)
@@ -828,7 +824,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if block_stmt && p.config.semicolons {
 		if block_stmt && p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 			push_generic_token(p, .Semicolon, 0)
 		}
 		}
-	case ^Defer_Stmt:
+	case ^ast.Defer_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 		push_generic_token(p, .Defer, 0)
 		push_generic_token(p, .Defer, 0)
 
 
@@ -837,7 +833,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if p.config.semicolons {
 		if p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 			push_generic_token(p, .Semicolon, 0)
 		}
 		}
-	case ^When_Stmt:
+	case ^ast.When_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 		push_generic_token(p, .When, 1)
 		push_generic_token(p, .When, 1)
 		visit_expr(p, v.cond)
 		visit_expr(p, v.cond)
@@ -857,7 +853,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 			visit_stmt(p, v.else_stmt)
 			visit_stmt(p, v.else_stmt)
 		}
 		}
 
 
-	case ^Branch_Stmt:
+	case ^ast.Branch_Stmt:
 		move_line(p, v.pos)
 		move_line(p, v.pos)
 
 
 		push_generic_token(p, v.tok.kind, 0)
 		push_generic_token(p, v.tok.kind, 0)
@@ -921,8 +917,6 @@ push_poly_params :: proc(p: ^Printer, poly_params: ^ast.Field_List) {
 
 
 @(private)
 @(private)
 visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
-	using ast
-
 	if expr == nil {
 	if expr == nil {
 		return
 		return
 	}
 	}
@@ -930,14 +924,14 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 	set_source_position(p, expr.pos)
 	set_source_position(p, expr.pos)
 
 
 	switch v in expr.derived_expr {
 	switch v in expr.derived_expr {
-	case ^Bad_Expr:
+	case ^ast.Bad_Expr:
 
 
-	case ^Tag_Expr:
+	case ^ast.Tag_Expr:
 		push_generic_token(p, .Hash, 1)
 		push_generic_token(p, .Hash, 1)
 		push_generic_token(p, v.op.kind, 1, v.op.text)
 		push_generic_token(p, v.op.kind, 1, v.op.text)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 
 
-	case ^Inline_Asm_Expr:
+	case ^ast.Inline_Asm_Expr:
 		push_generic_token(p, v.tok.kind, 1, v.tok.text)
 		push_generic_token(p, v.tok.kind, 1, v.tok.text)
 
 
 		push_generic_token(p, .Open_Paren, 1)
 		push_generic_token(p, .Open_Paren, 1)
@@ -954,42 +948,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		push_generic_token(p, .Comma, 0)
 		push_generic_token(p, .Comma, 0)
 		visit_expr(p, v.constraints_string)
 		visit_expr(p, v.constraints_string)
 		push_generic_token(p, .Close_Brace, 0)
 		push_generic_token(p, .Close_Brace, 0)
-	case ^Undef:
+	case ^ast.Undef:
 		push_generic_token(p, .Undef, 1)
 		push_generic_token(p, .Undef, 1)
-	case ^Auto_Cast:
+	case ^ast.Auto_Cast:
 		push_generic_token(p, v.op.kind, 1)
 		push_generic_token(p, v.op.kind, 1)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
-	case ^Ternary_If_Expr:
+	case ^ast.Ternary_If_Expr:
 		visit_expr(p, v.x)
 		visit_expr(p, v.x)
 		push_generic_token(p, v.op1.kind, 1)
 		push_generic_token(p, v.op1.kind, 1)
 		visit_expr(p, v.cond)
 		visit_expr(p, v.cond)
 		push_generic_token(p, v.op2.kind, 1)
 		push_generic_token(p, v.op2.kind, 1)
 		visit_expr(p, v.y)
 		visit_expr(p, v.y)
-	case ^Ternary_When_Expr:
+	case ^ast.Ternary_When_Expr:
 		visit_expr(p, v.x)
 		visit_expr(p, v.x)
 		push_generic_token(p, v.op1.kind, 1)
 		push_generic_token(p, v.op1.kind, 1)
 		visit_expr(p, v.cond)
 		visit_expr(p, v.cond)
 		push_generic_token(p, v.op2.kind, 1)
 		push_generic_token(p, v.op2.kind, 1)
 		visit_expr(p, v.y)
 		visit_expr(p, v.y)
-	case ^Or_Else_Expr:
+	case ^ast.Or_Else_Expr:
 		visit_expr(p, v.x)
 		visit_expr(p, v.x)
 		push_generic_token(p, v.token.kind, 1)
 		push_generic_token(p, v.token.kind, 1)
 		visit_expr(p, v.y)
 		visit_expr(p, v.y)
-	case ^Or_Return_Expr:
+	case ^ast.Or_Return_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, v.token.kind, 1)
 		push_generic_token(p, v.token.kind, 1)
-	case ^Selector_Call_Expr:
+	case ^ast.Selector_Call_Expr:
 		visit_expr(p, v.call.expr)
 		visit_expr(p, v.call.expr)
 		push_generic_token(p, .Open_Paren, 1)
 		push_generic_token(p, .Open_Paren, 1)
 		visit_exprs(p, v.call.args, {.Add_Comma})
 		visit_exprs(p, v.call.args, {.Add_Comma})
 		push_generic_token(p, .Close_Paren, 0)
 		push_generic_token(p, .Close_Paren, 0)
-	case ^Ellipsis:
+	case ^ast.Ellipsis:
 		push_generic_token(p, .Ellipsis, 1)
 		push_generic_token(p, .Ellipsis, 1)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
-	case ^Relative_Type:
+	case ^ast.Relative_Type:
 		visit_expr(p, v.tag)
 		visit_expr(p, v.tag)
 		visit_expr(p, v.type)
 		visit_expr(p, v.type)
-	case ^Slice_Expr:
+	case ^ast.Slice_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Open_Bracket, 0)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.low)
 		visit_expr(p, v.low)
@@ -999,37 +993,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			visit_expr(p, v.high)
 			visit_expr(p, v.high)
 		}
 		}
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
-	case ^Ident:
+	case ^ast.Ident:
 		if .Enforce_Poly_Names in options {
 		if .Enforce_Poly_Names in options {
 			push_generic_token(p, .Dollar, 1)
 			push_generic_token(p, .Dollar, 1)
 			push_ident_token(p, v.name, 0)
 			push_ident_token(p, v.name, 0)
 		} else {
 		} else {
 			push_ident_token(p, v.name, 1)
 			push_ident_token(p, v.name, 1)
 		}
 		}
-	case ^Deref_Expr:
+	case ^ast.Deref_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, v.op.kind, 0)
 		push_generic_token(p, v.op.kind, 0)
-	case ^Type_Cast:
+	case ^ast.Type_Cast:
 		push_generic_token(p, v.tok.kind, 1)
 		push_generic_token(p, v.tok.kind, 1)
 		push_generic_token(p, .Open_Paren, 0)
 		push_generic_token(p, .Open_Paren, 0)
 		visit_expr(p, v.type)
 		visit_expr(p, v.type)
 		push_generic_token(p, .Close_Paren, 0)
 		push_generic_token(p, .Close_Paren, 0)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
-	case ^Basic_Directive:
+	case ^ast.Basic_Directive:
 		push_generic_token(p, v.tok.kind, 1)
 		push_generic_token(p, v.tok.kind, 1)
 		push_ident_token(p, v.name, 0)
 		push_ident_token(p, v.name, 0)
-	case ^Distinct_Type:
+	case ^ast.Distinct_Type:
 		push_generic_token(p, .Distinct, 1)
 		push_generic_token(p, .Distinct, 1)
 		visit_expr(p, v.type)
 		visit_expr(p, v.type)
-	case ^Dynamic_Array_Type:
+	case ^ast.Dynamic_Array_Type:
 		visit_expr(p, v.tag)
 		visit_expr(p, v.tag)
 		push_generic_token(p, .Open_Bracket, 1)
 		push_generic_token(p, .Open_Bracket, 1)
 		push_generic_token(p, .Dynamic, 0)
 		push_generic_token(p, .Dynamic, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.elem)
 		visit_expr(p, v.elem)
-	case ^Bit_Set_Type:
+	case ^ast.Bit_Set_Type:
 		push_generic_token(p, .Bit_Set, 1)
 		push_generic_token(p, .Bit_Set, 1)
 		push_generic_token(p, .Open_Bracket, 0)
 		push_generic_token(p, .Open_Bracket, 0)
 
 
@@ -1041,7 +1035,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		}
 		}
 
 
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
-	case ^Union_Type:
+	case ^ast.Union_Type:
 		push_generic_token(p, .Union, 1)
 		push_generic_token(p, .Union, 1)
 
 
 		push_poly_params(p, v.poly_params)
 		push_poly_params(p, v.poly_params)
@@ -1066,7 +1060,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
 			visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
 			visit_end_brace(p, v.end)
 			visit_end_brace(p, v.end)
 		}
 		}
-	case ^Enum_Type:
+	case ^ast.Enum_Type:
 		push_generic_token(p, .Enum, 1)
 		push_generic_token(p, .Enum, 1)
 
 
 		hint_current_line(p, {.Enum})
 		hint_current_line(p, {.Enum})
@@ -1089,7 +1083,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		}
 		}
 
 
 		set_source_position(p, v.end)
 		set_source_position(p, v.end)
-	case ^Struct_Type:
+	case ^ast.Struct_Type:
 		push_generic_token(p, .Struct, 1)
 		push_generic_token(p, .Struct, 1)
 
 
 		hint_current_line(p, {.Struct})
 		hint_current_line(p, {.Struct})
@@ -1124,7 +1118,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		}
 		}
 
 
 		set_source_position(p, v.end)
 		set_source_position(p, v.end)
-	case ^Proc_Lit:
+	case ^ast.Proc_Lit:
 		switch v.inlining {
 		switch v.inlining {
 		case .None:
 		case .None:
 		case .Inline:
 		case .Inline:
@@ -1143,16 +1137,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		} else {
 		} else {
 			push_generic_token(p, .Undef, 1)
 			push_generic_token(p, .Undef, 1)
 		}
 		}
-	case ^Proc_Type:
+	case ^ast.Proc_Type:
 		visit_proc_type(p, v)
 		visit_proc_type(p, v)
-	case ^Basic_Lit:
+	case ^ast.Basic_Lit:
 		push_generic_token(p, v.tok.kind, 1, v.tok.text)
 		push_generic_token(p, v.tok.kind, 1, v.tok.text)
-	case ^Binary_Expr:
+	case ^ast.Binary_Expr:
 		visit_binary_expr(p, v)
 		visit_binary_expr(p, v)
-	case ^Implicit_Selector_Expr:
+	case ^ast.Implicit_Selector_Expr:
 		push_generic_token(p, .Period, 1)
 		push_generic_token(p, .Period, 1)
 		push_ident_token(p, v.field.name, 0)
 		push_ident_token(p, v.field.name, 0)
-	case ^Call_Expr:
+	case ^ast.Call_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 
 
 		push_format_token(p,
 		push_format_token(p,
@@ -1167,34 +1161,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 
 
 		visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
 		visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
 		push_generic_token(p, .Close_Paren, 0)
 		push_generic_token(p, .Close_Paren, 0)
-	case ^Typeid_Type:
+	case ^ast.Typeid_Type:
 		push_generic_token(p, .Typeid, 1)
 		push_generic_token(p, .Typeid, 1)
 
 
 		if v.specialization != nil {
 		if v.specialization != nil {
 			push_generic_token(p, .Quo, 0)
 			push_generic_token(p, .Quo, 0)
 			visit_expr(p, v.specialization)
 			visit_expr(p, v.specialization)
 		}
 		}
-	case ^Selector_Expr:
+	case ^ast.Selector_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, v.op.kind, 0)
 		push_generic_token(p, v.op.kind, 0)
 		visit_expr(p, v.field)
 		visit_expr(p, v.field)
-	case ^Paren_Expr:
+	case ^ast.Paren_Expr:
 		push_generic_token(p, .Open_Paren, 1)
 		push_generic_token(p, .Open_Paren, 1)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Close_Paren, 0)
 		push_generic_token(p, .Close_Paren, 0)
-	case ^Index_Expr:
+	case ^ast.Index_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Open_Bracket, 0)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.index)
 		visit_expr(p, v.index)
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
-	case ^Matrix_Index_Expr:
+	case ^ast.Matrix_Index_Expr:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Open_Bracket, 0)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.row_index)
 		visit_expr(p, v.row_index)
 		push_generic_token(p, .Comma, 0)
 		push_generic_token(p, .Comma, 0)
 		visit_expr(p, v.column_index)
 		visit_expr(p, v.column_index)
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
-	case ^Proc_Group:
+	case ^ast.Proc_Group:
 		push_generic_token(p, v.tok.kind, 1)
 		push_generic_token(p, v.tok.kind, 1)
 
 
 		if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
 		if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
@@ -1209,7 +1203,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_generic_token(p, .Close_Brace, 0)
 			push_generic_token(p, .Close_Brace, 0)
 		}
 		}
 
 
-	case ^Comp_Lit:
+	case ^ast.Comp_Lit:
 		if v.type != nil {
 		if v.type != nil {
 			visit_expr(p, v.type)
 			visit_expr(p, v.type)
 		}
 		}
@@ -1226,18 +1220,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_generic_token(p, .Close_Brace, 0)
 			push_generic_token(p, .Close_Brace, 0)
 		}
 		}
 
 
-	case ^Unary_Expr:
+	case ^ast.Unary_Expr:
 		push_generic_token(p, v.op.kind, 1)
 		push_generic_token(p, v.op.kind, 1)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
-	case ^Field_Value:
+	case ^ast.Field_Value:
 		visit_expr(p, v.field)
 		visit_expr(p, v.field)
 		push_generic_token(p, .Eq, 1)
 		push_generic_token(p, .Eq, 1)
 		visit_expr(p, v.value)
 		visit_expr(p, v.value)
-	case ^Type_Assertion:
+	case ^ast.Type_Assertion:
 		visit_expr(p, v.expr)
 		visit_expr(p, v.expr)
 
 
-		if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" {
+		if unary, ok := v.type.derived.(^ast.Unary_Expr); ok && unary.op.text == "?" {
 			push_generic_token(p, .Period, 0)
 			push_generic_token(p, .Period, 0)
 			visit_expr(p, v.type)
 			visit_expr(p, v.type)
 		} else {
 		} else {
@@ -1247,13 +1241,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_generic_token(p, .Close_Paren, 0)
 			push_generic_token(p, .Close_Paren, 0)
 		}
 		}
 
 
-	case ^Pointer_Type:
+	case ^ast.Pointer_Type:
 		push_generic_token(p, .Pointer, 1)
 		push_generic_token(p, .Pointer, 1)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.elem)
 		visit_expr(p, v.elem)
-	case ^Implicit:
+	case ^ast.Implicit:
 		push_generic_token(p, v.tok.kind, 1)
 		push_generic_token(p, v.tok.kind, 1)
-	case ^Poly_Type:
+	case ^ast.Poly_Type:
 		push_generic_token(p, .Dollar, 1)
 		push_generic_token(p, .Dollar, 1)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.type)
 		visit_expr(p, v.type)
@@ -1263,28 +1257,28 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			merge_next_token(p)
 			merge_next_token(p)
 			visit_expr(p, v.specialization)
 			visit_expr(p, v.specialization)
 		}
 		}
-	case ^Array_Type:
+	case ^ast.Array_Type:
 		visit_expr(p, v.tag)
 		visit_expr(p, v.tag)
 		push_generic_token(p, .Open_Bracket, 1)
 		push_generic_token(p, .Open_Bracket, 1)
 		visit_expr(p, v.len)
 		visit_expr(p, v.len)
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.elem)
 		visit_expr(p, v.elem)
-	case ^Map_Type:
+	case ^ast.Map_Type:
 		push_generic_token(p, .Map, 1)
 		push_generic_token(p, .Map, 1)
 		push_generic_token(p, .Open_Bracket, 0)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.key)
 		visit_expr(p, v.key)
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		merge_next_token(p)
 		merge_next_token(p)
 		visit_expr(p, v.value)
 		visit_expr(p, v.value)
-	case ^Helper_Type:
+	case ^ast.Helper_Type:
 		visit_expr(p, v.type)
 		visit_expr(p, v.type)
-	case ^Multi_Pointer_Type:
+	case ^ast.Multi_Pointer_Type:
 		push_generic_token(p, .Open_Bracket, 1)
 		push_generic_token(p, .Open_Bracket, 1)
 		push_generic_token(p, .Pointer, 0)
 		push_generic_token(p, .Pointer, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		visit_expr(p, v.elem)
 		visit_expr(p, v.elem)
-	case ^Matrix_Type:
+	case ^ast.Matrix_Type:
 		push_generic_token(p, .Matrix, 1)
 		push_generic_token(p, .Matrix, 1)
 		push_generic_token(p, .Open_Bracket, 0)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.row_count)
 		visit_expr(p, v.row_count)

+ 16 - 16
core/odin/tokenizer/tokenizer.odin

@@ -75,34 +75,34 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
 	t.error_count += 1
 	t.error_count += 1
 }
 }
 
 
-advance_rune :: proc(using t: ^Tokenizer) {
-	if read_offset < len(src) {
-		offset = read_offset
-		if ch == '\n' {
-			line_offset = offset
-			line_count += 1
+advance_rune :: proc(t: ^Tokenizer) {
+	if t.read_offset < len(t.src) {
+		t.offset = t.read_offset
+		if t.ch == '\n' {
+			t.line_offset = t.offset
+			t.line_count += 1
 		}
 		}
-		r, w := rune(src[read_offset]), 1
+		r, w := rune(t.src[t.read_offset]), 1
 		switch {
 		switch {
 		case r == 0:
 		case r == 0:
 			error(t, t.offset, "illegal character NUL")
 			error(t, t.offset, "illegal character NUL")
 		case r >= utf8.RUNE_SELF:
 		case r >= utf8.RUNE_SELF:
-			r, w = utf8.decode_rune_in_string(src[read_offset:])
+			r, w = utf8.decode_rune_in_string(t.src[t.read_offset:])
 			if r == utf8.RUNE_ERROR && w == 1 {
 			if r == utf8.RUNE_ERROR && w == 1 {
 				error(t, t.offset, "illegal UTF-8 encoding")
 				error(t, t.offset, "illegal UTF-8 encoding")
-			} else if r == utf8.RUNE_BOM && offset > 0 {
+			} else if r == utf8.RUNE_BOM && t.offset > 0 {
 				error(t, t.offset, "illegal byte order mark")
 				error(t, t.offset, "illegal byte order mark")
 			}
 			}
 		}
 		}
-		read_offset += w
-		ch = r
+		t.read_offset += w
+		t.ch = r
 	} else {
 	} else {
-		offset = len(src)
-		if ch == '\n' {
-			line_offset = offset
-			line_count += 1
+		t.offset = len(t.src)
+		if t.ch == '\n' {
+			t.line_offset = t.offset
+			t.line_count += 1
 		}
 		}
-		ch = -1
+		t.ch = -1
 	}
 	}
 }
 }
 
 

+ 59 - 60
core/reflect/reflect.odin

@@ -8,35 +8,35 @@ _ :: intrinsics
 
 
 Type_Info :: runtime.Type_Info
 Type_Info :: runtime.Type_Info
 
 
-Type_Info_Named            :: runtime.Type_Info_Named
-Type_Info_Integer          :: runtime.Type_Info_Integer
-Type_Info_Rune             :: runtime.Type_Info_Rune
-Type_Info_Float            :: runtime.Type_Info_Float
-Type_Info_Complex          :: runtime.Type_Info_Complex
-Type_Info_Quaternion       :: runtime.Type_Info_Quaternion
-Type_Info_String           :: runtime.Type_Info_String
-Type_Info_Boolean          :: runtime.Type_Info_Boolean
-Type_Info_Any              :: runtime.Type_Info_Any
-Type_Info_Type_Id          :: runtime.Type_Info_Type_Id
-Type_Info_Pointer          :: runtime.Type_Info_Pointer
-Type_Info_Multi_Pointer    :: runtime.Type_Info_Multi_Pointer
-Type_Info_Procedure        :: runtime.Type_Info_Procedure
-Type_Info_Array            :: runtime.Type_Info_Array
-Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
-Type_Info_Dynamic_Array    :: runtime.Type_Info_Dynamic_Array
-Type_Info_Slice            :: runtime.Type_Info_Slice
-Type_Info_Parameters       :: runtime.Type_Info_Parameters
-Type_Info_Tuple            :: runtime.Type_Info_Parameters
-Type_Info_Struct           :: runtime.Type_Info_Struct
-Type_Info_Union            :: runtime.Type_Info_Union
-Type_Info_Enum             :: runtime.Type_Info_Enum
-Type_Info_Map              :: runtime.Type_Info_Map
-Type_Info_Bit_Set          :: runtime.Type_Info_Bit_Set
-Type_Info_Simd_Vector      :: runtime.Type_Info_Simd_Vector
-Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
-Type_Info_Relative_Slice   :: runtime.Type_Info_Relative_Slice
-Type_Info_Matrix           :: runtime.Type_Info_Matrix
-Type_Info_Soa_Pointer      :: runtime.Type_Info_Soa_Pointer
+Type_Info_Named                  :: runtime.Type_Info_Named
+Type_Info_Integer                :: runtime.Type_Info_Integer
+Type_Info_Rune                   :: runtime.Type_Info_Rune
+Type_Info_Float                  :: runtime.Type_Info_Float
+Type_Info_Complex                :: runtime.Type_Info_Complex
+Type_Info_Quaternion             :: runtime.Type_Info_Quaternion
+Type_Info_String                 :: runtime.Type_Info_String
+Type_Info_Boolean                :: runtime.Type_Info_Boolean
+Type_Info_Any                    :: runtime.Type_Info_Any
+Type_Info_Type_Id                :: runtime.Type_Info_Type_Id
+Type_Info_Pointer                :: runtime.Type_Info_Pointer
+Type_Info_Multi_Pointer          :: runtime.Type_Info_Multi_Pointer
+Type_Info_Procedure              :: runtime.Type_Info_Procedure
+Type_Info_Array                  :: runtime.Type_Info_Array
+Type_Info_Enumerated_Array       :: runtime.Type_Info_Enumerated_Array
+Type_Info_Dynamic_Array          :: runtime.Type_Info_Dynamic_Array
+Type_Info_Slice                  :: runtime.Type_Info_Slice
+Type_Info_Parameters             :: runtime.Type_Info_Parameters
+Type_Info_Tuple                  :: runtime.Type_Info_Parameters
+Type_Info_Struct                 :: runtime.Type_Info_Struct
+Type_Info_Union                  :: runtime.Type_Info_Union
+Type_Info_Enum                   :: runtime.Type_Info_Enum
+Type_Info_Map                    :: runtime.Type_Info_Map
+Type_Info_Bit_Set                :: runtime.Type_Info_Bit_Set
+Type_Info_Simd_Vector            :: runtime.Type_Info_Simd_Vector
+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_Enum_Value :: runtime.Type_Info_Enum_Value
 Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
 
 
@@ -69,7 +69,7 @@ Type_Kind :: enum {
 	Bit_Set,
 	Bit_Set,
 	Simd_Vector,
 	Simd_Vector,
 	Relative_Pointer,
 	Relative_Pointer,
-	Relative_Slice,
+	Relative_Multi_Pointer,
 	Matrix,
 	Matrix,
 	Soa_Pointer,
 	Soa_Pointer,
 }
 }
@@ -80,34 +80,34 @@ type_kind :: proc(T: typeid) -> Type_Kind {
 	ti := type_info_of(T)
 	ti := type_info_of(T)
 	if ti != nil {
 	if ti != nil {
 		switch _ in ti.variant {
 		switch _ in ti.variant {
-		case Type_Info_Named:            return .Named
-		case Type_Info_Integer:          return .Integer
-		case Type_Info_Rune:             return .Rune
-		case Type_Info_Float:            return .Float
-		case Type_Info_Complex:          return .Complex
-		case Type_Info_Quaternion:       return .Quaternion
-		case Type_Info_String:           return .String
-		case Type_Info_Boolean:          return .Boolean
-		case Type_Info_Any:              return .Any
-		case Type_Info_Type_Id:          return .Type_Id
-		case Type_Info_Pointer:          return .Pointer
-		case Type_Info_Multi_Pointer:    return .Multi_Pointer
-		case Type_Info_Procedure:        return .Procedure
-		case Type_Info_Array:            return .Array
-		case Type_Info_Enumerated_Array: return .Enumerated_Array
-		case Type_Info_Dynamic_Array:    return .Dynamic_Array
-		case Type_Info_Slice:            return .Slice
-		case Type_Info_Parameters:       return .Tuple
-		case Type_Info_Struct:           return .Struct
-		case Type_Info_Union:            return .Union
-		case Type_Info_Enum:             return .Enum
-		case Type_Info_Map:              return .Map
-		case Type_Info_Bit_Set:          return .Bit_Set
-		case Type_Info_Simd_Vector:      return .Simd_Vector
-		case Type_Info_Relative_Pointer: return .Relative_Pointer
-		case Type_Info_Relative_Slice:   return .Relative_Slice
-		case Type_Info_Matrix:           return .Matrix
-		case Type_Info_Soa_Pointer:      return .Soa_Pointer
+		case Type_Info_Named:                  return .Named
+		case Type_Info_Integer:                return .Integer
+		case Type_Info_Rune:                   return .Rune
+		case Type_Info_Float:                  return .Float
+		case Type_Info_Complex:                return .Complex
+		case Type_Info_Quaternion:             return .Quaternion
+		case Type_Info_String:                 return .String
+		case Type_Info_Boolean:                return .Boolean
+		case Type_Info_Any:                    return .Any
+		case Type_Info_Type_Id:                return .Type_Id
+		case Type_Info_Pointer:                return .Pointer
+		case Type_Info_Multi_Pointer:          return .Multi_Pointer
+		case Type_Info_Procedure:              return .Procedure
+		case Type_Info_Array:                  return .Array
+		case Type_Info_Enumerated_Array:       return .Enumerated_Array
+		case Type_Info_Dynamic_Array:          return .Dynamic_Array
+		case Type_Info_Slice:                  return .Slice
+		case Type_Info_Parameters:             return .Tuple
+		case Type_Info_Struct:                 return .Struct
+		case Type_Info_Union:                  return .Union
+		case Type_Info_Enum:                   return .Enum
+		case Type_Info_Map:                    return .Map
+		case Type_Info_Bit_Set:                return .Bit_Set
+		case Type_Info_Simd_Vector:            return .Simd_Vector
+		case Type_Info_Relative_Pointer:       return .Relative_Pointer
+		case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
+		case Type_Info_Matrix:                 return .Matrix
+		case Type_Info_Soa_Pointer:            return .Soa_Pointer
 		}
 		}
 
 
 	}
 	}
@@ -1457,8 +1457,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		return equal(va, vb, including_indirect_array_recursion, recursion_level+1) 
 		return equal(va, vb, including_indirect_array_recursion, recursion_level+1) 
 	case Type_Info_Map:
 	case Type_Info_Map:
 		return false
 		return false
-	case Type_Info_Relative_Slice:
-		return false
 	case 
 	case 
 		Type_Info_Boolean,
 		Type_Info_Boolean,
 		Type_Info_Integer, 
 		Type_Info_Integer, 
@@ -1474,6 +1472,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		Type_Info_Enum,
 		Type_Info_Enum,
 		Type_Info_Simd_Vector,
 		Type_Info_Simd_Vector,
 		Type_Info_Relative_Pointer,
 		Type_Info_Relative_Pointer,
+		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Matrix:
 		Type_Info_Matrix:
 		return runtime.memory_compare(a.data, b.data, t.size) == 0
 		return runtime.memory_compare(a.data, b.data, t.size) == 0

+ 7 - 8
core/reflect/types.odin

@@ -165,9 +165,9 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
 		y := b.variant.(Type_Info_Relative_Pointer) or_return
 		y := b.variant.(Type_Info_Relative_Pointer) or_return
 		return x.base_integer == y.base_integer && x.pointer == y.pointer
 		return x.base_integer == y.base_integer && x.pointer == y.pointer
 
 
-	case Type_Info_Relative_Slice:
-		y := b.variant.(Type_Info_Relative_Slice) or_return
-		return x.base_integer == y.base_integer && x.slice == y.slice
+	case Type_Info_Relative_Multi_Pointer:
+		y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
+		return x.base_integer == y.base_integer && x.pointer == y.pointer
 		
 		
 	case Type_Info_Matrix:
 	case Type_Info_Matrix:
 		y := b.variant.(Type_Info_Matrix) or_return
 		y := b.variant.(Type_Info_Matrix) or_return
@@ -383,9 +383,9 @@ is_relative_pointer :: proc(info: ^Type_Info) -> bool {
 	return ok
 	return ok
 }
 }
 @(require_results)
 @(require_results)
-is_relative_slice :: proc(info: ^Type_Info) -> bool {
+is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	if info == nil { return false }
-	_, ok := type_info_base(info).variant.(Type_Info_Relative_Slice)
+	_, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer)
 	return ok
 	return ok
 }
 }
 
 
@@ -395,7 +395,6 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
 
 
 
 
 
 
-
 write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
 write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
 	return write_type_writer(strings.to_writer(buf), type_info_of(id))
 	return write_type_writer(strings.to_writer(buf), type_info_of(id))
 }
 }
@@ -652,11 +651,11 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
 		io.write_string(w, ") ",         &n) or_return
 		io.write_string(w, ") ",         &n) or_return
 		write_type(w, info.pointer,      &n) or_return
 		write_type(w, info.pointer,      &n) or_return
 
 
-	case Type_Info_Relative_Slice:
+	case Type_Info_Relative_Multi_Pointer:
 		io.write_string(w, "#relative(", &n) or_return
 		io.write_string(w, "#relative(", &n) or_return
 		write_type(w, info.base_integer, &n) or_return
 		write_type(w, info.base_integer, &n) or_return
 		io.write_string(w, ") ",         &n) or_return
 		io.write_string(w, ") ",         &n) or_return
-		write_type(w, info.slice,        &n) or_return
+		write_type(w, info.pointer,      &n) or_return
 		
 		
 	case Type_Info_Matrix:
 	case Type_Info_Matrix:
 		io.write_string(w, "matrix[",               &n) or_return
 		io.write_string(w, "matrix[",               &n) or_return

+ 5 - 5
core/runtime/core.odin

@@ -162,11 +162,11 @@ Type_Info_Simd_Vector :: struct {
 	count:      int,
 	count:      int,
 }
 }
 Type_Info_Relative_Pointer :: struct {
 Type_Info_Relative_Pointer :: struct {
-	pointer:      ^Type_Info,
+	pointer:      ^Type_Info, // ^T
 	base_integer: ^Type_Info,
 	base_integer: ^Type_Info,
 }
 }
-Type_Info_Relative_Slice :: struct {
-	slice:        ^Type_Info,
+Type_Info_Relative_Multi_Pointer :: struct {
+	pointer:      ^Type_Info, // [^]T
 	base_integer: ^Type_Info,
 	base_integer: ^Type_Info,
 }
 }
 Type_Info_Matrix :: struct {
 Type_Info_Matrix :: struct {
@@ -219,7 +219,7 @@ Type_Info :: struct {
 		Type_Info_Bit_Set,
 		Type_Info_Bit_Set,
 		Type_Info_Simd_Vector,
 		Type_Info_Simd_Vector,
 		Type_Info_Relative_Pointer,
 		Type_Info_Relative_Pointer,
-		Type_Info_Relative_Slice,
+		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Matrix,
 		Type_Info_Matrix,
 		Type_Info_Soa_Pointer,
 		Type_Info_Soa_Pointer,
 	},
 	},
@@ -252,7 +252,7 @@ Typeid_Kind :: enum u8 {
 	Bit_Set,
 	Bit_Set,
 	Simd_Vector,
 	Simd_Vector,
 	Relative_Pointer,
 	Relative_Pointer,
-	Relative_Slice,
+	Relative_Multi_Pointer,
 	Matrix,
 	Matrix,
 }
 }
 #assert(len(Typeid_Kind) < 32)
 #assert(len(Typeid_Kind) < 32)

+ 5 - 2
core/runtime/default_allocators_general.odin

@@ -4,13 +4,16 @@
 //+build !js
 //+build !js
 package runtime
 package runtime
 
 
+// TODO(bill): reimplement these procedures in the os_specific stuff
+import "core:os"
+
 when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
 when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+	_ :: os
+
 	// mem.nil_allocator reimplementation
 	// mem.nil_allocator reimplementation
 	default_allocator_proc :: nil_allocator_proc
 	default_allocator_proc :: nil_allocator_proc
 	default_allocator :: nil_allocator
 	default_allocator :: nil_allocator
 } else {
 } else {
-	// TODO(bill): reimplement these procedures in the os_specific stuff
-	import "core:os"
 
 
 	default_allocator_proc :: os.heap_allocator_proc
 	default_allocator_proc :: os.heap_allocator_proc
 
 

+ 118 - 98
core/runtime/dynamic_map_internal.odin

@@ -414,68 +414,21 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
 	tk := map_cell_index_dynamic(sk, info.ks, 1)
 	tk := map_cell_index_dynamic(sk, info.ks, 1)
 	tv := map_cell_index_dynamic(sv, info.vs, 1)
 	tv := map_cell_index_dynamic(sv, info.vs, 1)
 
 
-	for {
-		hp := &hs[pos]
-		element_hash := hp^
+	swap_loop: for {
+		element_hash := hs[pos]
 
 
 		if map_hash_is_empty(element_hash) {
 		if map_hash_is_empty(element_hash) {
-			kp := map_cell_index_dynamic(ks, info.ks, pos)
-			vp := map_cell_index_dynamic(vs, info.vs, pos)
-			intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
-			intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
-			hp^ = h
+			k_dst := map_cell_index_dynamic(ks, info.ks, pos)
+			v_dst := map_cell_index_dynamic(vs, info.vs, pos)
+			intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+			hs[pos] = h
 
 
-			return result if result != 0 else vp
+			return result if result != 0 else v_dst
 		}
 		}
 
 
 		if map_hash_is_deleted(element_hash) {
 		if map_hash_is_deleted(element_hash) {
-			next_pos := (pos + 1) & mask
-
-			// backward shift
-			for !map_hash_is_empty(hs[next_pos]) {
-				probe_distance := map_probe_distance(m^, hs[next_pos], next_pos)
-				if probe_distance == 0 {
-					break
-				}
-				probe_distance -= 1
-
-				kp := map_cell_index_dynamic(ks, info.ks, pos)
-				vp := map_cell_index_dynamic(vs, info.vs, pos)
-				kn := map_cell_index_dynamic(ks, info.ks, next_pos)
-				vn := map_cell_index_dynamic(vs, info.vs, next_pos)
-
-				if distance > probe_distance {
-					if result == 0 {
-						result = vp
-					}
-					// move stored into pos; store next
-					intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
-					intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
-					hs[pos] = h
-
-					intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kn), size_of_k)
-					intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(vn), size_of_v)
-					h = hs[next_pos]
-				} else {
-					// move next back 1
-					intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(kn), size_of_k)
-					intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(vn), size_of_v)
-					hs[pos] = hs[next_pos]
-					distance = probe_distance
-				}
-				hs[next_pos] = 0
-				pos = (pos + 1) & mask
-				next_pos = (next_pos + 1) & mask
-				distance += 1
-			}
-
-			kp := map_cell_index_dynamic(ks, info.ks, pos)
-			vp := map_cell_index_dynamic(vs, info.vs, pos)
-			intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
-			intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
-			hs[pos] = h
-
-			return result if result != 0 else vp
+			break swap_loop
 		}
 		}
 
 
 		if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
 		if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
@@ -495,8 +448,8 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
 			intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v)
 			intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v)
 
 
 			th := h
 			th := h
-			h = hp^
-			hp^ = th
+			h = hs[pos]
+			hs[pos] = th
 
 
 			distance = probe_distance
 			distance = probe_distance
 		}
 		}
@@ -504,6 +457,103 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
 		pos = (pos + 1) & mask
 		pos = (pos + 1) & mask
 		distance += 1
 		distance += 1
 	}
 	}
+
+	// backward shift loop
+	hs[pos] = 0
+	look_ahead: uintptr = 1
+	for {
+		la_pos := (pos + look_ahead) & mask
+		element_hash := hs[la_pos]
+
+		if map_hash_is_deleted(element_hash) {
+			look_ahead += 1
+			hs[la_pos] = 0
+			continue
+		}
+
+		k_dst := map_cell_index_dynamic(ks, info.ks, pos)
+		v_dst := map_cell_index_dynamic(vs, info.vs, pos)
+
+		if map_hash_is_empty(element_hash) {
+			intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+			hs[pos] = h
+
+			return result if result != 0 else v_dst
+		}
+
+		k_src := map_cell_index_dynamic(ks, info.ks, la_pos)
+		v_src := map_cell_index_dynamic(vs, info.vs, la_pos)
+		probe_distance := map_probe_distance(m^, element_hash, la_pos)
+
+		if probe_distance < look_ahead {
+			// probed can be made ideal while placing saved (ending condition)
+			if result == 0 {
+				result = v_dst
+			}
+			intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+			hs[pos] = h
+
+			// This will be an ideal move
+			pos = (la_pos - probe_distance) & mask
+			look_ahead -= probe_distance
+
+			// shift until we hit ideal/empty
+			for probe_distance != 0 {
+				k_dst = map_cell_index_dynamic(ks, info.ks, pos)
+				v_dst = map_cell_index_dynamic(vs, info.vs, pos)
+
+				intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k)
+				intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v)
+				hs[pos] = element_hash
+				hs[la_pos] = 0
+
+				pos = (pos + 1) & mask
+				la_pos = (la_pos + 1) & mask
+				look_ahead = (la_pos - pos) & mask
+				element_hash = hs[la_pos]
+				if map_hash_is_empty(element_hash) {
+					return
+				}
+
+				probe_distance = map_probe_distance(m^, element_hash, la_pos)
+				if probe_distance == 0 {
+					return
+				}
+				// can be ideal?
+				if probe_distance < look_ahead {
+					pos = (la_pos - probe_distance) & mask
+				}
+				k_src = map_cell_index_dynamic(ks, info.ks, la_pos)
+				v_src = map_cell_index_dynamic(vs, info.vs, la_pos)
+			}
+			return
+		} else if distance < probe_distance - look_ahead {
+			// shift back probed
+			intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v)
+			hs[pos] = element_hash
+			hs[la_pos] = 0
+		} else {
+			// place saved, save probed
+			if result == 0 {
+				result = v_dst
+			}
+			intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+			hs[pos] = h
+
+			intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(k_src), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(v_src), size_of_v)
+			h = hs[la_pos]
+			hs[la_pos] = 0
+			distance = probe_distance - look_ahead
+		}
+
+		pos = (pos + 1) & mask
+		distance += 1
+	}
 }
 }
 
 
 @(require_results)
 @(require_results)
@@ -696,49 +746,19 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
 	m.len -= 1
 	m.len -= 1
 	ok = true
 	ok = true
 
 
-	{ // coalesce tombstones
-		// HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots
-		mask := (uintptr(1)<<map_log2_cap(m^)) - 1
-		curr_index := uintptr(index)
-
-		// TODO(bill): determine a good value for this empirically
-		// if we do not implement backward shift deletion
-		PROBE_COUNT :: 8
-		for _ in 0..<PROBE_COUNT {
-			next_index := (curr_index + 1) & mask
-			if next_index == index {
-				// looped around
-				break
-			}
-
-			// if the next element is empty or has zero probe distance, then any lookup
-			// will always fail on the next, so we can clear both of them
-			hash := hs[next_index]
-			if map_hash_is_empty(hash) || map_probe_distance(m^, hash, next_index) == 0 {
-				hs[curr_index] = 0
-				return
-			}
-
-			// now the next element will have a probe count of at least one,
-			// so it can use the delete slot instead
-			hs[curr_index] = hs[next_index]
-
-			mem_copy_non_overlapping(
-				rawptr(map_cell_index_dynamic(ks, info.ks, curr_index)),
-				rawptr(map_cell_index_dynamic(ks, info.ks, next_index)),
-				int(info.ks.size_of_type),
-			)
-			mem_copy_non_overlapping(
-				rawptr(map_cell_index_dynamic(vs, info.vs, curr_index)),
-				rawptr(map_cell_index_dynamic(vs, info.vs, next_index)),
-				int(info.vs.size_of_type),
-			)
-
-			curr_index = next_index
-		}
+	mask := (uintptr(1)<<map_log2_cap(m^)) - 1
+	curr_index := uintptr(index)
+	next_index := (curr_index + 1) & mask
 
 
+	// if the next element is empty or has zero probe distance, then any lookup
+	// will always fail on the next, so we can clear both of them
+	hash := hs[next_index]
+	if map_hash_is_empty(hash) || map_probe_distance(m^, hash, next_index) == 0 {
+		hs[curr_index] = 0
+	} else {
 		hs[curr_index] |= TOMBSTONE_MASK
 		hs[curr_index] |= TOMBSTONE_MASK
 	}
 	}
+
 	return
 	return
 }
 }
 
 

+ 9 - 9
core/runtime/error_checks.odin

@@ -235,7 +235,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
 	handle_error(loc, len)
 	handle_error(loc, len)
 }
 }
 
 
-make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #caller_location, len, cap: int) {
+make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len, cap: int) {
 	if 0 <= len && len <= cap {
 	if 0 <= len && len <= cap {
 		return
 		return
 	}
 	}
@@ -271,18 +271,18 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
 
 
 
 
 
 
-bounds_check_error_loc :: #force_inline proc "contextless" (using loc := #caller_location, index, count: int) {
-	bounds_check_error(file_path, line, column, index, count)
+bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
+	bounds_check_error(loc.file_path, loc.line, loc.column, index, count)
 }
 }
 
 
-slice_expr_error_hi_loc :: #force_inline proc "contextless" (using loc := #caller_location, hi: int, len: int) {
-	slice_expr_error_hi(file_path, line, column, hi, len)
+slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
+	slice_expr_error_hi(loc.file_path, loc.line, loc.column, hi, len)
 }
 }
 
 
-slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (using loc := #caller_location, lo, hi: int, len: int) {
-	slice_expr_error_lo_hi(file_path, line, column, lo, hi, len)
+slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
+	slice_expr_error_lo_hi(loc.file_path, loc.line, loc.column, lo, hi, len)
 }
 }
 
 
-dynamic_array_expr_error_loc :: #force_inline proc "contextless" (using loc := #caller_location, low, high, max: int) {
-	dynamic_array_expr_error(file_path, line, column, low, high, max)
+dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
+	dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
 }
 }

+ 11 - 2
core/runtime/internal.odin

@@ -11,7 +11,7 @@ RUNTIME_LINKAGE :: "strong" when (
 	ODIN_BUILD_MODE == .Dynamic ||
 	ODIN_BUILD_MODE == .Dynamic ||
 	!ODIN_NO_CRT) &&
 	!ODIN_NO_CRT) &&
 	!IS_WASM) else "internal"
 	!IS_WASM) else "internal"
-RUNTIME_REQUIRE :: true
+RUNTIME_REQUIRE :: !ODIN_TILDE
 
 
 
 
 @(private)
 @(private)
@@ -218,10 +218,18 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
 	case n == 0: return true
 	case n == 0: return true
 	case x == y: return true
 	case x == y: return true
 	}
 	}
-	
 	a, b := ([^]byte)(x), ([^]byte)(y)
 	a, b := ([^]byte)(x), ([^]byte)(y)
 	length := uint(n)
 	length := uint(n)
+
+	for i := uint(0); i < length; i += 1 {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+	return true
 	
 	
+/*
+
 	when size_of(uint) == 8 {
 	when size_of(uint) == 8 {
 		if word_length := length >> 3; word_length != 0 {
 		if word_length := length >> 3; word_length != 0 {
 			for _ in 0..<word_length {
 			for _ in 0..<word_length {
@@ -276,6 +284,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
 
 
 		return true
 		return true
 	}
 	}
+*/
 
 
 }
 }
 memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
 memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {

+ 6 - 9
core/runtime/os_specific_windows.odin

@@ -1,9 +1,6 @@
-//+private
 //+build windows
 //+build windows
 package runtime
 package runtime
 
 
-import "core:intrinsics"
-
 foreign import kernel32 "system:Kernel32.lib"
 foreign import kernel32 "system:Kernel32.lib"
 
 
 @(private="file")
 @(private="file")
@@ -102,12 +99,12 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
 
 
 	allocated_mem: rawptr
 	allocated_mem: rawptr
 	if old_ptr != nil {
 	if old_ptr != nil {
-		original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
+		original_old_ptr := ([^]rawptr)(old_ptr)[-1]
 		allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
 		allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
 	} else {
 	} else {
 		allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
 		allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
 	}
 	}
-	aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
+	aligned_mem := ([^]u8)(allocated_mem)[size_of(rawptr):]
 
 
 	ptr := uintptr(aligned_mem)
 	ptr := uintptr(aligned_mem)
 	aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
 	aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
@@ -116,10 +113,10 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
 		return nil, .Out_Of_Memory
 		return nil, .Out_Of_Memory
 	}
 	}
 
 
-	aligned_mem = rawptr(aligned_ptr)
-	intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
+	aligned_mem = ([^]byte)(aligned_ptr)
+	([^]rawptr)(aligned_mem)[-1] = allocated_mem
 
 
-	return byte_slice(aligned_mem, size), nil
+	return aligned_mem[:size], nil
 }
 }
 
 
 _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
 _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
@@ -129,7 +126,7 @@ _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory
 
 
 _windows_default_free :: proc "contextless" (ptr: rawptr) {
 _windows_default_free :: proc "contextless" (ptr: rawptr) {
 	if ptr != nil {
 	if ptr != nil {
-		heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^)
+		heap_free(([^]rawptr)(ptr)[-1])
 	}
 	}
 }
 }
 
 

+ 17 - 9
core/runtime/print.odin

@@ -8,6 +8,11 @@ _INTEGER_DIGITS_VAR := _INTEGER_DIGITS
 when !ODIN_NO_RTTI {
 when !ODIN_NO_RTTI {
 	print_any_single :: proc "contextless" (arg: any) {
 	print_any_single :: proc "contextless" (arg: any) {
 		x := arg
 		x := arg
+		if x.data == nil {
+			print_string("nil")
+			return
+		}
+
 		if loc, ok := x.(Source_Code_Location); ok {
 		if loc, ok := x.(Source_Code_Location); ok {
 			print_caller_location(loc)
 			print_caller_location(loc)
 			return
 			return
@@ -48,6 +53,7 @@ when !ODIN_NO_RTTI {
 		case int:     print_int(v)
 		case int:     print_int(v)
 		case uint:    print_uint(v)
 		case uint:    print_uint(v)
 		case uintptr: print_uintptr(v)
 		case uintptr: print_uintptr(v)
+		case rawptr:  print_uintptr(uintptr(v))
 
 
 		case bool: print_string("true" if v else "false")
 		case bool: print_string("true" if v else "false")
 		case b8:   print_string("true" if v else "false")
 		case b8:   print_string("true" if v else "false")
@@ -58,7 +64,7 @@ when !ODIN_NO_RTTI {
 		case:
 		case:
 			ti := type_info_of(x.id)
 			ti := type_info_of(x.id)
 			#partial switch v in ti.variant {
 			#partial switch v in ti.variant {
-			case Type_Info_Pointer:
+			case Type_Info_Pointer, Type_Info_Multi_Pointer:
 				print_uintptr((^uintptr)(x.data)^)
 				print_uintptr((^uintptr)(x.data)^)
 				return
 				return
 			}
 			}
@@ -67,7 +73,9 @@ when !ODIN_NO_RTTI {
 		}
 		}
 	}
 	}
 	println_any :: proc "contextless" (args: ..any) {
 	println_any :: proc "contextless" (args: ..any) {
+		context = default_context()
 		loop: for arg, i in args {
 		loop: for arg, i in args {
+			assert(arg.id != nil)
 			if i != 0 {
 			if i != 0 {
 				print_string(" ")
 				print_string(" ")
 			}
 			}
@@ -215,19 +223,19 @@ print_uint    :: proc "contextless" (x: uint)    { print_u64(u64(x)) }
 print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
 print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
 print_int     :: proc "contextless" (x: int)     { print_i64(i64(x)) }
 print_int     :: proc "contextless" (x: int)     { print_i64(i64(x)) }
 
 
-print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
-	print_string(file_path)
+print_caller_location :: proc "contextless" (loc: Source_Code_Location) {
+	print_string(loc.file_path)
 	when ODIN_ERROR_POS_STYLE == .Default {
 	when ODIN_ERROR_POS_STYLE == .Default {
 		print_byte('(')
 		print_byte('(')
-		print_u64(u64(line))
+		print_u64(u64(loc.line))
 		print_byte(':')
 		print_byte(':')
-		print_u64(u64(column))
+		print_u64(u64(loc.column))
 		print_byte(')')
 		print_byte(')')
 	} else when ODIN_ERROR_POS_STYLE == .Unix {
 	} else when ODIN_ERROR_POS_STYLE == .Unix {
 		print_byte(':')
 		print_byte(':')
-		print_u64(u64(line))
+		print_u64(u64(loc.line))
 		print_byte(':')
 		print_byte(':')
-		print_u64(u64(column))
+		print_u64(u64(loc.column))
 		print_byte(':')
 		print_byte(':')
 	} else {
 	} else {
 		#panic("unhandled ODIN_ERROR_POS_STYLE")
 		#panic("unhandled ODIN_ERROR_POS_STYLE")
@@ -463,11 +471,11 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
 		print_string(") ")
 		print_string(") ")
 		print_type(info.pointer)
 		print_type(info.pointer)
 
 
-	case Type_Info_Relative_Slice:
+	case Type_Info_Relative_Multi_Pointer:
 		print_string("#relative(")
 		print_string("#relative(")
 		print_type(info.base_integer)
 		print_type(info.base_integer)
 		print_string(") ")
 		print_string(") ")
-		print_type(info.slice)
+		print_type(info.pointer)
 		
 		
 	case Type_Info_Matrix:
 	case Type_Info_Matrix:
 		print_string("matrix[")
 		print_string("matrix[")

+ 2 - 2
core/runtime/procs.odin

@@ -31,7 +31,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
 		if ptr != nil && len != 0 {
 		if ptr != nil && len != 0 {
 			b := byte(val)
 			b := byte(val)
 			p := ([^]byte)(ptr)
 			p := ([^]byte)(ptr)
-			for i in 0..<len {
+			for i := 0; i < len; i += 1 {
 				p[i] = b
 				p[i] = b
 			}
 			}
 		}
 		}
@@ -75,7 +75,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
 		if ptr != nil && len != 0 {
 		if ptr != nil && len != 0 {
 			b := byte(val)
 			b := byte(val)
 			p := ([^]byte)(ptr)
 			p := ([^]byte)(ptr)
-			for i in 0..<len {
+			for i := 0; i < len; i += 1 {
 				p[i] = b
 				p[i] = b
 			}
 			}
 		}
 		}

+ 6 - 0
core/sys/windows/types.odin

@@ -1709,6 +1709,12 @@ MONITORINFO :: struct {
 }
 }
 LPMONITORINFO :: ^MONITORINFO
 LPMONITORINFO :: ^MONITORINFO
 
 
+CCHDEVICENAME :: 32
+MONITORINFOEXW :: struct {
+	using _: MONITORINFO,
+	szDevice: [CCHDEVICENAME]WCHAR,
+}
+
 // SetWindowsHook() codes
 // SetWindowsHook() codes
 WH_MIN             :: -1
 WH_MIN             :: -1
 WH_MSGFILTER       :: -1
 WH_MSGFILTER       :: -1

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

@@ -228,6 +228,9 @@ foreign user32 {
 	SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
 	SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
 	CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
 	CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
 	GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
 	GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
+
+	GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU ---
+	EnableMenuItem :: proc(hMenu: HMENU, uIDEnableItem: UINT, uEnable: UINT) -> BOOL ---
 }
 }
 
 
 CreateWindowW :: #force_inline proc "stdcall" (
 CreateWindowW :: #force_inline proc "stdcall" (

+ 2 - 0
core/text/i18n/i18n.odin

@@ -71,6 +71,8 @@ Error :: enum {
 	TS_File_Expected_Source,
 	TS_File_Expected_Source,
 	TS_File_Expected_Translation,
 	TS_File_Expected_Translation,
 	TS_File_Expected_NumerusForm,
 	TS_File_Expected_NumerusForm,
+	Bad_Str,
+	Bad_Id,
 
 
 }
 }
 
 

+ 34 - 8
core/text/i18n/qt_linguist.odin

@@ -30,10 +30,26 @@ TS_XML_Options := xml.Options{
 parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
 parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
 	context.allocator = allocator
 	context.allocator = allocator
 
 
+	get_str :: proc(val: xml.Value) -> (str: string, err: Error) {
+		v, ok := val.(string)
+		if ok {
+			return v, .None
+		}
+		return "", .Bad_Str
+	}
+
+	get_id :: proc(val: xml.Value) -> (str: xml.Element_ID, err: Error) {
+		v, ok := val.(xml.Element_ID)
+		if ok {
+			return v, .None
+		}
+		return 0, .Bad_Id
+	}
+
 	ts, xml_err := xml.parse(data, TS_XML_Options)
 	ts, xml_err := xml.parse(data, TS_XML_Options)
 	defer xml.destroy(ts)
 	defer xml.destroy(ts)
 
 
-	if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].children) == 0 {
+	if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].value) == 0 {
 		return nil, .TS_File_Parse_Error
 		return nil, .TS_File_Parse_Error
 	}
 	}
 
 
@@ -46,10 +62,12 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
 
 
 	section: ^Section
 	section: ^Section
 
 
-	for child_id in ts.elements[0].children {
+	for value in ts.elements[0].value {
+		child_id := get_id(value) or_return
+
 		// These should be <context>s.
 		// These should be <context>s.
-		child := ts.elements[child_id]
-		if child.ident != "context" {
+
+		if ts.elements[child_id].ident != "context" {
 			return translation, .TS_File_Expected_Context
 			return translation, .TS_File_Expected_Context
 		}
 		}
 
 
@@ -61,7 +79,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
 
 
 		section_name, _ := strings.intern_get(&translation.intern, "")
 		section_name, _ := strings.intern_get(&translation.intern, "")
 		if !options.merge_sections {
 		if !options.merge_sections {
-			section_name, _ = strings.intern_get(&translation.intern, ts.elements[section_name_id].value)
+			value_text := get_str(ts.elements[section_name_id].value[0]) or_return
+			section_name, _ = strings.intern_get(&translation.intern, value_text)
 		}
 		}
 
 
 		if section_name not_in translation.k_v {
 		if section_name not_in translation.k_v {
@@ -92,8 +111,14 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
 				return translation, .TS_File_Expected_Translation
 				return translation, .TS_File_Expected_Translation
 			}
 			}
 
 
-			source, _ := strings.intern_get(&translation.intern, ts.elements[source_id].value)
-			xlat,   _ := strings.intern_get(&translation.intern, ts.elements[translation_id].value)
+			source    := get_str(ts.elements[source_id].value[0]) or_return
+			source, _  = strings.intern_get(&translation.intern, source)
+
+			xlat := ""
+			if !has_plurals {
+				xlat    = get_str(ts.elements[translation_id].value[0]) or_return
+				xlat, _ = strings.intern_get(&translation.intern, xlat)
+			}
 
 
 			if source in section {
 			if source in section {
 				return translation, .Duplicate_Key
 				return translation, .Duplicate_Key
@@ -124,7 +149,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
 					if !numerus_found {
 					if !numerus_found {
 						break
 						break
 					}
 					}
-					numerus, _ := strings.intern_get(&translation.intern, ts.elements[numerus_id].value)
+					numerus := get_str(ts.elements[numerus_id].value[0]) or_return
+					numerus, _ = strings.intern_get(&translation.intern, numerus)
 					section[source][num_plurals] = numerus
 					section[source][num_plurals] = numerus
 
 
 					num_plurals += 1
 					num_plurals += 1

+ 2 - 2
core/thread/thread_windows.odin

@@ -129,8 +129,8 @@ _destroy :: proc(thread: ^Thread) {
 	free(thread, thread.creation_allocator)
 	free(thread, thread.creation_allocator)
 }
 }
 
 
-_terminate :: proc(using thread : ^Thread, exit_code: int) {
-	win32.TerminateThread(win32_thread, u32(exit_code))
+_terminate :: proc(thread: ^Thread, exit_code: int) {
+	win32.TerminateThread(thread.win32_thread, u32(exit_code))
 }
 }
 
 
 _yield :: proc() {
 _yield :: proc() {

+ 19 - 17
core/time/time.odin

@@ -59,28 +59,30 @@ sleep :: proc "contextless" (d: Duration) {
 	_sleep(d)
 	_sleep(d)
 }
 }
 
 
-stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
-	if !running {
-		_start_time = tick_now()
-		running = true
+stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
+	if !stopwatch.running {
+		stopwatch._start_time = tick_now()
+		stopwatch.running = true
 	}
 	}
 }
 }
 
 
-stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
-	if running {
-		_accumulation += tick_diff(_start_time, tick_now())
-		running = false
+stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
+	if stopwatch.running {
+		stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
+		stopwatch.running = false
 	}
 	}
 }
 }
 
 
-stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
-	_accumulation = {}
-	running = false
+stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
+	stopwatch._accumulation = {}
+	stopwatch.running = false
 }
 }
 
 
-stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
-	if !running { return _accumulation }
-	return _accumulation + tick_diff(_start_time, tick_now())
+stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
+	if !stopwatch.running {
+		return stopwatch._accumulation
+	}
+	return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
 }
 }
 
 
 diff :: proc "contextless" (start, end: Time) -> Duration {
 diff :: proc "contextless" (start, end: Time) -> Duration {
@@ -171,9 +173,9 @@ day :: proc "contextless" (t: Time) -> (day: int) {
 }
 }
 
 
 weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
 weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
-    abs := _time_abs(t)
-    sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
-    return Weekday(int(sec) / SECONDS_PER_DAY)
+	abs := _time_abs(t)
+	sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
+	return Weekday(int(sec) / SECONDS_PER_DAY)
 }
 }
 
 
 clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
 clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }

+ 28 - 6
examples/demo/demo.odin

@@ -1,3 +1,4 @@
+//+vet !using-stmt !using-param
 package main
 package main
 
 
 import "core:fmt"
 import "core:fmt"
@@ -458,6 +459,27 @@ named_proc_return_parameters :: proc() {
 	fmt.println("foo2 =", foo2()) // 567 321
 	fmt.println("foo2 =", foo2()) // 567 321
 }
 }
 
 
+variadic_procedures :: proc() {
+	fmt.println("\n# variadic procedures")
+	sum :: proc(nums: ..int, init_value:= 0) -> (result: int) {
+		result = init_value
+		for n in nums {
+			result += n
+		}
+		return
+	}
+	fmt.println("sum(()) =", sum())
+	fmt.println("sum(1, 2) =", sum(1, 2))
+	fmt.println("sum(1, 2, 3, 4, 5) =", sum(1, 2, 3, 4, 5))
+	fmt.println("sum(1, 2, 3, 4, 5, init_value = 5) =", sum(1, 2, 3, 4, 5, init_value = 5))
+
+	// pass a slice as varargs
+	odds := []int{1, 3, 5}
+	fmt.println("odds =", odds)
+	fmt.println("sum(..odds) =", sum(..odds))
+	fmt.println("sum(..odds, init_value = 5) =", sum(..odds, init_value = 5))
+}
+
 
 
 explicit_procedure_overloading :: proc() {
 explicit_procedure_overloading :: proc() {
 	fmt.println("\n# explicit procedure overloading")
 	fmt.println("\n# explicit procedure overloading")
@@ -2023,12 +2045,11 @@ relative_data_types :: proc() {
 	fmt.println(ptr^)
 	fmt.println(ptr^)
 
 
 	arr := [3]int{1, 2, 3}
 	arr := [3]int{1, 2, 3}
-	s := arr[:]
-	rel_slice: #relative(i16) []int
-	rel_slice = s
-	fmt.println(rel_slice)
-	fmt.println(rel_slice[:])
-	fmt.println(rel_slice[1])
+	multi_ptr: #relative(i16) [^]int
+	multi_ptr = &arr[0]
+	fmt.println(multi_ptr)
+	fmt.println(multi_ptr[:3])
+	fmt.println(multi_ptr[1])
 }
 }
 
 
 or_else_operator :: proc() {
 or_else_operator :: proc() {
@@ -2462,6 +2483,7 @@ main :: proc() {
 		the_basics()
 		the_basics()
 		control_flow()
 		control_flow()
 		named_proc_return_parameters()
 		named_proc_return_parameters()
+		variadic_procedures()
 		explicit_procedure_overloading()
 		explicit_procedure_overloading()
 		struct_type()
 		struct_type()
 		union_type()
 		union_type()

+ 11 - 0
src/array.cpp

@@ -168,6 +168,17 @@ gb_internal gb_inline Slice<T> slice(Slice<T> const &array, isize lo, isize hi)
 	}
 	}
 	return out;
 	return out;
 }
 }
+template <typename T>
+gb_internal gb_inline Slice<T> slice(Array<T> const &array, isize lo, isize hi) {
+	GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
+	Slice<T> out = {};
+	isize len = hi-lo;
+	if (len > 0) {
+		out.data = array.data+lo;
+		out.count = len;
+	}
+	return out;
+}
 
 
 
 
 template <typename T>
 template <typename T>

+ 41 - 3
src/build_settings.cpp

@@ -216,6 +216,43 @@ enum BuildPath : u8 {
 	BuildPathCOUNT,
 	BuildPathCOUNT,
 };
 };
 
 
+enum VetFlags : u64 {
+	VetFlag_NONE       = 0,
+	VetFlag_Unused     = 1u<<0, // 1
+	VetFlag_Shadowing  = 1u<<1, // 2
+	VetFlag_UsingStmt  = 1u<<2, // 4
+	VetFlag_UsingParam = 1u<<3, // 8
+	VetFlag_Style      = 1u<<4, // 16
+	VetFlag_Semicolon  = 1u<<5, // 32
+
+	VetFlag_Extra     = 1u<<16,
+
+	VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt, // excluding extra
+
+	VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam,
+};
+
+u64 get_vet_flag_from_name(String const &name) {
+	if (name == "unused") {
+		return VetFlag_Unused;
+	} else if (name == "shadowing") {
+		return VetFlag_Shadowing;
+	} else if (name == "using-stmt") {
+		return VetFlag_UsingStmt;
+	} else if (name == "using-param") {
+		return VetFlag_UsingParam;
+	} else if (name == "style") {
+		return VetFlag_Style;
+	} else if (name == "semicolon") {
+		return VetFlag_Semicolon;
+	} else if (name == "extra") {
+		return VetFlag_Extra;
+	}
+	return VetFlag_NONE;
+}
+
+
+
 // This stores the information for the specify architecture of this build
 // This stores the information for the specify architecture of this build
 struct BuildContext {
 struct BuildContext {
 	// Constants
 	// Constants
@@ -255,6 +292,8 @@ struct BuildContext {
 	String resource_filepath;
 	String resource_filepath;
 	String pdb_filepath;
 	String pdb_filepath;
 
 
+	u64 vet_flags;
+
 	bool   has_resource;
 	bool   has_resource;
 	String link_flags;
 	String link_flags;
 	String extra_linker_flags;
 	String extra_linker_flags;
@@ -280,15 +319,12 @@ struct BuildContext {
 	bool   no_entry_point;
 	bool   no_entry_point;
 	bool   no_thread_local;
 	bool   no_thread_local;
 	bool   use_lld;
 	bool   use_lld;
-	bool   vet;
-	bool   vet_extra;
 	bool   cross_compiling;
 	bool   cross_compiling;
 	bool   different_os;
 	bool   different_os;
 	bool   keep_object_files;
 	bool   keep_object_files;
 	bool   disallow_do;
 	bool   disallow_do;
 
 
 	bool   strict_style;
 	bool   strict_style;
-	bool   strict_style_init_only;
 
 
 	bool   ignore_warnings;
 	bool   ignore_warnings;
 	bool   warnings_as_errors;
 	bool   warnings_as_errors;
@@ -318,6 +354,8 @@ struct BuildContext {
 
 
 	isize max_error_count;
 	isize max_error_count;
 
 
+	bool tilde_backend;
+
 
 
 	u32 cmd_doc_flags;
 	u32 cmd_doc_flags;
 	Array<String> extra_packages;
 	Array<String> extra_packages;

+ 5 - 2
src/check_builtin.cpp

@@ -1406,7 +1406,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
 		}
 		}
 		return false;
 		return false;
 	} else if (name == "load_or") {
 	} else if (name == "load_or") {
-		warning(call, "'#load_or' is deprecated in favour of '#load(path) or_else default'");
+		error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'");
 
 
 		if (ce->args.count != 2) {
 		if (ce->args.count != 2) {
 			if (ce->args.count == 0) {
 			if (ce->args.count == 0) {
@@ -1748,7 +1748,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 			mode = Addressing_Constant;
 			mode = Addressing_Constant;
 			value = exact_value_i64(at->EnumeratedArray.count);
 			value = exact_value_i64(at->EnumeratedArray.count);
 			type = t_untyped_integer;
 			type = t_untyped_integer;
-		} else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
+		} else if (is_type_slice(op_type) && id == BuiltinProc_len) {
 			mode = Addressing_Value;
 			mode = Addressing_Value;
 		} else if (is_type_dynamic_array(op_type)) {
 		} else if (is_type_dynamic_array(op_type)) {
 			mode = Addressing_Value;
 			mode = Addressing_Value;
@@ -3692,6 +3692,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 					case Type_SimdVector:
 					case Type_SimdVector:
 						operand->type = alloc_type_multi_pointer(base_array_type(base));
 						operand->type = alloc_type_multi_pointer(base_array_type(base));
 						break;
 						break;
+					case Type_Matrix:
+						operand->type = alloc_type_multi_pointer(base->Matrix.elem);
+						break;
 					}
 					}
 				}
 				}
 				break;
 				break;

+ 77 - 90
src/check_decl.cpp

@@ -7,13 +7,15 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
 		e->type == t_invalid) {
 		e->type == t_invalid) {
 
 
 		if (operand->mode == Addressing_Builtin) {
 		if (operand->mode == Addressing_Builtin) {
+			ERROR_BLOCK();
 			gbString expr_str = expr_to_string(operand->expr);
 			gbString expr_str = expr_to_string(operand->expr);
 
 
-			// TODO(bill): is this a good enough error message?
 			error(operand->expr,
 			error(operand->expr,
-				  "Cannot assign built-in procedure '%s' in %.*s",
-				  expr_str,
-				  LIT(context_name));
+			      "Cannot assign built-in procedure '%s' in %.*s",
+			      expr_str,
+			      LIT(context_name));
+
+			error_line("\tBuilt-in procedures are implemented by the compiler and might not be actually instantiated procedure\n");
 
 
 			operand->mode = Addressing_Invalid;
 			operand->mode = Addressing_Invalid;
 
 
@@ -159,9 +161,8 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op
 	}
 	}
 
 
 	if (operand->mode != Addressing_Constant) {
 	if (operand->mode != Addressing_Constant) {
-		// TODO(bill): better error
 		gbString str = expr_to_string(operand->expr);
 		gbString str = expr_to_string(operand->expr);
-		error(operand->expr, "'%s' is not a constant", str);
+		error(operand->expr, "'%s' is not a compile-time known constant", str);
 		gb_string_free(str);
 		gb_string_free(str);
 		if (e->type == nullptr) {
 		if (e->type == nullptr) {
 			e->type = t_invalid;
 			e->type = t_invalid;
@@ -354,31 +355,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
 
 
 	// using decl
 	// using decl
 	if (decl->is_using) {
 	if (decl->is_using) {
-		warning(init_expr, "'using' an enum declaration is not allowed, prefer using implicit selector expressions e.g. '.A'");
-		#if 1
-		// NOTE(bill): Must be an enum declaration
-		if (te->kind == Ast_EnumType) {
-			Scope *parent = e->scope;
-			if (parent->flags&ScopeFlag_File) {
-				// NOTE(bill): Use package scope
-				parent = parent->parent;
-			}
-
-			Type *t = base_type(e->type);
-			if (t->kind == Type_Enum) {
-				for (Entity *f : t->Enum.fields) {
-					if (f->kind != Entity_Constant) {
-						continue;
-					}
-					String name = f->token.string;
-					if (is_blank_ident(name)) {
-						continue;
-					}
-					add_entity(ctx, parent, nullptr, f);
-				}
-			}
-		}
-		#endif
+		error(init_expr, "'using' an enum declaration is not allowed, prefer using implicit selector expressions e.g. '.A'");
 	}
 	}
 }
 }
 
 
@@ -757,6 +734,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
 	return link_name;
 	return link_name;
 }
 }
 
 
+gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
+	if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
+		return;
+	}
+	if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+		error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+	} else if (ac.objc_type == nullptr) {
+		error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+	} else if (ac.objc_name.len == 0 && ac.objc_type) {
+		error(e->token, "@(objc_name) is required with @(objc_type)");
+	} else {
+		Type *t = ac.objc_type;
+		if (t->kind == Type_Named) {
+			Entity *tn = t->Named.type_name;
+
+			GB_ASSERT(tn->kind == Entity_TypeName);
+
+			if (tn->scope != e->scope) {
+				error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+			} else {
+				mutex_lock(&global_type_name_objc_metadata_mutex);
+				defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+				if (!tn->TypeName.objc_metadata) {
+					tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+				}
+				auto *md = tn->TypeName.objc_metadata;
+				mutex_lock(md->mutex);
+				defer (mutex_unlock(md->mutex));
+
+				if (!ac.objc_is_class_method) {
+					bool ok = true;
+					for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+						if (entry.name == ac.objc_name) {
+							error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+							ok = false;
+							break;
+						}
+					}
+					if (ok) {
+						array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+					}
+				} else {
+					bool ok = true;
+					for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+						if (entry.name == ac.objc_name) {
+							error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+							ok = false;
+							break;
+						}
+					}
+					if (ok) {
+						array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+					}
+				}
+			}
+		}
+	}
+}
+
 gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	GB_ASSERT(e->type == nullptr);
 	GB_ASSERT(e->type == nullptr);
 	if (d->proc_lit->kind != Ast_ProcLit) {
 	if (d->proc_lit->kind != Ast_ProcLit) {
@@ -840,62 +877,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	}
 	}
 	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
 	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
 
 
-	if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
-		if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
-			error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
-		} else if (ac.objc_type == nullptr) {
-			error(e->token, "@(objc_name) requires that @(objc_type) to be set");
-		} else if (ac.objc_name.len == 0 && ac.objc_type) {
-			error(e->token, "@(objc_name) is required with @(objc_type)");
-		} else {
-			Type *t = ac.objc_type;
-			if (t->kind == Type_Named) {
-				Entity *tn = t->Named.type_name;
-
-				GB_ASSERT(tn->kind == Entity_TypeName);
-
-				if (tn->scope != e->scope) {
-					error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
-				} else {
-					mutex_lock(&global_type_name_objc_metadata_mutex);
-					defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
-
-					if (!tn->TypeName.objc_metadata) {
-						tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
-					}
-					auto *md = tn->TypeName.objc_metadata;
-					mutex_lock(md->mutex);
-					defer (mutex_unlock(md->mutex));
-
-					if (!ac.objc_is_class_method) {
-						bool ok = true;
-						for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
-							if (entry.name == ac.objc_name) {
-								error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
-								ok = false;
-								break;
-							}
-						}
-						if (ok) {
-							array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
-						}
-					} else {
-						bool ok = true;
-						for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
-							if (entry.name == ac.objc_name) {
-								error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
-								ok = false;
-								break;
-							}
-						}
-						if (ok) {
-							array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
-						}
-					}
-				}
-			}
-		}
-	}
+	check_objc_methods(ctx, e, ac);
 
 
 	if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
 	if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
 		error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
 		error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
@@ -1059,7 +1041,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 		auto *fp = &ctx->info->foreigns;
 		auto *fp = &ctx->info->foreigns;
 		StringHashKey key = string_hash_string(name);
 		StringHashKey key = string_hash_string(name);
 		Entity **found = string_map_get(fp, key);
 		Entity **found = string_map_get(fp, key);
-		if (found) {
+		if (found && e != *found) {
 			Entity *f = *found;
 			Entity *f = *found;
 			TokenPos pos = f->token.pos;
 			TokenPos pos = f->token.pos;
 			Type *this_type = base_type(e->type);
 			Type *this_type = base_type(e->type);
@@ -1241,7 +1223,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
 	check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
 	check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
 }
 }
 
 
-gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
+gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
 	GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
 	GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
 	auto *pge = &pg_entity->ProcGroup;
 	auto *pge = &pg_entity->ProcGroup;
 	String proc_group_name = pg_entity->token.string;
 	String proc_group_name = pg_entity->token.string;
@@ -1366,6 +1348,11 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity,
 		}
 		}
 	}
 	}
 
 
+	AttributeContext ac = {};
+	check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
+	check_objc_methods(ctx, pg_entity, ac);
+
+
 }
 }
 
 
 gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
 gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
@@ -1626,7 +1613,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
 	}
 	}
 	check_close_scope(ctx);
 	check_close_scope(ctx);
 
 
-	check_scope_usage(ctx->checker, ctx->scope);
+	check_scope_usage(ctx->checker, ctx->scope, check_vet_flags(body));
 
 
 	add_deps_from_child_to_parent(decl);
 	add_deps_from_child_to_parent(decl);
 
 

+ 184 - 150
src/check_expr.cpp

@@ -349,6 +349,10 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
 		return false;
 		return false;
 	}
 	}
 
 
+	if (base_entity->flags & EntityFlag_Disabled) {
+		return false;
+	}
+
 	String name = base_entity->token.string;
 	String name = base_entity->token.string;
 
 
 	Type *src = base_type(base_entity->type);
 	Type *src = base_type(base_entity->type);
@@ -462,7 +466,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
 
 
 
 
 	{
 	{
-		// LEAK TODO(bill): This is technically a memory leak as it has to generate the type twice
+		// LEAK NOTE(bill): This is technically a memory leak as it has to generate the type twice
 		bool prev_no_polymorphic_errors = nctx.no_polymorphic_errors;
 		bool prev_no_polymorphic_errors = nctx.no_polymorphic_errors;
 		defer (nctx.no_polymorphic_errors = prev_no_polymorphic_errors);
 		defer (nctx.no_polymorphic_errors = prev_no_polymorphic_errors);
 		nctx.no_polymorphic_errors = false;
 		nctx.no_polymorphic_errors = false;
@@ -470,7 +474,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
 		// NOTE(bill): Reset scope from the failed procedure type
 		// NOTE(bill): Reset scope from the failed procedure type
 		scope_reset(scope);
 		scope_reset(scope);
 
 
-		// LEAK TODO(bill): Cloning this AST may be leaky
+		// LEAK NOTE(bill): Cloning this AST may be leaky but this is not really an issue due to arena-based allocation
 		Ast *cloned_proc_type_node = clone_ast(pt->node);
 		Ast *cloned_proc_type_node = clone_ast(pt->node);
 		success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
 		success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
 		if (!success) {
 		if (!success) {
@@ -778,16 +782,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
 		}
 		}
 	}
 	}
 
 
-	// ^T <- rawptr
-#if 0
-	// TODO(bill): Should C-style (not C++) pointer cast be allowed?
-	if (is_type_pointer(dst) && is_type_rawptr(src)) {
-	    return true;
-	}
-#endif
-#if 1
-
-
 	// rawptr <- ^T
 	// rawptr <- ^T
 	if (are_types_identical(type, t_rawptr) && is_type_pointer(src)) {
 	if (are_types_identical(type, t_rawptr) && is_type_pointer(src)) {
 		return 5;
 		return 5;
@@ -808,7 +802,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
 			return 4;
 			return 4;
 		}
 		}
 	}
 	}
-#endif
 
 
 	if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) {
 	if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) {
 		bool modify_type = !c->no_polymorphic_errors;
 		bool modify_type = !c->no_polymorphic_errors;
@@ -824,7 +817,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
 			}
 			}
 		}
 		}
 
 
-		// TODO(bill): Determine which rule is a better on in practice
 		if (dst->Union.variants.count == 1) {
 		if (dst->Union.variants.count == 1) {
 			Type *vt = dst->Union.variants[0];
 			Type *vt = dst->Union.variants[0];
 			i64 score = check_distance_between_types(c, operand, vt);
 			i64 score = check_distance_between_types(c, operand, vt);
@@ -864,8 +856,8 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
 		}
 		}
 	}
 	}
 
 
-	if (is_type_relative_slice(dst)) {
-		i64 score = check_distance_between_types(c, operand, dst->RelativeSlice.slice_type);
+	if (is_type_relative_multi_pointer(dst)) {
+		i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
 		if (score >= 0) {
 		if (score >= 0) {
 			return score+2;
 			return score+2;
 		}
 		}
@@ -1013,8 +1005,8 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
 		case Type_RelativePointer:
 		case Type_RelativePointer:
 			type = type->RelativePointer.pointer_type;
 			type = type->RelativePointer.pointer_type;
 			continue;
 			continue;
-		case Type_RelativeSlice:
-			type = type->RelativeSlice.slice_type;
+		case Type_RelativeMultiPointer:
+			type = type->RelativeMultiPointer.pointer_type;
 			continue;
 			continue;
 		}
 		}
 		return nullptr;
 		return nullptr;
@@ -1093,7 +1085,7 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
 
 
 			// TODO(bill): is this a good enough error message?
 			// TODO(bill): is this a good enough error message?
 			error(operand->expr,
 			error(operand->expr,
-			      "Cannot assign overloaded procedure '%s' to '%s' in %.*s",
+			      "Cannot assign overloaded procedure group '%s' to '%s' in %.*s",
 			      expr_str,
 			      expr_str,
 			      op_type_str,
 			      op_type_str,
 			      LIT(context_name));
 			      LIT(context_name));
@@ -1120,7 +1112,6 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
 
 
 		switch (operand->mode) {
 		switch (operand->mode) {
 		case Addressing_Builtin:
 		case Addressing_Builtin:
-			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error(operand->expr,
 			error(operand->expr,
 			      "Cannot assign built-in procedure '%s' in %.*s",
 			      "Cannot assign built-in procedure '%s' in %.*s",
 			      expr_str,
 			      expr_str,
@@ -1412,9 +1403,6 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
 		return false;
 		return false;
 	case Type_Proc:
 	case Type_Proc:
 		if (source->kind == Type_Proc) {
 		if (source->kind == Type_Proc) {
-			// return check_is_assignable_to(c, &o, poly);
-			// TODO(bill): Polymorphic type assignment
-			#if 1
 			TypeProc *x = &poly->Proc;
 			TypeProc *x = &poly->Proc;
 			TypeProc *y = &source->Proc;
 			TypeProc *y = &source->Proc;
 			if (x->calling_convention != y->calling_convention) {
 			if (x->calling_convention != y->calling_convention) {
@@ -1447,7 +1435,6 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
 			}
 			}
 
 
 			return true;
 			return true;
-			#endif
 		}
 		}
 		return false;
 		return false;
 	case Type_Map:
 	case Type_Map:
@@ -1699,7 +1686,6 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
 		gb_string_free(str);
 		gb_string_free(str);
 		return false;
 		return false;
 	}
 	}
-	// TODO(bill): Handle errors correctly
 	Type *type = base_type(core_array_type(o->type));
 	Type *type = base_type(core_array_type(o->type));
 	gbString str = nullptr;
 	gbString str = nullptr;
 	switch (op.kind) {
 	switch (op.kind) {
@@ -1743,7 +1729,6 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
 gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
 gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
 	Type *main_type = o->type;
 	Type *main_type = o->type;
 
 
-	// TODO(bill): Handle errors correctly
 	Type *type = base_type(core_array_type(main_type));
 	Type *type = base_type(core_array_type(main_type));
 	Type *ct = core_type(type);
 	Type *ct = core_type(type);
 
 
@@ -2261,7 +2246,7 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
 }
 }
 
 
 gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
 gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
-	if (!build_context.strict_style) {
+	if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) {
 		return;
 		return;
 	}
 	}
 
 
@@ -2351,7 +2336,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
 				o->type = alloc_type_pointer(o->type);
 				o->type = alloc_type_pointer(o->type);
 			}
 			}
 		} else {
 		} else {
-			if (build_context.strict_style && ast_node_expect(node, Ast_UnaryExpr)) {
+			if (ast_node_expect(node, Ast_UnaryExpr)) {
 				ast_node(ue, UnaryExpr, node);
 				ast_node(ue, UnaryExpr, node);
 				check_old_for_or_switch_value_usage(ue->expr);
 				check_old_for_or_switch_value_usage(ue->expr);
 			}
 			}
@@ -2775,8 +2760,6 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
 		gb_string_free(err_str);
 		gb_string_free(err_str);
 	}
 	}
 
 
-	// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
-
 	if (!is_type_integer(x->type)) {
 	if (!is_type_integer(x->type)) {
 		gbString err_str = expr_to_string(x->expr);
 		gbString err_str = expr_to_string(x->expr);
 		error(node, "Shift operand '%s' must be an integer", err_str);
 		error(node, "Shift operand '%s' must be an integer", err_str);
@@ -3099,7 +3082,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
 		update_untyped_expr_type(c, x->expr, final_type, true);
 		update_untyped_expr_type(c, x->expr, final_type, true);
 	}
 	}
 
 
-	if (build_context.vet_extra) {
+	if (check_vet_flags(x->expr) & VetFlag_Extra) {
 		if (are_types_identical(x->type, type)) {
 		if (are_types_identical(x->type, type)) {
 			gbString str = type_to_string(type);
 			gbString str = type_to_string(type);
 			warning(x->expr, "Unneeded cast to the same type '%s'", str);
 			warning(x->expr, "Unneeded cast to the same type '%s'", str);
@@ -3171,7 +3154,7 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
 		return false;
 		return false;
 	}
 	}
 
 
-	if (build_context.vet_extra) {
+	if (check_vet_flags(node) & VetFlag_Extra) {
 		if (are_types_identical(o->type, dst_t)) {
 		if (are_types_identical(o->type, dst_t)) {
 			gbString str = type_to_string(dst_t);
 			gbString str = type_to_string(dst_t);
 			warning(o->expr, "Unneeded transmute to the same type '%s'", str);
 			warning(o->expr, "Unneeded transmute to the same type '%s'", str);
@@ -4437,7 +4420,6 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
 	case_end;
 	case_end;
 
 
 	default:
 	default:
-		// TODO(bill): Should this be a general fallback?
 		if (success_) *success_ = true;
 		if (success_) *success_ = true;
 		if (finish_) *finish_ = true;
 		if (finish_) *finish_ = true;
 		return empty_exact_value;
 		return empty_exact_value;
@@ -4793,8 +4775,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
 	}
 	}
 
 
 	if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
 	if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
-		// TODO(bill): Simd_Vector swizzling
-
 		String field_name = selector->Ident.token.string;
 		String field_name = selector->Ident.token.string;
 		if (1 < field_name.len && field_name.len <= 4) {
 		if (1 < field_name.len && field_name.len <= 4) {
 			u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
 			u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -5112,27 +5092,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
 	return false;
 	return false;
 }
 }
 
 
-gb_internal isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) {
-	if (lhs != nullptr && c->decl != nullptr) {
-		for (isize j = 0; (tuple_index + j) < lhs_count && j < tuple_count; j++) {
-			Entity *e = lhs[tuple_index + j];
-			if (e != nullptr) {
-				DeclInfo *decl = decl_info_of_entity(e);
-				if (decl != nullptr) {
-					rw_mutex_shared_lock(&decl->deps_mutex);
-					rw_mutex_lock(&c->decl->deps_mutex);
-					for (Entity *dep : decl->deps) {
-						ptr_set_add(&c->decl->deps, dep);
-					}
-					rw_mutex_unlock(&c->decl->deps_mutex);
-					rw_mutex_shared_unlock(&decl->deps_mutex);
-				}
-			}
-		}
-	}
-	return tuple_count;
-}
-
 gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
 gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
 	if (o.type && is_type_no_copy(o.type)) {
 	if (o.type && is_type_no_copy(o.type)) {
 		Ast *expr = unparen_expr(o.expr);
 		Ast *expr = unparen_expr(o.expr);
@@ -5240,6 +5199,31 @@ enum UnpackFlag : u32 {
 
 
 
 
 gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
 gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
+	auto const &add_dependencies_from_unpacking = [](CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) -> isize {
+		if (lhs == nullptr || c->decl == nullptr) {
+			return tuple_count;
+		}
+		for (isize j = 0; (tuple_index + j) < lhs_count && j < tuple_count; j++) {
+			Entity *e = lhs[tuple_index + j];
+			if (e == nullptr) {
+				continue;
+			}
+			DeclInfo *decl = decl_info_of_entity(e);
+			if (decl == nullptr) {
+				continue;
+			}
+			rw_mutex_shared_lock(&decl->deps_mutex);
+			rw_mutex_lock(&c->decl->deps_mutex);
+			for (Entity *dep : decl->deps) {
+				ptr_set_add(&c->decl->deps, dep);
+			}
+			rw_mutex_unlock(&c->decl->deps_mutex);
+			rw_mutex_shared_unlock(&decl->deps_mutex);
+		}
+		return tuple_count;
+	};
+
+
 	bool allow_ok    = (flags & UnpackFlag_AllowOk) != 0;
 	bool allow_ok    = (flags & UnpackFlag_AllowOk) != 0;
 	bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
 	bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
 	bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
 	bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
@@ -5494,6 +5478,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
 
 
 	auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count);
 	auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count);
 
 
+	bool named_variadic_param = false;
+
 	if (named_operands.count != 0) {
 	if (named_operands.count != 0) {
 		GB_ASSERT(ce->split_args->named.count == named_operands.count);
 		GB_ASSERT(ce->split_args->named.count == named_operands.count);
 		for_array(i, ce->split_args->named) {
 		for_array(i, ce->split_args->named) {
@@ -5519,6 +5505,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
 				err = CallArgumentError_ParameterNotFound;
 				err = CallArgumentError_ParameterNotFound;
 				continue;
 				continue;
 			}
 			}
+			if (pt->variadic && param_index == pt->variadic_index) {
+				named_variadic_param = true;
+			}
 			if (visited[param_index]) {
 			if (visited[param_index]) {
 				if (show_error) {
 				if (show_error) {
 					error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
 					error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
@@ -5720,11 +5709,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
 				}
 				}
 				continue;
 				continue;
 			}
 			}
-
-			if (param_is_variadic) {
-				continue;
-			}
-
 			score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
 			score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
 		}
 		}
 	}
 	}
@@ -6136,7 +6120,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 	{
 	{
 		// NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
 		// NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
 		// where the same positional parameter has the same type value (and ellipsis)
 		// where the same positional parameter has the same type value (and ellipsis)
-		bool proc_arg_count_all_equal = true;
 		isize proc_arg_count = -1;
 		isize proc_arg_count = -1;
 		for (Entity *p : procs) {
 		for (Entity *p : procs) {
 			Type *pt = base_type(p->type);
 			Type *pt = base_type(p->type);
@@ -6144,15 +6127,12 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 				if (proc_arg_count < 0) {
 				if (proc_arg_count < 0) {
 					proc_arg_count = pt->Proc.param_count;
 					proc_arg_count = pt->Proc.param_count;
 				} else {
 				} else {
-					if (proc_arg_count != pt->Proc.param_count) {
-						proc_arg_count_all_equal = false;
-						break;
-					}
+					proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count);
 				}
 				}
 			}
 			}
 		}
 		}
 
 
-		if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
+		if (proc_arg_count >= 0) {
 			lhs_count = proc_arg_count;
 			lhs_count = proc_arg_count;
 			if (lhs_count > 0)  {
 			if (lhs_count > 0)  {
 				lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
 				lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
@@ -6258,14 +6238,18 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 			}
 			}
 			isize index = i;
 			isize index = i;
 
 
+			ValidIndexAndScore item = {};
+			item.score = data.score;
+
 			if (data.gen_entity != nullptr) {
 			if (data.gen_entity != nullptr) {
 				array_add(&proc_entities, data.gen_entity);
 				array_add(&proc_entities, data.gen_entity);
 				index = proc_entities.count-1;
 				index = proc_entities.count-1;
+
+				// prefer non-polymorphic procedures over polymorphic
+				item.score += assign_score_function(1);
 			}
 			}
 
 
-			ValidIndexAndScore item = {};
 			item.index = index;
 			item.index = index;
-			item.score = data.score;
 			array_add(&valids, item);
 			array_add(&valids, item);
 		}
 		}
 	}
 	}
@@ -6328,9 +6312,44 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 			print_argument_types();
 			print_argument_types();
 		}
 		}
 
 
+		if (procs.count == 0) {
+			procs = proc_group_entities_cloned(c, *operand);
+		}
 		if (procs.count > 0) {
 		if (procs.count > 0) {
 			error_line("Did you mean to use one of the following:\n");
 			error_line("Did you mean to use one of the following:\n");
 		}
 		}
+		isize max_name_length = 0;
+		isize max_type_length = 0;
+		for (Entity *proc : procs) {
+			Type *t = base_type(proc->type);
+			if (t == t_invalid) continue;
+			String prefix = {};
+			String prefix_sep = {};
+			if (proc->pkg) {
+				prefix = proc->pkg->name;
+				prefix_sep = str_lit(".");
+			}
+			String name = proc->token.string;
+			max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len);
+
+			gbString pt;
+			if (t->Proc.node != nullptr) {
+				pt = expr_to_string(t->Proc.node);
+			} else {
+				pt = type_to_string(t);
+			}
+
+			max_type_length = gb_max(max_type_length, gb_string_length(pt));
+			gb_string_free(pt);
+		}
+
+		isize max_spaces = gb_max(max_name_length, max_type_length);
+		char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
+		for (isize i = 0; i < max_spaces; i++) {
+			spaces[i] = ' ';
+		}
+		spaces[max_spaces] = 0;
+
 		for (Entity *proc : procs) {
 		for (Entity *proc : procs) {
 			TokenPos pos = proc->token.pos;
 			TokenPos pos = proc->token.pos;
 			Type *t = base_type(proc->type);
 			Type *t = base_type(proc->type);
@@ -6350,12 +6369,23 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 				prefix_sep = str_lit(".");
 				prefix_sep = str_lit(".");
 			}
 			}
 			String name = proc->token.string;
 			String name = proc->token.string;
+			isize len = prefix.len + prefix_sep.len + name.len;
+
+			int name_padding = cast(int)gb_max(max_name_length - len, 0);
+			int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0);
 
 
 			char const *sep = "::";
 			char const *sep = "::";
 			if (proc->kind == Entity_Variable) {
 			if (proc->kind == Entity_Variable) {
 				sep = ":=";
 				sep = ":=";
 			}
 			}
-			error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
+			error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n",
+			           LIT(prefix), LIT(prefix_sep), LIT(name),
+			           name_padding, spaces,
+			           sep,
+			           pt,
+			           type_padding, spaces,
+			           token_pos_to_string(pos)
+			);
 		}
 		}
 		if (procs.count > 0) {
 		if (procs.count > 0) {
 			error_line("\n");
 			error_line("\n");
@@ -6369,8 +6399,8 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 		error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
 		error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
 		print_argument_types();
 		print_argument_types();
 
 
-		for (isize i = 0; i < valids.count; i++) {
-			Entity *proc = proc_entities[valids[i].index];
+		for (auto const &valid : valids) {
+			Entity *proc = proc_entities[valid.index];
 			GB_ASSERT(proc != nullptr);
 			GB_ASSERT(proc != nullptr);
 			TokenPos pos = proc->token.pos;
 			TokenPos pos = proc->token.pos;
 			Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
 			Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
@@ -7107,7 +7137,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
 		i32 id = operand->builtin_id;
 		i32 id = operand->builtin_id;
 		Entity *e = entity_of_node(operand->expr);
 		Entity *e = entity_of_node(operand->expr);
 		if (e != nullptr && e->token.string == "expand_to_tuple") {
 		if (e != nullptr && e->token.string == "expand_to_tuple") {
-			warning(operand->expr, "'expand_to_tuple' has been replaced with 'expand_values'");
+			error(operand->expr, "'expand_to_tuple' has been replaced with 'expand_values'");
 		}
 		}
 		if (!check_builtin_procedure(c, operand, call, id, type_hint)) {
 		if (!check_builtin_procedure(c, operand, call, id, type_hint)) {
 			operand->mode = Addressing_Invalid;
 			operand->mode = Addressing_Invalid;
@@ -7128,6 +7158,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
 				c->decl->defer_used += 1;
 				c->decl->defer_used += 1;
 			}
 			}
 		}
 		}
+		add_entity_use(c, operand->expr, initial_entity);
 	}
 	}
 
 
 	if (operand->mode != Addressing_ProcGroup) {
 	if (operand->mode != Addressing_ProcGroup) {
@@ -7335,11 +7366,11 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
 		}
 		}
 		return true;
 		return true;
 
 
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		{
 		{
-			Type *slice_type = base_type(t->RelativeSlice.slice_type);
-			GB_ASSERT(slice_type->kind == Type_Slice);
-			o->type = slice_type->Slice.elem;
+			Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
+			GB_ASSERT(pointer_type->kind == Type_MultiPointer);
+			o->type = pointer_type->MultiPointer.elem;
 			if (o->mode != Addressing_Constant) {
 			if (o->mode != Addressing_Constant) {
 				o->mode = Addressing_Variable;
 				o->mode = Addressing_Variable;
 			}
 			}
@@ -9315,13 +9346,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
 	ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
 	ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
 	c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
 	c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
 
 
-	if (x.mode == Addressing_Invalid || x.type == t_invalid) {
+	if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) {
 		o->mode = Addressing_Invalid;
 		o->mode = Addressing_Invalid;
 		o->type = t_invalid;
 		o->type = t_invalid;
 		o->expr = node;
 		o->expr = node;
 		return kind;
 		return kind;
 	}
 	}
-	if (!is_type_proc(x.type)) {
+	if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) {
 		gbString type_str = type_to_string(x.type);
 		gbString type_str = type_to_string(x.type);
 		error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
 		error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
 		gb_string_free(type_str);
 		gb_string_free(type_str);
@@ -9344,76 +9375,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
 		first_arg->state_flags |= StateFlag_SelectorCallExpr;
 		first_arg->state_flags |= StateFlag_SelectorCallExpr;
 	}
 	}
 
 
-	Type *pt = base_type(x.type);
-	GB_ASSERT(pt->kind == Type_Proc);
-	Type *first_type = nullptr;
-	String first_arg_name = {};
-	if (pt->Proc.param_count > 0) {
-		Entity *f = pt->Proc.params->Tuple.variables[0];
-		first_type = f->type;
-		first_arg_name = f->token.string;
-	}
-	if (first_arg_name.len == 0) {
-		first_arg_name = str_lit("_");
-	}
+	if (e->kind != Entity_ProcGroup) {
+		Type *pt = base_type(x.type);
+		GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type));
+		Type *first_type = nullptr;
+		String first_arg_name = {};
+		if (pt->Proc.param_count > 0) {
+			Entity *f = pt->Proc.params->Tuple.variables[0];
+			first_type = f->type;
+			first_arg_name = f->token.string;
+		}
+		if (first_arg_name.len == 0) {
+			first_arg_name = str_lit("_");
+		}
 
 
-	if (first_type == nullptr) {
-		error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
-		o->mode = Addressing_Invalid;
-		o->type = t_invalid;
-		o->expr = node;
-		return Expr_Stmt;
-	}
+		if (first_type == nullptr) {
+			error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+			o->mode = Addressing_Invalid;
+			o->type = t_invalid;
+			o->expr = node;
+			return Expr_Stmt;
+		}
 
 
-	Operand y = {};
-	y.mode = first_arg->tav.mode;
-	y.type = first_arg->tav.type;
-	y.value = first_arg->tav.value;
+		Operand y = {};
+		y.mode = first_arg->tav.mode;
+		y.type = first_arg->tav.type;
+		y.value = first_arg->tav.value;
 
 
-	if (check_is_assignable_to(c, &y, first_type)) {
-		// Do nothing, it's valid
-	} else {
-		Operand z = y;
-		z.type = type_deref(y.type);
-		if (check_is_assignable_to(c, &z, first_type)) {
-			// NOTE(bill): AST GENERATION HACK!
-			Token op = {Token_Pointer};
-			first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
-		} else if (y.mode == Addressing_Variable) {
-			Operand w = y;
-			w.type = alloc_type_pointer(y.type);
-			if (check_is_assignable_to(c, &w, first_type)) {
+		if (check_is_assignable_to(c, &y, first_type)) {
+			// Do nothing, it's valid
+		} else {
+			Operand z = y;
+			z.type = type_deref(y.type);
+			if (check_is_assignable_to(c, &z, first_type)) {
 				// NOTE(bill): AST GENERATION HACK!
 				// NOTE(bill): AST GENERATION HACK!
-				Token op = {Token_And};
-				first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+				Token op = {Token_Pointer};
+				first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
+			} else if (y.mode == Addressing_Variable) {
+				Operand w = y;
+				w.type = alloc_type_pointer(y.type);
+				if (check_is_assignable_to(c, &w, first_type)) {
+					// NOTE(bill): AST GENERATION HACK!
+					Token op = {Token_And};
+					first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+				}
 			}
 			}
 		}
 		}
-	}
 
 
-	if (ce->args.count > 0) {
-		bool fail = false;
-		bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
-		for (Ast *arg : ce->args) {
-			bool mix = false;
-			if (first_is_field_value) {
-				mix = arg->kind != Ast_FieldValue;
-			} else {
-				mix = arg->kind == Ast_FieldValue;
+		if (ce->args.count > 0) {
+			bool fail = false;
+			bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+			for (Ast *arg : ce->args) {
+				bool mix = false;
+				if (first_is_field_value) {
+					mix = arg->kind != Ast_FieldValue;
+				} else {
+					mix = arg->kind == Ast_FieldValue;
+				}
+				if (mix) {
+					fail = true;
+					break;
+				}
 			}
 			}
-			if (mix) {
-				fail = true;
-				break;
+			if (!fail && first_is_field_value) {
+				Token op = {Token_Eq};
+				AstFile *f = first_arg->file();
+				first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
 			}
 			}
 		}
 		}
-		if (!fail && first_is_field_value) {
-			Token op = {Token_Eq};
-			AstFile *f = first_arg->file();
-			first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
-		}
 	}
 	}
 
 
-
-
 	auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
 	auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
 	modified_args[0] = first_arg;
 	modified_args[0] = first_arg;
 	slice_copy(&modified_args, ce->args, 1);
 	slice_copy(&modified_args, ce->args, 1);
@@ -9471,14 +9502,14 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
 
 
 	if (is_const) {
 	if (is_const) {
 		if (is_type_array(t)) {
 		if (is_type_array(t)) {
-			// OKay
+			// Okay
 		} else if (is_type_slice(t)) {
 		} else if (is_type_slice(t)) {
 			// Okay
 			// Okay
 		} else if (is_type_enumerated_array(t)) {
 		} else if (is_type_enumerated_array(t)) {
 			// Okay
 			// Okay
 		} else if (is_type_string(t)) {
 		} else if (is_type_string(t)) {
 			// Okay
 			// Okay
-		} else if (is_type_relative_slice(t)) {
+		} else if (is_type_relative_multi_pointer(t)) {
 			// Okay
 			// Okay
 		} else if (is_type_matrix(t)) {
 		} else if (is_type_matrix(t)) {
 			// Okay
 			// Okay
@@ -9616,17 +9647,9 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
 		}
 		}
 		break;
 		break;
 
 
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		valid = true;
 		valid = true;
-		o->type = t->RelativeSlice.slice_type;
-		if (o->mode != Addressing_Variable) {
-			gbString str = expr_to_string(node);
-			error(node, "Cannot relative slice '%s', as value is not addressable", str);
-			gb_string_free(str);
-			o->mode = Addressing_Invalid;
-			o->expr = node;
-			return kind;
-		}
+		o->type = type_deref(o->type);
 		break;
 		break;
 
 
 	case Type_EnumeratedArray:
 	case Type_EnumeratedArray:
@@ -9705,8 +9728,19 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
 			x[i:n] -> []T
 			x[i:n] -> []T
 		*/
 		*/
 		o->type = alloc_type_slice(t->MultiPointer.elem);
 		o->type = alloc_type_slice(t->MultiPointer.elem);
+	} else if (t->kind == Type_RelativeMultiPointer && se->high != nullptr) {
+		/*
+			x[:]   -> [^]T
+			x[i:]  -> [^]T
+			x[:n]  -> []T
+			x[i:n] -> []T
+		*/
+		Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
+		GB_ASSERT(pointer_type->kind == Type_MultiPointer);
+		o->type = alloc_type_slice(pointer_type->MultiPointer.elem);
 	}
 	}
 
 
+
 	o->mode = Addressing_Value;
 	o->mode = Addressing_Value;
 
 
 	if (is_type_string(t) && max_count >= 0) {
 	if (is_type_string(t) && max_count >= 0) {
@@ -9982,7 +10016,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
 			Type *type = type_of_expr(ac->expr);
 			Type *type = type_of_expr(ac->expr);
 			check_cast(c, o, type_hint);
 			check_cast(c, o, type_hint);
 			if (is_type_typed(type) && are_types_identical(type, type_hint)) {
 			if (is_type_typed(type) && are_types_identical(type, type_hint)) {
-				if (build_context.vet_extra) {
+				if (check_vet_flags(node) & VetFlag_Extra) {
 					error(node, "Redundant 'auto_cast' applied to expression");
 					error(node, "Redundant 'auto_cast' applied to expression");
 				}
 				}
 			}
 			}

+ 11 - 6
src/check_stmt.cpp

@@ -384,7 +384,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
 		}
 		}
 
 
 		if (e != nullptr) {
 		if (e != nullptr) {
-			// HACK TODO(bill): Should the entities be freed as it's technically a leak
 			rhs->mode = Addressing_Value;
 			rhs->mode = Addressing_Value;
 			rhs->type = e->type;
 			rhs->type = e->type;
 			rhs->proc_group = nullptr;
 			rhs->proc_group = nullptr;
@@ -394,7 +393,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
 			ast_node(i, Ident, node);
 			ast_node(i, Ident, node);
 			e = scope_lookup(ctx->scope, i->token.string);
 			e = scope_lookup(ctx->scope, i->token.string);
 			if (e != nullptr && e->kind == Entity_Variable) {
 			if (e != nullptr && e->kind == Entity_Variable) {
-				used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case
+				used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case
 			}
 			}
 		}
 		}
 
 
@@ -888,7 +887,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
 	check_open_scope(ctx, node);
 	check_open_scope(ctx, node);
 	defer (check_close_scope(ctx));
 	defer (check_close_scope(ctx));
 
 
-	check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be?
+	check_label(ctx, ss->label, node);
 
 
 	if (ss->init != nullptr) {
 	if (ss->init != nullptr) {
 		check_stmt(ctx, ss->init, 0);
 		check_stmt(ctx, ss->init, 0);
@@ -1125,7 +1124,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
 	check_open_scope(ctx, node);
 	check_open_scope(ctx, node);
 	defer (check_close_scope(ctx));
 	defer (check_close_scope(ctx));
 
 
-	check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be?
+	check_label(ctx, ss->label, node);
 
 
 	if (ss->tag->kind != Ast_AssignStmt) {
 	if (ss->tag->kind != Ast_AssignStmt) {
 		error(ss->tag, "Expected an 'in' assignment for this type switch statement");
 		error(ss->tag, "Expected an 'in' assignment for this type switch statement");
@@ -1960,7 +1959,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
 		Token token = ast_token(node);
 		Token token = ast_token(node);
 		if (vd->type != nullptr && entity_count > 1) {
 		if (vd->type != nullptr && entity_count > 1) {
 			error(token, "'using' can only be applied to one variable of the same type");
 			error(token, "'using' can only be applied to one variable of the same type");
-			// TODO(bill): Should a 'continue' happen here?
+			// NOTE(bill): `using` will only be applied to a single declaration
 		}
 		}
 
 
 		for (isize entity_index = 0; entity_index < 1; entity_index++) {
 		for (isize entity_index = 0; entity_index < 1; entity_index++) {
@@ -2294,7 +2293,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 	mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed;
 	mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed;
 
 
 	check_open_scope(ctx, node);
 	check_open_scope(ctx, node);
-	check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be?
+	check_label(ctx, fs->label, node);
 
 
 	if (fs->init != nullptr) {
 	if (fs->init != nullptr) {
 		check_stmt(ctx, fs->init, 0);
 		check_stmt(ctx, fs->init, 0);
@@ -2464,6 +2463,12 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
 			error(us->token, "Empty 'using' list");
 			error(us->token, "Empty 'using' list");
 			return;
 			return;
 		}
 		}
+		if (check_vet_flags(node) & VetFlag_UsingStmt) {
+			ERROR_BLOCK();
+			error(node, "'using' as a statement is not allowed when '-vet' or '-vet-using' is applied");
+			error_line("\t'using' is considered bad practice to use as a statement outside of immediate refactoring\n");
+		}
+
 		for (Ast *expr : us->list) {
 		for (Ast *expr : us->list) {
 			expr = unparen_expr(expr);
 			expr = unparen_expr(expr);
 			Entity *e = nullptr;
 			Entity *e = nullptr;

+ 10 - 4
src/check_type.cpp

@@ -1474,6 +1474,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 		Type *specialization = nullptr;
 		Type *specialization = nullptr;
 
 
 		bool is_using = (p->flags&FieldFlag_using) != 0;
 		bool is_using = (p->flags&FieldFlag_using) != 0;
+		if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) {
+			ERROR_BLOCK();
+			error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied");
+			error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n");
+
+		}
 
 
 		if (type_expr == nullptr) {
 		if (type_expr == nullptr) {
 			param_value = handle_parameter_value(ctx, nullptr, &type, default_value, true);
 			param_value = handle_parameter_value(ctx, nullptr, &type, default_value, true);
@@ -2772,16 +2778,16 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
 
 
 		Type *relative_type = nullptr;
 		Type *relative_type = nullptr;
 		Type *base_type = check_type(ctx, rt->type);
 		Type *base_type = check_type(ctx, rt->type);
-		if (!is_type_pointer(base_type) && !is_type_slice(base_type)) {
-			error(rt->type, "#relative types can only be a pointer or slice");
+		if (!is_type_pointer(base_type) && !is_type_multi_pointer(base_type)) {
+			error(rt->type, "#relative types can only be a pointer or multi-pointer");
 			relative_type = base_type;
 			relative_type = base_type;
 		} else if (base_integer == nullptr) {
 		} else if (base_integer == nullptr) {
 			relative_type = base_type;
 			relative_type = base_type;
 		} else {
 		} else {
 			if (is_type_pointer(base_type)) {
 			if (is_type_pointer(base_type)) {
 				relative_type = alloc_type_relative_pointer(base_type, base_integer);
 				relative_type = alloc_type_relative_pointer(base_type, base_integer);
-			} else if (is_type_slice(base_type)) {
-				relative_type = alloc_type_relative_slice(base_type, base_integer);
+			} else if (is_type_multi_pointer(base_type)) {
+				relative_type = alloc_type_relative_multi_pointer(base_type, base_integer);
 			}
 			}
 		}
 		}
 		GB_ASSERT(relative_type != nullptr);
 		GB_ASSERT(relative_type != nullptr);

+ 123 - 44
src/checker.cpp

@@ -521,6 +521,28 @@ GB_COMPARE_PROC(entity_variable_pos_cmp) {
 }
 }
 
 
 
 
+
+gb_internal u64 check_vet_flags(CheckerContext *c) {
+	AstFile *file = c->file;
+	if (file == nullptr &&
+	    c->curr_proc_decl &&
+	    c->curr_proc_decl->proc_lit) {
+		file = c->curr_proc_decl->proc_lit->file();
+	}
+	if (file && file->vet_flags_set) {
+		return file->vet_flags;
+	}
+	return build_context.vet_flags;
+}
+
+gb_internal u64 check_vet_flags(Ast *node) {
+	AstFile *file = node->file();
+	if (file && file->vet_flags_set) {
+		return file->vet_flags;
+	}
+	return build_context.vet_flags;
+}
+
 enum VettedEntityKind {
 enum VettedEntityKind {
 	VettedEntity_Invalid,
 	VettedEntity_Invalid,
 
 
@@ -655,9 +677,9 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
 	return false;
 	return false;
 }
 }
 
 
-gb_internal void check_scope_usage(Checker *c, Scope *scope) {
-	bool vet_unused = true;
-	bool vet_shadowing = true;
+gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
+	bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
+	bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
 
 
 	Array<VettedEntity> vetted_entities = {};
 	Array<VettedEntity> vetted_entities = {};
 	array_init(&vetted_entities, heap_allocator());
 	array_init(&vetted_entities, heap_allocator());
@@ -691,15 +713,17 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope) {
 
 
 		if (ve.kind == VettedEntity_Shadowed_And_Unused) {
 		if (ve.kind == VettedEntity_Shadowed_And_Unused) {
 			error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
 			error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
-		} else if (build_context.vet) {
+		} else if (vet_flags) {
 			switch (ve.kind) {
 			switch (ve.kind) {
 			case VettedEntity_Unused:
 			case VettedEntity_Unused:
-				error(e->token, "'%.*s' declared but not used", LIT(name));
+				if (vet_flags & VetFlag_Unused) {
+					error(e->token, "'%.*s' declared but not used", LIT(name));
+				}
 				break;
 				break;
 			case VettedEntity_Shadowed:
 			case VettedEntity_Shadowed:
-				if (e->flags&EntityFlag_Using) {
+				if ((vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0 && e->flags&EntityFlag_Using) {
 					error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
 					error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
-				} else {
+				} else if ((vet_flags & (VetFlag_Shadowing)) != 0) {
 					error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
 					error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
 				}
 				}
 				break;
 				break;
@@ -726,7 +750,7 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope) {
 		if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
 		if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
 			// Ignore these
 			// Ignore these
 		} else {
 		} else {
-			check_scope_usage(c, child);
+			check_scope_usage(c, child, vet_flags);
 		}
 		}
 	}
 	}
 }
 }
@@ -943,7 +967,6 @@ gb_internal void init_universal(void) {
 	add_global_bool_constant("true",  true);
 	add_global_bool_constant("true",  true);
 	add_global_bool_constant("false", false);
 	add_global_bool_constant("false", false);
 
 
-	// TODO(bill): Set through flags in the compiler
 	add_global_string_constant("ODIN_VENDOR",  bc->ODIN_VENDOR);
 	add_global_string_constant("ODIN_VENDOR",  bc->ODIN_VENDOR);
 	add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
 	add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
 	add_global_string_constant("ODIN_ROOT",    bc->ODIN_ROOT);
 	add_global_string_constant("ODIN_ROOT",    bc->ODIN_ROOT);
@@ -1046,6 +1069,7 @@ gb_internal void init_universal(void) {
 	add_global_bool_constant("ODIN_NO_RTTI",            bc->no_rtti);
 	add_global_bool_constant("ODIN_NO_RTTI",            bc->no_rtti);
 
 
 	add_global_bool_constant("ODIN_VALGRIND_SUPPORT",         bc->ODIN_VALGRIND_SUPPORT);
 	add_global_bool_constant("ODIN_VALGRIND_SUPPORT",         bc->ODIN_VALGRIND_SUPPORT);
+	add_global_bool_constant("ODIN_TILDE",                    bc->tilde_backend);
 
 
 	add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
 	add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
 
 
@@ -1114,6 +1138,7 @@ gb_internal void init_universal(void) {
 
 
 
 
 	t_u8_ptr       = alloc_type_pointer(t_u8);
 	t_u8_ptr       = alloc_type_pointer(t_u8);
+	t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
 	t_int_ptr      = alloc_type_pointer(t_int);
 	t_int_ptr      = alloc_type_pointer(t_int);
 	t_i64_ptr      = alloc_type_pointer(t_i64);
 	t_i64_ptr      = alloc_type_pointer(t_i64);
 	t_f64_ptr      = alloc_type_pointer(t_f64);
 	t_f64_ptr      = alloc_type_pointer(t_f64);
@@ -1453,7 +1478,6 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
 	if (ctx->decl) {
 	if (ctx->decl) {
 		mutex = &ctx->decl->type_and_value_mutex;
 		mutex = &ctx->decl->type_and_value_mutex;
 	} else if (ctx->pkg) {
 	} else if (ctx->pkg) {
-		// TODO(bill): is a per package mutex is a good idea here?
 		mutex = &ctx->pkg->type_and_value_mutex;
 		mutex = &ctx->pkg->type_and_value_mutex;
 	}
 	}
 
 
@@ -1581,30 +1605,28 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti
 	if (entity == nullptr) {
 	if (entity == nullptr) {
 		return;
 		return;
 	}
 	}
-	if (identifier != nullptr) {
-		if (identifier->kind != Ast_Ident) {
-			return;
-		}
-		Ast *empty_ident = nullptr;
-		entity->identifier.compare_exchange_strong(empty_ident, identifier);
-
-		identifier->Ident.entity = entity;
-
-		String dmsg = entity->deprecated_message;
-		if (dmsg.len > 0) {
-			warning(identifier, "%.*s is deprecated: %.*s", LIT(entity->token.string), LIT(dmsg));
-		}
-		String wmsg = entity->warning_message;
-		if (wmsg.len > 0) {
-			warning(identifier, "%.*s: %.*s", LIT(entity->token.string), LIT(wmsg));
-		}
-	}
-	entity->flags |= EntityFlag_Used;
 	add_declaration_dependency(c, entity);
 	add_declaration_dependency(c, entity);
+	entity->flags |= EntityFlag_Used;
 	if (entity_has_deferred_procedure(entity)) {
 	if (entity_has_deferred_procedure(entity)) {
 		Entity *deferred = entity->Procedure.deferred_procedure.entity;
 		Entity *deferred = entity->Procedure.deferred_procedure.entity;
 		add_entity_use(c, nullptr, deferred);
 		add_entity_use(c, nullptr, deferred);
 	}
 	}
+	if (identifier == nullptr || identifier->kind != Ast_Ident) {
+		return;
+	}
+	Ast *empty_ident = nullptr;
+	entity->identifier.compare_exchange_strong(empty_ident, identifier);
+
+	identifier->Ident.entity = entity;
+
+	String dmsg = entity->deprecated_message;
+	if (dmsg.len > 0) {
+		warning(identifier, "%.*s is deprecated: %.*s", LIT(entity->token.string), LIT(dmsg));
+	}
+	String wmsg = entity->warning_message;
+	if (wmsg.len > 0) {
+		warning(identifier, "%.*s: %.*s", LIT(entity->token.string), LIT(wmsg));
+	}
 }
 }
 
 
 
 
@@ -1947,9 +1969,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		add_type_info_type_internal(c, bt->RelativePointer.base_integer);
 		add_type_info_type_internal(c, bt->RelativePointer.base_integer);
 		break;
 		break;
 
 
-	case Type_RelativeSlice:
-		add_type_info_type_internal(c, bt->RelativeSlice.slice_type);
-		add_type_info_type_internal(c, bt->RelativeSlice.base_integer);
+	case Type_RelativeMultiPointer:
+		add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type);
+		add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer);
 		break;
 		break;
 
 
 	case Type_Matrix:
 	case Type_Matrix:
@@ -2188,9 +2210,9 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
 		add_min_dep_type_info(c, bt->RelativePointer.base_integer);
 		add_min_dep_type_info(c, bt->RelativePointer.base_integer);
 		break;
 		break;
 
 
-	case Type_RelativeSlice:
-		add_min_dep_type_info(c, bt->RelativeSlice.slice_type);
-		add_min_dep_type_info(c, bt->RelativeSlice.base_integer);
+	case Type_RelativeMultiPointer:
+		add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type);
+		add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer);
 		break;
 		break;
 
 
 	case Type_Matrix:
 	case Type_Matrix:
@@ -2310,7 +2332,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("memory_equal"),
 		str_lit("memory_equal"),
 		str_lit("memory_compare"),
 		str_lit("memory_compare"),
 		str_lit("memory_compare_zero"),
 		str_lit("memory_compare_zero"),
+	);
 
 
+	FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
 		// Extended data type internal procedures
 		// Extended data type internal procedures
 		str_lit("umodti3"),
 		str_lit("umodti3"),
 		str_lit("udivti3"),
 		str_lit("udivti3"),
@@ -2556,9 +2580,6 @@ gb_internal Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInf
 		}
 		}
 	}
 	}
 
 
-	// TODO(bill): This could be multithreaded to improve performance
-	// This means that the entity graph node set will have to be thread safe
-
 	TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
 	TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
 	auto G = array_make<EntityGraphNode *>(allocator, 0, M.count);
 	auto G = array_make<EntityGraphNode *>(allocator, 0, M.count);
 
 
@@ -2779,7 +2800,7 @@ gb_internal void init_core_type_info(Checker *c) {
 	t_type_info_bit_set          = find_core_type(c, str_lit("Type_Info_Bit_Set"));
 	t_type_info_bit_set          = find_core_type(c, str_lit("Type_Info_Bit_Set"));
 	t_type_info_simd_vector      = find_core_type(c, str_lit("Type_Info_Simd_Vector"));
 	t_type_info_simd_vector      = find_core_type(c, str_lit("Type_Info_Simd_Vector"));
 	t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
 	t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
-	t_type_info_relative_slice   = find_core_type(c, str_lit("Type_Info_Relative_Slice"));
+	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_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_soa_pointer      = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
 
 
@@ -2808,7 +2829,7 @@ gb_internal void init_core_type_info(Checker *c) {
 	t_type_info_bit_set_ptr          = alloc_type_pointer(t_type_info_bit_set);
 	t_type_info_bit_set_ptr          = alloc_type_pointer(t_type_info_bit_set);
 	t_type_info_simd_vector_ptr      = alloc_type_pointer(t_type_info_simd_vector);
 	t_type_info_simd_vector_ptr      = alloc_type_pointer(t_type_info_simd_vector);
 	t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
 	t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
-	t_type_info_relative_slice_ptr   = alloc_type_pointer(t_type_info_relative_slice);
+	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_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_soa_pointer_ptr      = alloc_type_pointer(t_type_info_soa_pointer);
 }
 }
@@ -2935,6 +2956,60 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
 	return false;
 	return false;
 }
 }
 
 
+gb_internal DECL_ATTRIBUTE_PROC(proc_group_attribute) {
+	if (name == ATTRIBUTE_USER_TAG_NAME) {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind != ExactValue_String) {
+			error(elem, "Expected a string value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_name") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_String) {
+			if (string_is_valid_identifier(ev.value_string)) {
+				ac->objc_name = ev.value_string;
+			} else {
+				error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+			}
+		} else {
+			error(elem, "Expected a string value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_is_class_method") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_Bool) {
+			ac->objc_is_class_method = ev.value_bool;
+		} else {
+			error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_type") {
+		if (value == nullptr) {
+			error(elem, "Expected a type for '%.*s'", LIT(name));
+		} else {
+			Type *objc_type = check_type(c, value);
+			if (objc_type != nullptr) {
+				if (!has_type_got_objc_class_attribute(objc_type)) {
+					gbString t = type_to_string(objc_type);
+					error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
+					gb_string_free(t);
+				} else {
+					ac->objc_type = objc_type;
+				}
+			}
+		}
+		return true;
+	} else if (name == "require_results") {
+		if (value != nullptr) {
+			error(elem, "Expected no value for '%.*s'", LIT(name));
+		}
+		ac->require_results = true;
+		return true;
+	}
+	return false;
+}
+
+
 gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 	if (name == ATTRIBUTE_USER_TAG_NAME) {
 	if (name == ATTRIBUTE_USER_TAG_NAME) {
 		ExactValue ev = check_decl_attribute_value(c, value);
 		ExactValue ev = check_decl_attribute_value(c, value);
@@ -3007,7 +3082,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			check_expr(c, &o, value);
 			check_expr(c, &o, value);
 			Entity *e = entity_of_node(o.expr);
 			Entity *e = entity_of_node(o.expr);
 			if (e != nullptr && e->kind == Entity_Procedure) {
 			if (e != nullptr && e->kind == Entity_Procedure) {
-				warning(elem, "'%.*s' is deprecated, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name));
+				error(elem, "'%.*s' is not allowed any more, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name));
 				if (ac->deferred_procedure.entity != nullptr) {
 				if (ac->deferred_procedure.entity != nullptr) {
 					error(elem, "Previous usage of a 'deferred_*' attribute");
 					error(elem, "Previous usage of a 'deferred_*' attribute");
 				}
 				}
@@ -4506,7 +4581,7 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
 		if (value != nullptr) {
 		if (value != nullptr) {
 			error(elem, "Expected no parameter for '%.*s'", LIT(name));
 			error(elem, "Expected no parameter for '%.*s'", LIT(name));
 		} else if (name == "force") {
 		} else if (name == "force") {
-			warning(elem, "'force' is deprecated and is identical to 'require'");
+			error(elem, "'force' was replaced with 'require'");
 		}
 		}
 		ac->require_declaration = true;
 		ac->require_declaration = true;
 		return true;
 		return true;
@@ -5904,7 +5979,11 @@ gb_internal void check_parsed_files(Checker *c) {
 	TIME_SECTION("check scope usage");
 	TIME_SECTION("check scope usage");
 	for (auto const &entry : c->info.files) {
 	for (auto const &entry : c->info.files) {
 		AstFile *f = entry.value;
 		AstFile *f = entry.value;
-		check_scope_usage(c, f->scope);
+		u64 vet_flags = build_context.vet_flags;
+		if (f->vet_flags_set) {
+			vet_flags = f->vet_flags;
+		}
+		check_scope_usage(c, f->scope, vet_flags);
 	}
 	}
 
 
 	TIME_SECTION("add basic type information");
 	TIME_SECTION("add basic type information");
@@ -6022,7 +6101,7 @@ gb_internal void check_parsed_files(Checker *c) {
 		while (mpsc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
 		while (mpsc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
 			if (c->info.entry_point == nullptr && node != nullptr) {
 			if (c->info.entry_point == nullptr && node != nullptr) {
 				if (node->file()->pkg->kind != Package_Runtime) {
 				if (node->file()->pkg->kind != Package_Runtime) {
-					warning(node, "usage of intrinsics.__entry_point will be a no-op");
+					error(node, "usage of intrinsics.__entry_point will be a no-op");
 				}
 				}
 			}
 			}
 		}
 		}

+ 3 - 2
src/checker.hpp

@@ -387,8 +387,6 @@ struct CheckerInfo {
 	BlockingMutex foreign_mutex; // NOT recursive
 	BlockingMutex foreign_mutex; // NOT recursive
 	StringMap<Entity *> foreigns;
 	StringMap<Entity *> foreigns;
 
 
-	// NOTE(bill): These are actually MPSC queues
-	// TODO(bill): Convert them to be MPSC queues
 	MPSCQueue<Entity *> definition_queue;
 	MPSCQueue<Entity *> definition_queue;
 	MPSCQueue<Entity *> entity_queue;
 	MPSCQueue<Entity *> entity_queue;
 	MPSCQueue<Entity *> required_global_variable_queue;
 	MPSCQueue<Entity *> required_global_variable_queue;
@@ -449,6 +447,9 @@ struct CheckerContext {
 	Ast *assignment_lhs_hint;
 	Ast *assignment_lhs_hint;
 };
 };
 
 
+gb_internal u64 check_vet_flags(CheckerContext *c);
+gb_internal u64 check_vet_flags(Ast *node);
+
 
 
 struct Checker {
 struct Checker {
 	Parser *    parser;
 	Parser *    parser;

+ 25 - 25
src/docs_format.cpp

@@ -59,31 +59,31 @@ struct OdinDocPosition {
 };
 };
 
 
 enum OdinDocTypeKind : u32 {
 enum OdinDocTypeKind : u32 {
-	OdinDocType_Invalid          = 0,
-	OdinDocType_Basic            = 1,
-	OdinDocType_Named            = 2,
-	OdinDocType_Generic          = 3,
-	OdinDocType_Pointer          = 4,
-	OdinDocType_Array            = 5,
-	OdinDocType_EnumeratedArray  = 6,
-	OdinDocType_Slice            = 7,
-	OdinDocType_DynamicArray     = 8,
-	OdinDocType_Map              = 9,
-	OdinDocType_Struct           = 10,
-	OdinDocType_Union            = 11,
-	OdinDocType_Enum             = 12,
-	OdinDocType_Tuple            = 13,
-	OdinDocType_Proc             = 14,
-	OdinDocType_BitSet           = 15,
-	OdinDocType_SimdVector       = 16,
-	OdinDocType_SOAStructFixed   = 17,
-	OdinDocType_SOAStructSlice   = 18,
-	OdinDocType_SOAStructDynamic = 19,
-	OdinDocType_RelativePointer  = 20,
-	OdinDocType_RelativeSlice    = 21,
-	OdinDocType_MultiPointer     = 22,
-	OdinDocType_Matrix           = 23,
-	OdinDocType_SoaPointer       = 24,
+	OdinDocType_Invalid              = 0,
+	OdinDocType_Basic                = 1,
+	OdinDocType_Named                = 2,
+	OdinDocType_Generic              = 3,
+	OdinDocType_Pointer              = 4,
+	OdinDocType_Array                = 5,
+	OdinDocType_EnumeratedArray      = 6,
+	OdinDocType_Slice                = 7,
+	OdinDocType_DynamicArray         = 8,
+	OdinDocType_Map                  = 9,
+	OdinDocType_Struct               = 10,
+	OdinDocType_Union                = 11,
+	OdinDocType_Enum                 = 12,
+	OdinDocType_Tuple                = 13,
+	OdinDocType_Proc                 = 14,
+	OdinDocType_BitSet               = 15,
+	OdinDocType_SimdVector           = 16,
+	OdinDocType_SOAStructFixed       = 17,
+	OdinDocType_SOAStructSlice       = 18,
+	OdinDocType_SOAStructDynamic     = 19,
+	OdinDocType_RelativePointer      = 20,
+	OdinDocType_RelativeMultiPointer = 21,
+	OdinDocType_MultiPointer         = 22,
+	OdinDocType_Matrix               = 23,
+	OdinDocType_SoaPointer           = 24,
 };
 };
 
 
 enum OdinDocTypeFlag_Basic : u32 {
 enum OdinDocTypeFlag_Basic : u32 {

+ 4 - 4
src/docs_writer.cpp

@@ -771,12 +771,12 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 		}
 		}
 		break;
 		break;
-	case Type_RelativeSlice:
-		doc_type.kind = OdinDocType_RelativeSlice;
+	case Type_RelativeMultiPointer:
+		doc_type.kind = OdinDocType_RelativeMultiPointer;
 		{
 		{
 			OdinDocTypeIndex types[2] = {};
 			OdinDocTypeIndex types[2] = {};
-			types[0] = odin_doc_type(w, type->RelativeSlice.slice_type);
-			types[1] = odin_doc_type(w, type->RelativeSlice.base_integer);
+			types[0] = odin_doc_type(w, type->RelativeMultiPointer.pointer_type);
+			types[1] = odin_doc_type(w, type->RelativeMultiPointer.base_integer);
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 		}
 		}
 		break;
 		break;

+ 11 - 8
src/entity.cpp

@@ -2,8 +2,6 @@ struct Scope;
 struct Checker;
 struct Checker;
 struct Type;
 struct Type;
 struct DeclInfo;
 struct DeclInfo;
-struct lbModule;
-struct lbProcedure;
 
 
 
 
 #define ENTITY_KINDS \
 #define ENTITY_KINDS \
@@ -183,8 +181,14 @@ struct Entity {
 
 
 	Entity *    aliased_of;
 	Entity *    aliased_of;
 
 
-	lbModule *   code_gen_module;
-	lbProcedure *code_gen_procedure;
+	union {
+		struct lbModule *code_gen_module;
+		struct cgModule *cg_module;
+	};
+	union {
+		struct lbProcedure *code_gen_procedure;
+		struct cgProcedure *cg_procedure;
+	};
 
 
 	u64         order_in_src;
 	u64         order_in_src;
 	String      deprecated_message;
 	String      deprecated_message;
@@ -287,7 +291,6 @@ gb_internal bool is_entity_kind_exported(EntityKind kind, bool allow_builtin = f
 }
 }
 
 
 gb_internal bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 gb_internal bool is_entity_exported(Entity *e, bool allow_builtin = false) {
-	// TODO(bill): Determine the actual exportation rules for imports of entities
 	GB_ASSERT(e != nullptr);
 	GB_ASSERT(e != nullptr);
 	if (!is_entity_kind_exported(e->kind, allow_builtin)) {
 	if (!is_entity_kind_exported(e->kind, allow_builtin)) {
 		return false;
 		return false;
@@ -401,7 +404,7 @@ gb_internal Entity *alloc_entity_array_elem(Scope *scope, Token token, Type *typ
 	return entity;
 	return entity;
 }
 }
 
 
-gb_internal Entity *alloc_entity_procedure(Scope *scope, Token token, Type *signature_type, u64 tags) {
+gb_internal Entity *alloc_entity_procedure(Scope *scope, Token token, Type *signature_type, u64 tags=0) {
 	Entity *entity = alloc_entity(Entity_Procedure, scope, token, signature_type);
 	Entity *entity = alloc_entity(Entity_Procedure, scope, token, signature_type);
 	entity->Procedure.tags = tags;
 	entity->Procedure.tags = tags;
 	return entity;
 	return entity;
@@ -418,7 +421,7 @@ gb_internal Entity *alloc_entity_import_name(Scope *scope, Token token, Type *ty
 	entity->ImportName.path = path;
 	entity->ImportName.path = path;
 	entity->ImportName.name = name;
 	entity->ImportName.name = name;
 	entity->ImportName.scope = import_scope;
 	entity->ImportName.scope = import_scope;
-	entity->state = EntityState_Resolved; // TODO(bill): Is this correct?
+	entity->state = EntityState_Resolved;
 	return entity;
 	return entity;
 }
 }
 
 
@@ -427,7 +430,7 @@ gb_internal Entity *alloc_entity_library_name(Scope *scope, Token token, Type *t
 	Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
 	Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
 	entity->LibraryName.paths = paths;
 	entity->LibraryName.paths = paths;
 	entity->LibraryName.name = name;
 	entity->LibraryName.name = name;
-	entity->state = EntityState_Resolved; // TODO(bill): Is this correct?
+	entity->state = EntityState_Resolved;
 	return entity;
 	return entity;
 }
 }
 
 

+ 2 - 2
src/error.cpp

@@ -411,7 +411,7 @@ gb_internal void error_line_va(char const *fmt, va_list va) {
 
 
 gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
 gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
 	mutex_lock(&global_error_collector.mutex);
 	mutex_lock(&global_error_collector.mutex);
-	global_error_collector.count++;
+	global_error_collector.count.fetch_add(1);
 	// NOTE(bill): Duplicate error, skip it
 	// NOTE(bill): Duplicate error, skip it
 	if (pos.line == 0) {
 	if (pos.line == 0) {
 		error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
@@ -425,7 +425,7 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li
 		error_out_va(fmt, va);
 		error_out_va(fmt, va);
 	}
 	}
 	mutex_unlock(&global_error_collector.mutex);
 	mutex_unlock(&global_error_collector.mutex);
-	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+	if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) {
 		gb_exit(1);
 		gb_exit(1);
 	}
 	}
 }
 }

+ 2 - 5
src/exact_value.cpp

@@ -26,8 +26,8 @@ enum ExactValueKind {
 	ExactValue_Complex    = 5,
 	ExactValue_Complex    = 5,
 	ExactValue_Quaternion = 6,
 	ExactValue_Quaternion = 6,
 	ExactValue_Pointer    = 7,
 	ExactValue_Pointer    = 7,
-	ExactValue_Compound   = 8,  // TODO(bill): Is this good enough?
-	ExactValue_Procedure  = 9, // TODO(bill): Is this good enough?
+	ExactValue_Compound   = 8,
+	ExactValue_Procedure  = 9,
 	ExactValue_Typeid     = 10,
 	ExactValue_Typeid     = 10,
 
 
 	ExactValue_Count,
 	ExactValue_Count,
@@ -101,7 +101,6 @@ gb_internal ExactValue exact_value_bool(bool b) {
 }
 }
 
 
 gb_internal ExactValue exact_value_string(String string) {
 gb_internal ExactValue exact_value_string(String string) {
-	// TODO(bill): Allow for numbers with underscores in them
 	ExactValue result = {ExactValue_String};
 	ExactValue result = {ExactValue_String};
 	result.value_string = string;
 	result.value_string = string;
 	return result;
 	return result;
@@ -702,7 +701,6 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
 	compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
 	compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
 }
 }
 
 
-// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
 gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
 gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
 	match_exact_values(&x, &y);
 	match_exact_values(&x, &y);
 
 
@@ -943,7 +941,6 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
 	case ExactValue_String: {
 	case ExactValue_String: {
 		String a = x.value_string;
 		String a = x.value_string;
 		String b = y.value_string;
 		String b = y.value_string;
-		// TODO(bill): gb_memcompare is used because the strings are UTF-8
 		switch (op) {
 		switch (op) {
 		case Token_CmpEq: return a == b;
 		case Token_CmpEq: return a == b;
 		case Token_NotEq: return a != b;
 		case Token_NotEq: return a != b;

+ 460 - 0
src/linker.cpp

@@ -0,0 +1,460 @@
+struct LinkerData {
+	BlockingMutex foreign_mutex;
+	PtrSet<Entity *> foreign_libraries_set;
+	Array<Entity *>  foreign_libraries;
+
+	Array<String> output_object_paths;
+	Array<String> output_temp_paths;
+	String   output_base;
+	String   output_name;
+};
+
+gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
+
+gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
+	gbAllocator ha = heap_allocator();
+	array_init(&ld->output_object_paths, ha);
+	array_init(&ld->output_temp_paths,   ha);
+	array_init(&ld->foreign_libraries,   ha, 0, 1024);
+	ptr_set_init(&ld->foreign_libraries_set, 1024);
+
+	if (build_context.out_filepath.len == 0) {
+		ld->output_name = remove_directory_from_path(init_fullpath);
+		ld->output_name = remove_extension_from_path(ld->output_name);
+		ld->output_name = string_trim_whitespace(ld->output_name);
+		if (ld->output_name.len == 0) {
+			ld->output_name = info->init_scope->pkg->name;
+		}
+		ld->output_base = ld->output_name;
+	} else {
+		ld->output_name = build_context.out_filepath;
+		ld->output_name = string_trim_whitespace(ld->output_name);
+		if (ld->output_name.len == 0) {
+			ld->output_name = info->init_scope->pkg->name;
+		}
+		isize pos = string_extension_position(ld->output_name);
+		if (pos < 0) {
+			ld->output_base = ld->output_name;
+		} else {
+			ld->output_base = substring(ld->output_name, 0, pos);
+		}
+	}
+
+	ld->output_base = path_to_full_path(ha, ld->output_base);
+
+}
+
+gb_internal i32 linker_stage(LinkerData *gen) {
+	i32 result = 0;
+	Timings *timings = &global_timings;
+
+	String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+	debugf("Linking %.*s\n", LIT(output_filename));
+
+	// TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
+
+	if (is_arch_wasm()) {
+		timings_start_section(timings, str_lit("wasm-ld"));
+
+	#if defined(GB_SYSTEM_WINDOWS)
+		result = system_exec_command_line_app("wasm-ld",
+			"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+			LIT(build_context.ODIN_ROOT),
+			LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+	#else
+		result = system_exec_command_line_app("wasm-ld",
+			"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+			LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+	#endif
+		return result;
+	}
+
+	if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+#if defined(GB_SYSTEM_UNIX)
+		result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+			LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+#else
+		gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
+			LIT(target_os_names[build_context.metrics.os]),
+			LIT(target_arch_names[build_context.metrics.arch])
+		);
+#endif
+	} else if (build_context.cross_compiling && build_context.different_os) {
+		gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
+			LIT(target_os_names[build_context.metrics.os]),
+			LIT(target_arch_names[build_context.metrics.arch])
+		);
+		build_context.keep_object_files = true;
+	} else {
+	#if defined(GB_SYSTEM_WINDOWS)
+		bool is_windows = true;
+	#else
+		bool is_windows = false;
+	#endif
+	#if defined(GB_SYSTEM_OSX)
+		bool is_osx = true;
+	#else
+		bool is_osx = false;
+	#endif
+
+
+		if (is_windows) {
+			String section_name = str_lit("msvc-link");
+			if (build_context.use_lld) {
+				section_name = str_lit("lld-link");
+			}
+			timings_start_section(timings, section_name);
+
+			gbString lib_str = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(lib_str));
+
+			gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
+			defer (gb_string_free(link_settings));
+
+			// Add library search paths.
+			if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
+				String path = {};
+				auto add_path = [&](String path) {
+					if (path[path.len-1] == '\\') {
+						path.len -= 1;
+					}
+					link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
+				};
+				add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
+				add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
+				add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
+			}
+
+
+			StringSet libs = {};
+			string_set_init(&libs, 64);
+			defer (string_set_destroy(&libs));
+
+			StringSet asm_files = {};
+			string_set_init(&asm_files, 64);
+			defer (string_set_destroy(&asm_files));
+
+			for (Entity *e : gen->foreign_libraries) {
+				GB_ASSERT(e->kind == Entity_LibraryName);
+				for_array(i, e->LibraryName.paths) {
+					String lib = string_trim_whitespace(e->LibraryName.paths[i]);
+					// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
+					// we will never uses these strings afterwards
+					string_to_lower(&lib);
+					if (lib.len == 0) {
+						continue;
+					}
+
+					if (has_asm_extension(lib)) {
+						if (!string_set_update(&asm_files, lib)) {
+							String asm_file = asm_files.entries[i].value;
+							String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
+
+							result = system_exec_command_line_app("nasm",
+								"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
+								"-f win64 "
+								"-o \"%.*s\" "
+								"%.*s "
+								"",
+								LIT(build_context.ODIN_ROOT), LIT(asm_file),
+								LIT(obj_file),
+								LIT(build_context.extra_assembler_flags)
+							);
+
+							if (result) {
+								return result;
+							}
+							array_add(&gen->output_object_paths, obj_file);
+						}
+					} else {
+						if (!string_set_update(&libs, lib)) {
+							lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
+						}
+					}
+				}
+			}
+
+			for (Entity *e : gen->foreign_libraries) {
+				GB_ASSERT(e->kind == Entity_LibraryName);
+				if (e->LibraryName.extra_linker_flags.len != 0) {
+					lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
+				}
+			}
+
+			if (build_context.build_mode == BuildMode_DynamicLibrary) {
+				link_settings = gb_string_append_fmt(link_settings, " /DLL");
+			} else {
+				link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
+			}
+
+			if (build_context.pdb_filepath != "") {
+				String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
+				link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
+			}
+
+			if (build_context.no_crt) {
+				link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
+			} else {
+				link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
+			}
+
+			if (build_context.ODIN_DEBUG) {
+				link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
+			}
+
+			gbString object_files = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(object_files));
+			for (String const &object_path : gen->output_object_paths) {
+				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+			}
+
+			String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
+			defer (gb_free(heap_allocator(), vs_exe_path.text));
+
+			String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
+			defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
+
+			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
+			if (!build_context.use_lld) { // msvc
+				String res_path = {};
+				defer (gb_free(heap_allocator(), res_path.text));
+				if (build_context.has_resource) {
+					String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
+					res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
+					gb_free(heap_allocator(), temp_res_path.text);
+
+					String rc_path  = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+					defer (gb_free(heap_allocator(), rc_path.text));
+
+					result = system_exec_command_line_app("msvc-link",
+						"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+						LIT(windows_sdk_bin_path),
+						LIT(res_path),
+						LIT(rc_path)
+					);
+
+					if (result) {
+						return result;
+					}
+				}
+
+				switch (build_context.build_mode) {
+				case BuildMode_Executable:
+					link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
+					break;
+				}
+
+				result = system_exec_command_line_app("msvc-link",
+					"\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
+					"/nologo /incremental:no /opt:ref /subsystem:%s "
+					"%.*s "
+					"%.*s "
+					"%s "
+					"",
+					LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
+					link_settings,
+					subsystem_str,
+					LIT(build_context.link_flags),
+					LIT(build_context.extra_linker_flags),
+					lib_str
+				);
+				if (result) {
+					return result;
+				}
+			} else { // lld
+				result = system_exec_command_line_app("msvc-lld-link",
+					"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
+					"/nologo /incremental:no /opt:ref /subsystem:%s "
+					"%.*s "
+					"%.*s "
+					"%s "
+					"",
+					LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
+					link_settings,
+					subsystem_str,
+					LIT(build_context.link_flags),
+					LIT(build_context.extra_linker_flags),
+					lib_str
+				);
+
+				if (result) {
+					return result;
+				}
+			}
+		} else {
+			timings_start_section(timings, str_lit("ld-link"));
+
+			// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
+			char cwd[256];
+			#if !defined(GB_SYSTEM_WINDOWS)
+			getcwd(&cwd[0], 256);
+			#endif
+			//printf("%s\n", cwd);
+
+			// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
+			//                files can be passed with -l:
+			gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+			defer (gb_string_free(lib_str));
+
+			StringSet libs = {};
+			string_set_init(&libs, 64);
+			defer (string_set_destroy(&libs));
+
+			for (Entity *e : gen->foreign_libraries) {
+				GB_ASSERT(e->kind == Entity_LibraryName);
+				for (String lib : e->LibraryName.paths) {
+					lib = string_trim_whitespace(lib);
+					if (lib.len == 0) {
+						continue;
+					}
+					if (string_set_update(&libs, lib)) {
+						continue;
+					}
+
+					// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
+					//   This allows you to specify '-f' in a #foreign_system_library,
+					//   without having to implement any new syntax specifically for MacOS.
+					if (build_context.metrics.os == TargetOs_darwin) {
+						if (string_ends_with(lib, str_lit(".framework"))) {
+							// framework thingie
+							String lib_name = lib;
+							lib_name = remove_extension_from_path(lib_name);
+							lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
+						} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
+							// For:
+							// object
+							// dynamic lib
+							// static libs, absolute full path relative to the file in which the lib was imported from
+							lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+						} else {
+							// dynamic or static system lib, just link regularly searching system library paths
+							lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+						}
+					} else {
+						// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
+						//                since those are statically linked to at link time. shared libraries (.so) has to be
+						//                available at runtime wherever the executable is run, so we make require those to be
+						//                local to the executable (unless the system collection is used, in which case we search
+						//                the system library paths for the library file).
+						if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
+							// static libs and object files, absolute full path relative to the file in which the lib was imported from
+							lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
+						} else if (string_ends_with(lib, str_lit(".so"))) {
+							// dynamic lib, relative path to executable
+							// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
+							//                at runtime to the executable
+							lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
+						} else {
+							// dynamic or static system lib, just link regularly searching system library paths
+							lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+						}
+					}
+				}
+			}
+
+			for (Entity *e : gen->foreign_libraries) {
+				GB_ASSERT(e->kind == Entity_LibraryName);
+				if (e->LibraryName.extra_linker_flags.len != 0) {
+					lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
+				}
+			}
+
+			gbString object_files = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(object_files));
+			for (String object_path : gen->output_object_paths) {
+				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+			}
+
+			gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
+
+			if (build_context.no_crt) {
+				link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
+			}
+
+			// NOTE(dweiler): We use clang as a frontend for the linker as there are
+			// other runtime and compiler support libraries that need to be linked in
+			// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
+			// These are not always typically inside /lib, /lib64, or /usr versions
+			// of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
+			// the distribution of Linux even. The gcc or clang specs is the only
+			// reliable way to query this information to call ld directly.
+			if (build_context.build_mode == BuildMode_DynamicLibrary) {
+				// NOTE(dweiler): Let the frontend know we're building a shared library
+				// so it doesn't generate symbols which cannot be relocated.
+				link_settings = gb_string_appendc(link_settings, "-shared ");
+
+				// NOTE(dweiler): _odin_entry_point must be called at initialization
+				// time of the shared object, similarly, _odin_exit_point must be called
+				// at deinitialization. We can pass both -init and -fini to the linker by
+				// using a comma separated list of arguments to -Wl.
+				//
+				// This previously used ld but ld cannot actually build a shared library
+				// correctly this way since all the other dependencies provided implicitly
+				// by the compiler frontend are still needed and most of the command
+				// line arguments prepared previously are incompatible with ld.
+				if (build_context.metrics.os == TargetOs_darwin) {
+					link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
+					// NOTE(weshardee): __odin_exit_point should also be added, but -fini
+					// does not exist on MacOS
+				} else {
+					link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
+					link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
+				}
+
+			} else if (build_context.metrics.os != TargetOs_openbsd) {
+				// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
+				link_settings = gb_string_appendc(link_settings, "-no-pie ");
+			}
+
+			gbString platform_lib_str = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(platform_lib_str));
+			if (build_context.metrics.os == TargetOs_darwin) {
+				platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
+			} else {
+				platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
+			}
+
+			if (build_context.metrics.os == TargetOs_darwin) {
+				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
+				if (build_context.minimum_os_version_string.len) {
+					link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
+				} else if (build_context.metrics.arch == TargetArch_arm64) {
+					link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0  ");
+				} else {
+					link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
+				}
+				// This points the linker to where the entry point is
+				link_settings = gb_string_appendc(link_settings, " -e _main ");
+			}
+
+			gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
+			defer (gb_string_free(link_command_line));
+
+			link_command_line = gb_string_appendc(link_command_line, object_files);
+			link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
+			link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
+			link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
+			link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
+			link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
+			link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
+
+			result = system_exec_command_line_app("ld-link", link_command_line);
+
+			if (result) {
+				return result;
+			}
+
+			if (is_osx && build_context.ODIN_DEBUG) {
+				// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
+				// to the symbols in the object file
+				result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
+
+				if (result) {
+					return result;
+				}
+			}
+		}
+	}
+
+	return result;
+}

+ 3 - 10
src/llvm_backend.hpp

@@ -69,7 +69,6 @@ enum lbAddrKind {
 	lbAddr_SoaVariable,
 	lbAddr_SoaVariable,
 
 
 	lbAddr_RelativePointer,
 	lbAddr_RelativePointer,
-	lbAddr_RelativeSlice,
 
 
 	lbAddr_Swizzle,
 	lbAddr_Swizzle,
 	lbAddr_SwizzleLarge,
 	lbAddr_SwizzleLarge,
@@ -190,13 +189,9 @@ struct lbModule {
 	LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
 	LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
 };
 };
 
 
-struct lbGenerator {
+struct lbGenerator : LinkerData {
 	CheckerInfo *info;
 	CheckerInfo *info;
 
 
-	Array<String> output_object_paths;
-	Array<String> output_temp_paths;
-	String   output_base;
-	String   output_name;
 	PtrMap<void *, lbModule *> modules; // key is `AstPackage *` (`void *` is used for future use)
 	PtrMap<void *, lbModule *> modules; // key is `AstPackage *` (`void *` is used for future use)
 	PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; 
 	PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; 
 	lbModule default_module;
 	lbModule default_module;
@@ -204,10 +199,6 @@ struct lbGenerator {
 	RecursiveMutex anonymous_proc_lits_mutex;
 	RecursiveMutex anonymous_proc_lits_mutex;
 	PtrMap<Ast *, lbProcedure *> anonymous_proc_lits; 
 	PtrMap<Ast *, lbProcedure *> anonymous_proc_lits; 
 
 
-	BlockingMutex foreign_mutex;
-	PtrSet<Entity *> foreign_libraries_set;
-	Array<Entity *>  foreign_libraries;
-
 	std::atomic<u32> global_array_index;
 	std::atomic<u32> global_array_index;
 	std::atomic<u32> global_generated_index;
 	std::atomic<u32> global_generated_index;
 
 
@@ -346,7 +337,9 @@ struct lbProcedure {
 };
 };
 
 
 
 
+#ifndef ABI_PKG_NAME_SEPARATOR
 #define ABI_PKG_NAME_SEPARATOR "."
 #define ABI_PKG_NAME_SEPARATOR "."
+#endif
 
 
 
 
 #if !ODIN_LLVM_MINIMUM_VERSION_14
 #if !ODIN_LLVM_MINIMUM_VERSION_14

+ 67 - 69
src/llvm_backend_const.cpp

@@ -567,7 +567,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
 
 
 		}
 		}
 	} else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
 	} else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
-		if (is_type_rune_array(type) && value.kind == ExactValue_String) {
+		if (is_type_rune_array(type)) {
 			i64 count  = type->Array.count;
 			i64 count  = type->Array.count;
 			Type *elem = type->Array.elem;
 			Type *elem = type->Array.elem;
 			LLVMTypeRef et = lb_type(m, elem);
 			LLVMTypeRef et = lb_type(m, elem);
@@ -1036,86 +1036,84 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
 			LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count);
 			LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count);
 			bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count);
 			bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count);
 
 
-			if (cl->elems.count > 0) {
-				if (cl->elems[0]->kind == Ast_FieldValue) {
-					isize elem_count = cl->elems.count;
-					for (isize i = 0; i < elem_count; i++) {
-						ast_node(fv, FieldValue, cl->elems[i]);
-						String name = fv->field->Ident.token.string;
+			if (cl->elems[0]->kind == Ast_FieldValue) {
+				isize elem_count = cl->elems.count;
+				for (isize i = 0; i < elem_count; i++) {
+					ast_node(fv, FieldValue, cl->elems[i]);
+					String name = fv->field->Ident.token.string;
 
 
-						TypeAndValue tav = fv->value->tav;
-						GB_ASSERT(tav.mode != Addressing_Invalid);
+					TypeAndValue tav = fv->value->tav;
+					GB_ASSERT(tav.mode != Addressing_Invalid);
 
 
-						Selection sel = lookup_field(type, name, false);
-						GB_ASSERT(!sel.indirect);
+					Selection sel = lookup_field(type, name, false);
+					GB_ASSERT(!sel.indirect);
 
 
-						Entity *f = type->Struct.fields[sel.index[0]];
-						i32 index = field_remapping[f->Variable.field_index];
-						if (elem_type_can_be_constant(f->type)) {
-							if (sel.index.count == 1) {
-								values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
+					Entity *f = type->Struct.fields[sel.index[0]];
+					i32 index = field_remapping[f->Variable.field_index];
+					if (elem_type_can_be_constant(f->type)) {
+						if (sel.index.count == 1) {
+							values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
+							visited[index] = true;
+						} else {
+							if (!visited[index]) {
+								values[index] = lb_const_value(m, f->type, {}, false).value;
 								visited[index] = true;
 								visited[index] = true;
-							} else {
-								if (!visited[index]) {
-									values[index] = lb_const_value(m, f->type, {}, false).value;
-									visited[index] = true;
-								}
-								unsigned idx_list_len = cast(unsigned)sel.index.count-1;
-								unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
-
-								if (lb_is_nested_possibly_constant(type, sel, fv->value)) {
-									bool is_constant = true;
-									Type *cv_type = f->type;
-									for (isize j = 1; j < sel.index.count; j++) {
-										i32 index = sel.index[j];
-										Type *cvt = base_type(cv_type);
-
-										if (cvt->kind == Type_Struct) {
-											if (cvt->Struct.is_raw_union) {
-												// sanity check which should have been caught by `lb_is_nested_possibly_constant`
-												is_constant = false;
-												break;
-											}
-											cv_type = cvt->Struct.fields[index]->type;
-
-											if (is_type_struct(cvt)) {
-												auto cv_field_remapping = lb_get_struct_remapping(m, cvt);
-												unsigned remapped_index = cast(unsigned)cv_field_remapping[index];
-												idx_list[j-1] = remapped_index;
-											} else {
-												idx_list[j-1] = cast(unsigned)index;
-											}
-										} else if (cvt->kind == Type_Array) {
-											cv_type = cvt->Array.elem;
+							}
+							unsigned idx_list_len = cast(unsigned)sel.index.count-1;
+							unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
+
+							if (lb_is_nested_possibly_constant(type, sel, fv->value)) {
+								bool is_constant = true;
+								Type *cv_type = f->type;
+								for (isize j = 1; j < sel.index.count; j++) {
+									i32 index = sel.index[j];
+									Type *cvt = base_type(cv_type);
+
+									if (cvt->kind == Type_Struct) {
+										if (cvt->Struct.is_raw_union) {
+											// sanity check which should have been caught by `lb_is_nested_possibly_constant`
+											is_constant = false;
+											break;
+										}
+										cv_type = cvt->Struct.fields[index]->type;
 
 
-											idx_list[j-1] = cast(unsigned)index;
+										if (is_type_struct(cvt)) {
+											auto cv_field_remapping = lb_get_struct_remapping(m, cvt);
+											unsigned remapped_index = cast(unsigned)cv_field_remapping[index];
+											idx_list[j-1] = remapped_index;
 										} else {
 										} else {
-											GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type));
+											idx_list[j-1] = cast(unsigned)index;
 										}
 										}
+									} else if (cvt->kind == Type_Array) {
+										cv_type = cvt->Array.elem;
+
+										idx_list[j-1] = cast(unsigned)index;
+									} else {
+										GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type));
 									}
 									}
-									if (is_constant) {
-										LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
-										GB_ASSERT(LLVMIsConstant(elem_value));
-										values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
-									}
+								}
+								if (is_constant) {
+									LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
+									GB_ASSERT(LLVMIsConstant(elem_value));
+									values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
 								}
 								}
 							}
 							}
 						}
 						}
 					}
 					}
-				} else {
-					for_array(i, cl->elems) {
-						Entity *f = type->Struct.fields[i];
-						TypeAndValue tav = cl->elems[i]->tav;
-						ExactValue val = {};
-						if (tav.mode != Addressing_Invalid) {
-							val = tav.value;
-						}
-						
-						i32 index = field_remapping[f->Variable.field_index];
-						if (elem_type_can_be_constant(f->type)) {
-							values[index]  = lb_const_value(m, f->type, val, allow_local).value;
-							visited[index] = true;
-						}
+				}
+			} else {
+				for_array(i, cl->elems) {
+					Entity *f = type->Struct.fields[i];
+					TypeAndValue tav = cl->elems[i]->tav;
+					ExactValue val = {};
+					if (tav.mode != Addressing_Invalid) {
+						val = tav.value;
+					}
+
+					i32 index = field_remapping[f->Variable.field_index];
+					if (elem_type_can_be_constant(f->type)) {
+						values[index]  = lb_const_value(m, f->type, val, allow_local).value;
+						visited[index] = true;
 					}
 					}
 				}
 				}
 			}
 			}

+ 5 - 12
src/llvm_backend_debug.cpp

@@ -442,19 +442,12 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 		gbString name = type_to_string(type, temporary_allocator());
 		gbString name = type_to_string(type, temporary_allocator());
 		return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
 		return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
 	}
 	}
+	case Type_RelativeMultiPointer: {
+		LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativeMultiPointer.base_integer);
+		gbString name = type_to_string(type, temporary_allocator());
+		return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
+	}
 
 
-	case Type_RelativeSlice:
-		{
-			unsigned element_count = 0;
-			LLVMMetadataRef elements[2] = {};
-			Type *base_integer = type->RelativeSlice.base_integer;
-			unsigned base_bits = cast(unsigned)(8*type_size_of(base_integer));
-			elements[0] = lb_debug_struct_field(m, str_lit("data_offset"), base_integer, 0);
-			elements[1] = lb_debug_struct_field(m, str_lit("len"), base_integer, base_bits);
-			gbString name = type_to_string(type, temporary_allocator());
-			return LLVMDIBuilderCreateStructType(m->debug_builder, nullptr, name, gb_string_length(name), nullptr, 0, 2*base_bits, base_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
-		}
-		
 	case Type_Matrix: {
 	case Type_Matrix: {
 		LLVMMetadataRef subscripts[1] = {};
 		LLVMMetadataRef subscripts[1] = {};
 		subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
 		subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,

+ 27 - 17
src/llvm_backend_expr.cpp

@@ -2863,7 +2863,6 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 	ast_node(ue, UnaryExpr, expr);
 	ast_node(ue, UnaryExpr, expr);
 	auto tv = type_and_value_of_expr(expr);
 	auto tv = type_and_value_of_expr(expr);
 
 
-
 	Ast *ue_expr = unparen_expr(ue->expr);
 	Ast *ue_expr = unparen_expr(ue->expr);
 	if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_type_tuple(tv.type)) {
 	if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_type_tuple(tv.type)) {
 		Type *tuple = tv.type;
 		Type *tuple = tv.type;
@@ -3803,25 +3802,32 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
 		lbValue v = {};
 		lbValue v = {};
 
 
 		LLVMValueRef indices[1] = {index.value};
 		LLVMValueRef indices[1] = {index.value};
-		v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "foo");
+		v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "");
 		v.type = alloc_type_pointer(t->MultiPointer.elem);
 		v.type = alloc_type_pointer(t->MultiPointer.elem);
 		return lb_addr(v);
 		return lb_addr(v);
 	}
 	}
 
 
-	case Type_RelativeSlice: {
-		lbAddr slice_addr = {};
+	case Type_RelativeMultiPointer: {
+		lbAddr rel_ptr_addr = {};
 		if (deref) {
 		if (deref) {
-			slice_addr = lb_addr(lb_build_expr(p, ie->expr));
+			lbValue rel_ptr_ptr = lb_build_expr(p, ie->expr);
+			rel_ptr_addr = lb_addr(rel_ptr_ptr);
 		} else {
 		} else {
-			slice_addr = lb_build_addr(p, ie->expr);
+			rel_ptr_addr = lb_build_addr(p, ie->expr);
 		}
 		}
-		lbValue slice = lb_addr_load(p, slice_addr);
+		lbValue rel_ptr = lb_relative_pointer_to_pointer(p, rel_ptr_addr);
 
 
-		lbValue elem = lb_slice_elem(p, slice);
-		lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
-		lbValue len = lb_slice_len(p, slice);
-		lb_emit_bounds_check(p, ast_token(ie->index), index, len);
-		lbValue v = lb_emit_ptr_offset(p, elem, index);
+		lbValue index = lb_build_expr(p, ie->index);
+		index = lb_emit_conv(p, index, t_int);
+		lbValue v = {};
+
+		Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
+		GB_ASSERT(pointer_type->kind == Type_MultiPointer);
+		Type *elem = pointer_type->MultiPointer.elem;
+
+		LLVMValueRef indices[1] = {index.value};
+		v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem), rel_ptr.value, indices, 1, "");
+		v.type = alloc_type_pointer(elem);
 		return lb_addr(v);
 		return lb_addr(v);
 	}
 	}
 
 
@@ -3925,8 +3931,11 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
 		return slice;
 		return slice;
 	}
 	}
 
 
-	case Type_RelativeSlice:
-		GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load");
+	case Type_RelativePointer:
+		GB_PANIC("TODO(bill): Type_RelativePointer should be handled above already on the lb_addr_load");
+		break;
+	case Type_RelativeMultiPointer:
+		GB_PANIC("TODO(bill): Type_RelativeMultiPointer should be handled above already on the lb_addr_load");
 		break;
 		break;
 
 
 	case Type_DynamicArray: {
 	case Type_DynamicArray: {
@@ -3996,7 +4005,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
 	}
 	}
 
 
 	case Type_Basic: {
 	case Type_Basic: {
-		GB_ASSERT(type == t_string);
+		GB_ASSERT_MSG(type == t_string, "got %s", type_to_string(type));
 		lbValue len = lb_string_len(p, base);
 		lbValue len = lb_string_len(p, base);
 		if (high.value == nullptr) high = len;
 		if (high.value == nullptr) high = len;
 
 
@@ -4171,7 +4180,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 
 
 				// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
 				// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
 				if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
 				if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
-					GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
+					GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet));
 
 
 					lb_emit_store_union_variant(p, gep, field_expr, fet);
 					lb_emit_store_union_variant(p, gep, field_expr, fet);
 				} else {
 				} else {
@@ -4519,8 +4528,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 			Selection sel = lookup_field(type, selector, false);
 			Selection sel = lookup_field(type, selector, false);
 			GB_ASSERT(sel.entity != nullptr);
 			GB_ASSERT(sel.entity != nullptr);
 			if (sel.pseudo_field) {
 			if (sel.pseudo_field) {
-				GB_ASSERT(sel.entity->kind == Entity_Procedure);
+				GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
 				Entity *e = entity_of_node(sel_node);
 				Entity *e = entity_of_node(sel_node);
+				GB_ASSERT(e->kind == Entity_Procedure);
 				return lb_addr(lb_find_value_from_entity(p->module, e));
 				return lb_addr(lb_find_value_from_entity(p->module, e));
 			}
 			}
 
 

+ 87 - 155
src/llvm_backend_general.cpp

@@ -103,37 +103,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
 	}
 	}
 
 
 	String init_fullpath = c->parser->init_fullpath;
 	String init_fullpath = c->parser->init_fullpath;
-
-	if (build_context.out_filepath.len == 0) {
-		gen->output_name = remove_directory_from_path(init_fullpath);
-		gen->output_name = remove_extension_from_path(gen->output_name);
-		gen->output_name = string_trim_whitespace(gen->output_name);
-		if (gen->output_name.len == 0) {
-			gen->output_name = c->info.init_scope->pkg->name;
-		}
-		gen->output_base = gen->output_name;
-	} else {
-		gen->output_name = build_context.out_filepath;
-		gen->output_name = string_trim_whitespace(gen->output_name);
-		if (gen->output_name.len == 0) {
-			gen->output_name = c->info.init_scope->pkg->name;
-		}
-		isize pos = string_extension_position(gen->output_name);
-		if (pos < 0) {
-			gen->output_base = gen->output_name;
-		} else {
-			gen->output_base = substring(gen->output_name, 0, pos);
-		}
-	}
-	gbAllocator ha = heap_allocator();
-	array_init(&gen->output_object_paths, ha);
-	array_init(&gen->output_temp_paths, ha);
-
-	gen->output_base = path_to_full_path(ha, gen->output_base);
-
-	gbString output_file_path = gb_string_make_length(ha, gen->output_base.text, gen->output_base.len);
-	output_file_path = gb_string_appendc(output_file_path, ".obj");
-	defer (gb_string_free(output_file_path));
+	linker_data_init(gen, &c->info, init_fullpath);
 
 
 	gen->info = &c->info;
 	gen->info = &c->info;
 
 
@@ -141,10 +111,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
 	map_init(&gen->modules_through_ctx, gen->info->packages.count*2);
 	map_init(&gen->modules_through_ctx, gen->info->packages.count*2);
 	map_init(&gen->anonymous_proc_lits, 1024);
 	map_init(&gen->anonymous_proc_lits, 1024);
 
 
-
-	array_init(&gen->foreign_libraries,       heap_allocator(), 0, 1024);
-	ptr_set_init(&gen->foreign_libraries_set, 1024);
-
 	if (USE_SEPARATE_MODULES) {
 	if (USE_SEPARATE_MODULES) {
 		for (auto const &entry : gen->info->packages) {
 		for (auto const &entry : gen->info->packages) {
 			AstPackage *pkg = entry.value;
 			AstPackage *pkg = entry.value;
@@ -383,9 +349,10 @@ gb_internal lbAddr lb_addr(lbValue addr) {
 	if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) {
 	if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) {
 		GB_ASSERT(is_type_pointer(addr.type));
 		GB_ASSERT(is_type_pointer(addr.type));
 		v.kind = lbAddr_RelativePointer;
 		v.kind = lbAddr_RelativePointer;
-	} else if (addr.type != nullptr && is_type_relative_slice(type_deref(addr.type))) {
-		GB_ASSERT(is_type_pointer(addr.type));
-		v.kind = lbAddr_RelativeSlice;
+	} else if (addr.type != nullptr && is_type_relative_multi_pointer(type_deref(addr.type))) {
+		GB_ASSERT(is_type_pointer(addr.type) ||
+		          is_type_multi_pointer(addr.type));
+		v.kind = lbAddr_RelativePointer;
 	}
 	}
 	return v;
 	return v;
 }
 }
@@ -458,6 +425,43 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) {
 	return type_deref(addr.addr.type);
 	return type_deref(addr.addr.type);
 }
 }
 
 
+
+gb_internal lbValue lb_relative_pointer_to_pointer(lbProcedure *p, lbAddr const &addr) {
+	GB_ASSERT(addr.kind == lbAddr_RelativePointer);
+
+	Type *t = base_type(lb_addr_type(addr));
+	GB_ASSERT(is_type_relative_pointer(t) || is_type_relative_multi_pointer(t));
+
+	Type *pointer_type = nullptr;
+	Type *base_integer = nullptr;
+	if (t->kind == Type_RelativePointer) {
+		pointer_type = t->RelativePointer.pointer_type;
+		base_integer = t->RelativePointer.base_integer;
+	} else if (t->kind == Type_RelativeMultiPointer) {
+		pointer_type = t->RelativeMultiPointer.pointer_type;
+		base_integer = t->RelativeMultiPointer.base_integer;
+	}
+
+	lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
+	lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
+	offset = lb_emit_load(p, offset);
+
+	if (!is_type_unsigned(base_integer)) {
+		offset = lb_emit_conv(p, offset, t_i64);
+	}
+	offset = lb_emit_conv(p, offset, t_uintptr);
+	lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
+	absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
+
+	lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
+
+	// NOTE(bill): nil check
+	lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
+	lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr);
+	return final_ptr;
+}
+
+
 gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
 gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
 	if (addr.addr.value == nullptr) {
 	if (addr.addr.value == nullptr) {
 		GB_PANIC("Illegal addr -> nullptr");
 		GB_PANIC("Illegal addr -> nullptr");
@@ -468,28 +472,8 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
 	case lbAddr_Map:
 	case lbAddr_Map:
 		return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
 		return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
 
 
-	case lbAddr_RelativePointer: {
-		Type *rel_ptr = base_type(lb_addr_type(addr));
-		GB_ASSERT(rel_ptr->kind == Type_RelativePointer);
-
-		lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
-		lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
-		offset = lb_emit_load(p, offset);
-
-		if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
-			offset = lb_emit_conv(p, offset, t_i64);
-		}
-		offset = lb_emit_conv(p, offset, t_uintptr);
-		lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
-		absolute_ptr = lb_emit_conv(p, absolute_ptr, rel_ptr->RelativePointer.pointer_type);
-
-		lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer));
-
-		// NOTE(bill): nil check
-		lbValue nil_ptr = lb_const_nil(p->module, rel_ptr->RelativePointer.pointer_type);
-		lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr);
-		return final_ptr;
-	}
+	case lbAddr_RelativePointer:
+		return lb_relative_pointer_to_pointer(p, addr);
 
 
 	case lbAddr_SoaVariable:
 	case lbAddr_SoaVariable:
 		// TODO(bill): FIX THIS HACK
 		// TODO(bill): FIX THIS HACK
@@ -511,6 +495,9 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
 
 
 gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) {
 gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) {
 	lbAddr addr = lb_build_addr(p, expr);
 	lbAddr addr = lb_build_addr(p, expr);
+	if (addr.kind == lbAddr_RelativePointer) {
+		return addr.addr;
+	}
 	return lb_addr_get_ptr(p, addr);
 	return lb_addr_get_ptr(p, addr);
 }
 }
 
 
@@ -719,9 +706,20 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
 
 
 	if (addr.kind == lbAddr_RelativePointer) {
 	if (addr.kind == lbAddr_RelativePointer) {
 		Type *rel_ptr = base_type(lb_addr_type(addr));
 		Type *rel_ptr = base_type(lb_addr_type(addr));
-		GB_ASSERT(rel_ptr->kind == Type_RelativePointer);
+		GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
+		          rel_ptr->kind == Type_RelativeMultiPointer);
+		Type *pointer_type = nullptr;
+		Type *base_integer = nullptr;
+
+		if (rel_ptr->kind == Type_RelativePointer) {
+			pointer_type = rel_ptr->RelativePointer.pointer_type;
+			base_integer = rel_ptr->RelativePointer.base_integer;
+		} else if (rel_ptr->kind == Type_RelativeMultiPointer) {
+			pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
+			base_integer = rel_ptr->RelativeMultiPointer.base_integer;
+		}
 
 
-		value = lb_emit_conv(p, value, rel_ptr->RelativePointer.pointer_type);
+		value = lb_emit_conv(p, value, pointer_type);
 
 
 		GB_ASSERT(is_type_pointer(addr.addr.type));
 		GB_ASSERT(is_type_pointer(addr.addr.type));
 		lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
 		lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
@@ -730,54 +728,20 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
 		offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
 		offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
 		offset.type = t_uintptr;
 		offset.type = t_uintptr;
 
 
-		if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
+		if (!is_type_unsigned(base_integer)) {
 			offset = lb_emit_conv(p, offset, t_i64);
 			offset = lb_emit_conv(p, offset, t_i64);
 		}
 		}
-		offset = lb_emit_conv(p, offset, rel_ptr->RelativePointer.base_integer);
+		offset = lb_emit_conv(p, offset, base_integer);
 
 
-		lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
+		lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(base_integer));
 		offset = lb_emit_select(p,
 		offset = lb_emit_select(p,
 			lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
 			lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
-			lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer),
+			lb_const_nil(p->module, base_integer),
 			offset
 			offset
 		);
 		);
 		LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
 		LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
 		return;
 		return;
 
 
-	} else if (addr.kind == lbAddr_RelativeSlice) {
-		Type *rel_ptr = base_type(lb_addr_type(addr));
-		GB_ASSERT(rel_ptr->kind == Type_RelativeSlice);
-
-		value = lb_emit_conv(p, value, rel_ptr->RelativeSlice.slice_type);
-
-		GB_ASSERT(is_type_pointer(addr.addr.type));
-		lbValue ptr = lb_emit_conv(p, lb_emit_struct_ep(p, addr.addr, 0), t_uintptr);
-		lbValue val_ptr = lb_emit_conv(p, lb_slice_elem(p, value), t_uintptr);
-		lbValue offset = {};
-		offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
-		offset.type = t_uintptr;
-
-		if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
-			offset = lb_emit_conv(p, offset, t_i64);
-		}
-		offset = lb_emit_conv(p, offset, rel_ptr->RelativePointer.base_integer);
-
-
-		lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
-		offset = lb_emit_select(p,
-			lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
-			lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer),
-			offset
-		);
-		LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
-
-		lbValue len = lb_slice_len(p, value);
-		len = lb_emit_conv(p, len, rel_ptr->RelativePointer.base_integer);
-
-		lbValue len_ptr = lb_emit_struct_ep(p, addr.addr, 1);
-		LLVMBuildStore(p->builder, len.value, len_ptr.value);
-
-		return;
 	} else if (addr.kind == lbAddr_Map) {
 	} else if (addr.kind == lbAddr_Map) {
 		lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
 		lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
 		return;
 		return;
@@ -1054,67 +1018,43 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
 
 
 	if (addr.kind == lbAddr_RelativePointer) {
 	if (addr.kind == lbAddr_RelativePointer) {
 		Type *rel_ptr = base_type(lb_addr_type(addr));
 		Type *rel_ptr = base_type(lb_addr_type(addr));
-		GB_ASSERT(rel_ptr->kind == Type_RelativePointer);
+		Type *base_integer = nullptr;
+		Type *pointer_type = nullptr;
+		GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
+		          rel_ptr->kind == Type_RelativeMultiPointer);
+
+		if (rel_ptr->kind == Type_RelativePointer) {
+			base_integer = rel_ptr->RelativePointer.base_integer;
+			pointer_type = rel_ptr->RelativePointer.pointer_type;
+		} else if (rel_ptr->kind == Type_RelativeMultiPointer) {
+			base_integer = rel_ptr->RelativeMultiPointer.base_integer;
+			pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
+		}
 
 
 		lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
 		lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
-		lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
+		lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
 		offset = lb_emit_load(p, offset);
 		offset = lb_emit_load(p, offset);
 
 
 
 
-		if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
+		if (!is_type_unsigned(base_integer)) {
 			offset = lb_emit_conv(p, offset, t_i64);
 			offset = lb_emit_conv(p, offset, t_i64);
 		}
 		}
 		offset = lb_emit_conv(p, offset, t_uintptr);
 		offset = lb_emit_conv(p, offset, t_uintptr);
 		lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
 		lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
-		absolute_ptr = lb_emit_conv(p, absolute_ptr, rel_ptr->RelativePointer.pointer_type);
+		absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
 
 
-		lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer));
+		lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
 
 
 		// NOTE(bill): nil check
 		// NOTE(bill): nil check
-		lbValue nil_ptr = lb_const_nil(p->module, rel_ptr->RelativePointer.pointer_type);
+		lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
 		lbValue final_ptr = {};
 		lbValue final_ptr = {};
 		final_ptr.type = absolute_ptr.type;
 		final_ptr.type = absolute_ptr.type;
 		final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
 		final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
 
 
-		return lb_emit_load(p, final_ptr);
-
-	} else if (addr.kind == lbAddr_RelativeSlice) {
-		Type *rel_ptr = base_type(lb_addr_type(addr));
-		GB_ASSERT(rel_ptr->kind == Type_RelativeSlice);
-
-		lbValue offset_ptr = lb_emit_struct_ep(p, addr.addr, 0);
-		lbValue ptr = lb_emit_conv(p, offset_ptr, t_uintptr);
-		lbValue offset = lb_emit_load(p, offset_ptr);
-
-
-		if (!is_type_unsigned(rel_ptr->RelativeSlice.base_integer)) {
-			offset = lb_emit_conv(p, offset, t_i64);
+		if (rel_ptr->kind == Type_RelativeMultiPointer) {
+			return final_ptr;
 		}
 		}
-		offset = lb_emit_conv(p, offset, t_uintptr);
-		lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
-
-		Type *slice_type = base_type(rel_ptr->RelativeSlice.slice_type);
-		GB_ASSERT(rel_ptr->RelativeSlice.slice_type->kind == Type_Slice);
-		Type *slice_elem = slice_type->Slice.elem;
-		Type *slice_elem_ptr = alloc_type_pointer(slice_elem);
-
-		absolute_ptr = lb_emit_conv(p, absolute_ptr, slice_elem_ptr);
-
-		lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, rel_ptr->RelativeSlice.base_integer));
-
-		// NOTE(bill): nil check
-		lbValue nil_ptr = lb_const_nil(p->module, slice_elem_ptr);
-		lbValue data = {};
-		data.type = absolute_ptr.type;
-		data.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
-
-		lbValue len = lb_emit_load(p, lb_emit_struct_ep(p, addr.addr, 1));
-		len = lb_emit_conv(p, len, t_int);
-
-		lbAddr slice = lb_add_local_generated(p, slice_type, false);
-		lb_fill_slice(p, slice, data, len);
-		return lb_addr_load(p, slice);
-
+		return lb_emit_load(p, final_ptr);
 
 
 	} else if (addr.kind == lbAddr_Map) {
 	} else if (addr.kind == lbAddr_Map) {
 		Type *map_type = base_type(type_deref(addr.addr.type));
 		Type *map_type = base_type(type_deref(addr.addr.type));
@@ -1895,8 +1835,8 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 			case Type_SimdVector:
 			case Type_SimdVector:
 				return lb_type_internal(m, base);
 				return lb_type_internal(m, base);
 
 
-			// TODO(bill): Deal with this correctly. Can this be named?
 			case Type_Proc:
 			case Type_Proc:
+				// TODO(bill): Deal with this correctly. Can this be named?
 				return lb_type_internal(m, base);
 				return lb_type_internal(m, base);
 
 
 			case Type_Tuple:
 			case Type_Tuple:
@@ -2173,17 +2113,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 
 
 	case Type_RelativePointer:
 	case Type_RelativePointer:
 		return lb_type_internal(m, type->RelativePointer.base_integer);
 		return lb_type_internal(m, type->RelativePointer.base_integer);
+	case Type_RelativeMultiPointer:
+		return lb_type_internal(m, type->RelativeMultiPointer.base_integer);
 
 
-	case Type_RelativeSlice:
-		{
-			LLVMTypeRef base_integer = lb_type_internal(m, type->RelativeSlice.base_integer);
 
 
-			unsigned field_count = 2;
-			LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count);
-			fields[0] = base_integer;
-			fields[1] = base_integer;
-			return LLVMStructTypeInContext(ctx, fields, field_count, false);
-		}
 		
 		
 	case Type_Matrix:
 	case Type_Matrix:
 		{
 		{
@@ -2869,7 +2802,6 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
 	if (USE_SEPARATE_MODULES) {
 	if (USE_SEPARATE_MODULES) {
 		lbModule *other_module = lb_module_of_entity(m->gen, e);
 		lbModule *other_module = lb_module_of_entity(m->gen, e);
 
 
-		// TODO(bill): correct this logic
 		bool is_external = other_module != m;
 		bool is_external = other_module != m;
 		if (!is_external) {
 		if (!is_external) {
 			if (e->code_gen_module != nullptr) {
 			if (e->code_gen_module != nullptr) {

+ 12 - 9
src/llvm_backend_proc.cpp

@@ -362,7 +362,6 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
 
 
 	Type *pt = p->type;
 	Type *pt = p->type;
 	lbCallingConventionKind cc_kind = lbCallingConvention_C;
 	lbCallingConventionKind cc_kind = lbCallingConvention_C;
-	// TODO(bill): Clean up this logic
 	if (!is_arch_wasm()) {
 	if (!is_arch_wasm()) {
 		cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
 		cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
 	}
 	}
@@ -1702,7 +1701,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 		lbValue v = lb_build_expr(p, ce->args[0]);
 		lbValue v = lb_build_expr(p, ce->args[0]);
 		Type *t = base_type(v.type);
 		Type *t = base_type(v.type);
 		if (is_type_pointer(t)) {
 		if (is_type_pointer(t)) {
-			// IMPORTANT TODO(bill): Should there be a nil pointer check?
 			v = lb_emit_load(p, v);
 			v = lb_emit_load(p, v);
 			t = type_deref(t);
 			t = type_deref(t);
 		}
 		}
@@ -1712,7 +1710,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 			return lb_string_len(p, v);
 			return lb_string_len(p, v);
 		} else if (is_type_array(t)) {
 		} else if (is_type_array(t)) {
 			GB_PANIC("Array lengths are constant");
 			GB_PANIC("Array lengths are constant");
-		} else if (is_type_slice(t) || is_type_relative_slice(t)) {
+		} else if (is_type_slice(t)) {
 			return lb_slice_len(p, v);
 			return lb_slice_len(p, v);
 		} else if (is_type_dynamic_array(t)) {
 		} else if (is_type_dynamic_array(t)) {
 			return lb_dynamic_array_len(p, v);
 			return lb_dynamic_array_len(p, v);
@@ -1730,7 +1728,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 		lbValue v = lb_build_expr(p, ce->args[0]);
 		lbValue v = lb_build_expr(p, ce->args[0]);
 		Type *t = base_type(v.type);
 		Type *t = base_type(v.type);
 		if (is_type_pointer(t)) {
 		if (is_type_pointer(t)) {
-			// IMPORTANT TODO(bill): Should there be a nil pointer check?
 			v = lb_emit_load(p, v);
 			v = lb_emit_load(p, v);
 			t = type_deref(t);
 			t = type_deref(t);
 		}
 		}
@@ -1738,7 +1735,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 			GB_PANIC("Unreachable");
 			GB_PANIC("Unreachable");
 		} else if (is_type_array(t)) {
 		} else if (is_type_array(t)) {
 			GB_PANIC("Array lengths are constant");
 			GB_PANIC("Array lengths are constant");
-		} else if (is_type_slice(t) || is_type_relative_slice(t)) {
+		} else if (is_type_slice(t)) {
 			return lb_slice_len(p, v);
 			return lb_slice_len(p, v);
 		} else if (is_type_dynamic_array(t)) {
 		} else if (is_type_dynamic_array(t)) {
 			return lb_dynamic_array_cap(p, v);
 			return lb_dynamic_array_cap(p, v);
@@ -3144,7 +3141,7 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
 
 
 	lbValue res = lb_build_call_expr_internal(p, expr);
 	lbValue res = lb_build_call_expr_internal(p, expr);
 
 
-	if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
+	if (ce->optional_ok_one) {
 		GB_ASSERT(is_type_tuple(res.type));
 		GB_ASSERT(is_type_tuple(res.type));
 		GB_ASSERT(res.type->Tuple.variables.count == 2);
 		GB_ASSERT(res.type->Tuple.variables.count == 2);
 		return lb_emit_struct_ev(p, res, 0);
 		return lb_emit_struct_ev(p, res, 0);
@@ -3332,9 +3329,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 		isize param_index = lookup_procedure_parameter(pt, name);
 		isize param_index = lookup_procedure_parameter(pt, name);
 		GB_ASSERT(param_index >= 0);
 		GB_ASSERT(param_index >= 0);
 
 
-		lbValue value = lb_build_expr(p, fv->value);
-		GB_ASSERT(!is_type_tuple(value.type));
-		args[param_index] = value;
+		Entity *e = pt->params->Tuple.variables[param_index];
+		if (e->kind == Entity_TypeName) {
+			lbValue value = lb_const_nil(p->module, e->type);
+			args[param_index] = value;
+		} else {
+			lbValue value = lb_build_expr(p, fv->value);
+			GB_ASSERT(!is_type_tuple(value.type));
+			args[param_index] = value;
+		}
 	}
 	}
 
 
 	TokenPos pos = ast_token(ce->proc).pos;
 	TokenPos pos = ast_token(ce->proc).pos;

+ 1 - 4
src/llvm_backend_stmt.cpp

@@ -1688,7 +1688,6 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
 			lb_add_entity(p->module, case_entity, ptr);
 			lb_add_entity(p->module, case_entity, ptr);
 			lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
 			lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
 		} else {
 		} else {
-			// TODO(bill): is the correct expected behaviour?
 			lb_store_type_case_implicit(p, clause, parent_value);
 			lb_store_type_case_implicit(p, clause, parent_value);
 		}
 		}
 
 
@@ -2014,12 +2013,10 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 	defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
 	defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
 
 
 	if (is->init != nullptr) {
 	if (is->init != nullptr) {
-		// TODO(bill): Should this have a separate block to begin with?
-	#if 1
 		lbBlock *init = lb_create_block(p, "if.init");
 		lbBlock *init = lb_create_block(p, "if.init");
 		lb_emit_jump(p, init);
 		lb_emit_jump(p, init);
 		lb_start_block(p, init);
 		lb_start_block(p, init);
-	#endif
+
 		lb_build_stmt(p, is->init);
 		lb_build_stmt(p, is->init);
 	}
 	}
 	lbBlock *then = lb_create_block(p, "if.then");
 	lbBlock *then = lb_create_block(p, "if.then");

+ 7 - 6
src/llvm_backend_type.cpp

@@ -57,7 +57,7 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
 	case Type_BitSet:          kind = Typeid_Bit_Set;          break;
 	case Type_BitSet:          kind = Typeid_Bit_Set;          break;
 	case Type_SimdVector:      kind = Typeid_Simd_Vector;      break;
 	case Type_SimdVector:      kind = Typeid_Simd_Vector;      break;
 	case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
 	case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
-	case Type_RelativeSlice:   kind = Typeid_Relative_Slice;   break;
+	case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
 	case Type_SoaPointer:      kind = Typeid_SoaPointer;       break;
 	case Type_SoaPointer:      kind = Typeid_SoaPointer;       break;
 	}
 	}
 
 
@@ -731,7 +731,6 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
 
 
 				type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
 				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++) {
 				for (isize source_index = 0; source_index < count; source_index++) {
-					// TODO(bill): Order fields in source order not layout order
 					Entity *f = t->Struct.fields[source_index];
 					Entity *f = t->Struct.fields[source_index];
 					lbValue tip = lb_type_info(m, f->type);
 					lbValue tip = lb_type_info(m, f->type);
 					i64 foffset = 0;
 					i64 foffset = 0;
@@ -858,12 +857,13 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
 				lb_emit_store(p, tag, res);
 				lb_emit_store(p, tag, res);
 			}
 			}
 			break;
 			break;
-		case Type_RelativeSlice:
+
+		case Type_RelativeMultiPointer:
 			{
 			{
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_slice_ptr);
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_multi_pointer_ptr);
 				LLVMValueRef vals[2] = {
 				LLVMValueRef vals[2] = {
-					lb_type_info(m, t->RelativeSlice.slice_type).value,
-					lb_type_info(m, t->RelativeSlice.base_integer).value,
+					lb_type_info(m, t->RelativeMultiPointer.pointer_type).value,
+					lb_type_info(m, t->RelativeMultiPointer.base_integer).value,
 				};
 				};
 
 
 				lbValue res = {};
 				lbValue res = {};
@@ -872,6 +872,7 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
 				lb_emit_store(p, tag, res);
 				lb_emit_store(p, tag, res);
 			}
 			}
 			break;
 			break;
+
 		case Type_Matrix: 
 		case Type_Matrix: 
 			{
 			{
 				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);
 				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);

+ 1 - 6
src/llvm_backend_utility.cpp

@@ -1124,11 +1124,6 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 		}
 		}
 	} else if (is_type_array(t)) {
 	} else if (is_type_array(t)) {
 		return lb_emit_array_epi(p, s, index);
 		return lb_emit_array_epi(p, s, index);
-	} else if (is_type_relative_slice(t)) {
-		switch (index) {
-		case 0: result_type = t->RelativeSlice.base_integer; break;
-		case 1: result_type = t->RelativeSlice.base_integer; break;
-		}
 	} else if (is_type_soa_pointer(t)) {
 	} else if (is_type_soa_pointer(t)) {
 		switch (index) {
 		switch (index) {
 		case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
 		case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
@@ -1547,7 +1542,7 @@ gb_internal lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
 	return lb_emit_struct_ev(p, slice, 0);
 	return lb_emit_struct_ev(p, slice, 0);
 }
 }
 gb_internal lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
 gb_internal lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
-	GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
+	GB_ASSERT(is_type_slice(slice.type));
 	return lb_emit_struct_ev(p, slice, 1);
 	return lb_emit_struct_ev(p, slice, 1);
 }
 }
 gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
 gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {

+ 139 - 460
src/main.cpp

@@ -1,5 +1,4 @@
 // #define NO_ARRAY_BOUNDS_CHECK
 // #define NO_ARRAY_BOUNDS_CHECK
-
 #include "common.cpp"
 #include "common.cpp"
 #include "timings.cpp"
 #include "timings.cpp"
 #include "tokenizer.cpp"
 #include "tokenizer.cpp"
@@ -71,6 +70,18 @@ gb_global Timings global_timings = {0};
 #include "checker.cpp"
 #include "checker.cpp"
 #include "docs.cpp"
 #include "docs.cpp"
 
 
+#include "linker.cpp"
+
+#if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND)
+#define ALLOW_TILDE 1
+#else
+#define ALLOW_TILDE 0
+#endif
+
+#if ALLOW_TILDE
+#include "tilde.cpp"
+#endif
+
 #include "llvm_backend.cpp"
 #include "llvm_backend.cpp"
 
 
 #if defined(GB_SYSTEM_OSX)
 #if defined(GB_SYSTEM_OSX)
@@ -147,422 +158,6 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt,
 	return exit_code;
 	return exit_code;
 }
 }
 
 
-
-gb_internal i32 linker_stage(lbGenerator *gen) {
-	i32 result = 0;
-	Timings *timings = &global_timings;
-
-	String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
-	debugf("Linking %.*s\n", LIT(output_filename));
-
-	// TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
-
-	if (is_arch_wasm()) {
-		timings_start_section(timings, str_lit("wasm-ld"));
-
-	#if defined(GB_SYSTEM_WINDOWS)
-		result = system_exec_command_line_app("wasm-ld",
-			"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
-			LIT(build_context.ODIN_ROOT),
-			LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
-	#else
-		result = system_exec_command_line_app("wasm-ld",
-			"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
-			LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
-	#endif
-		return result;
-	}
-
-	if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
-#if defined(GB_SYSTEM_UNIX) 
-		result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
-			LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
-#else
-		gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
-			LIT(target_os_names[build_context.metrics.os]),
-			LIT(target_arch_names[build_context.metrics.arch])
-		);
-#endif
-	} else if (build_context.cross_compiling && build_context.different_os) {
-		gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
-			LIT(target_os_names[build_context.metrics.os]),
-			LIT(target_arch_names[build_context.metrics.arch])
-		);
-		build_context.keep_object_files = true;
-	} else {
-	#if defined(GB_SYSTEM_WINDOWS)
-		bool is_windows = true;
-	#else
-		bool is_windows = false;
-	#endif
-	#if defined(GB_SYSTEM_OSX)
-		bool is_osx = true;
-	#else
-		bool is_osx = false;
-	#endif
-
-
-		if (is_windows) {
-			String section_name = str_lit("msvc-link");
-			if (build_context.use_lld) {
-				section_name = str_lit("lld-link");
-			}
-			timings_start_section(timings, section_name);
-
-			gbString lib_str = gb_string_make(heap_allocator(), "");
-			defer (gb_string_free(lib_str));
-
-			gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
-			defer (gb_string_free(link_settings));
-
-			// Add library search paths.
-			if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
-				String path = {};
-				auto add_path = [&](String path) {
-					if (path[path.len-1] == '\\') {
-						path.len -= 1;
-					}
-					link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
-				};
-				add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
-				add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
-				add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
-			}
-
-
-			StringSet libs = {};
-			string_set_init(&libs, 64);
-			defer (string_set_destroy(&libs));
-
-			StringSet asm_files = {};
-			string_set_init(&asm_files, 64);
-			defer (string_set_destroy(&asm_files));
-
-			for (Entity *e : gen->foreign_libraries) {
-				GB_ASSERT(e->kind == Entity_LibraryName);
-				for_array(i, e->LibraryName.paths) {
-					String lib = string_trim_whitespace(e->LibraryName.paths[i]);
-					// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
-					// we will never uses these strings afterwards
-					string_to_lower(&lib);
-					if (lib.len == 0) {
-						continue;
-					}
-
-					if (has_asm_extension(lib)) {
-						if (!string_set_update(&asm_files, lib)) {
-							String asm_file = asm_files.entries[i].value;
-							String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
-
-							result = system_exec_command_line_app("nasm",
-								"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
-								"-f win64 "
-								"-o \"%.*s\" "
-								"%.*s "
-								"",
-								LIT(build_context.ODIN_ROOT), LIT(asm_file),
-								LIT(obj_file),
-								LIT(build_context.extra_assembler_flags)
-							);
-
-							if (result) {
-								return result;
-							}
-							array_add(&gen->output_object_paths, obj_file);
-						}
-					} else {
-						if (!string_set_update(&libs, lib)) {
-							lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
-						}
-					}
-				}
-			}
-
-			for (Entity *e : gen->foreign_libraries) {
-				GB_ASSERT(e->kind == Entity_LibraryName);
-				if (e->LibraryName.extra_linker_flags.len != 0) {
-					lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
-				}
-			}
-
-			if (build_context.build_mode == BuildMode_DynamicLibrary) {
-				link_settings = gb_string_append_fmt(link_settings, " /DLL");
-			} else {
-				link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
-			}
-
-			if (build_context.pdb_filepath != "") {
-				String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
-				link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
-			}
-
-			if (build_context.no_crt) {
-				link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
-			} else {
-				link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
-			}
-
-			if (build_context.ODIN_DEBUG) {
-				link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
-			}
-
-			gbString object_files = gb_string_make(heap_allocator(), "");
-			defer (gb_string_free(object_files));
-			for (String const &object_path : gen->output_object_paths) {
-				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
-			}
-
-			String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
-			defer (gb_free(heap_allocator(), vs_exe_path.text));
-
-			String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
-			defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
-
-			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
-			if (!build_context.use_lld) { // msvc
-				String res_path = {};
-				defer (gb_free(heap_allocator(), res_path.text));
-				if (build_context.has_resource) {
-					String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
-					res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
-					gb_free(heap_allocator(), temp_res_path.text);
-
-					String rc_path  = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
-					defer (gb_free(heap_allocator(), rc_path.text));
-
-					result = system_exec_command_line_app("msvc-link",
-						"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
-						LIT(windows_sdk_bin_path),
-						LIT(res_path),
-						LIT(rc_path)
-					);
-
-					if (result) {
-						return result;
-					}
-				}
-
-				switch (build_context.build_mode) {
-				case BuildMode_Executable:
-					link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
-					break;
-				}
-
-				result = system_exec_command_line_app("msvc-link",
-					"\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
-					"/nologo /incremental:no /opt:ref /subsystem:%s "
-					"%.*s "
-					"%.*s "
-					"%s "
-					"",
-					LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
-					link_settings,
-					subsystem_str,
-					LIT(build_context.link_flags),
-					LIT(build_context.extra_linker_flags),
-					lib_str
-				);
-				if (result) {
-					return result;
-				}
-			} else { // lld
-				result = system_exec_command_line_app("msvc-lld-link",
-					"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
-					"/nologo /incremental:no /opt:ref /subsystem:%s "
-					"%.*s "
-					"%.*s "
-					"%s "
-					"",
-					LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
-					link_settings,
-					subsystem_str,
-					LIT(build_context.link_flags),
-					LIT(build_context.extra_linker_flags),
-					lib_str
-				);
-
-				if (result) {
-					return result;
-				}
-			}
-		} else {
-			timings_start_section(timings, str_lit("ld-link"));
-
-			// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
-			char cwd[256];
-			#if !defined(GB_SYSTEM_WINDOWS)
-			getcwd(&cwd[0], 256);
-			#endif
-			//printf("%s\n", cwd);
-
-			// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
-			//                files can be passed with -l:
-			gbString lib_str = gb_string_make(heap_allocator(), "-L/");
-			defer (gb_string_free(lib_str));
-
-			StringSet libs = {};
-			string_set_init(&libs, 64);
-			defer (string_set_destroy(&libs));
-
-			for (Entity *e : gen->foreign_libraries) {
-				GB_ASSERT(e->kind == Entity_LibraryName);
-				for (String lib : e->LibraryName.paths) {
-					lib = string_trim_whitespace(lib);
-					if (lib.len == 0) {
-						continue;
-					}
-					if (string_set_update(&libs, lib)) {
-						continue;
-					}
-
-					// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
-					//   This allows you to specify '-f' in a #foreign_system_library,
-					//   without having to implement any new syntax specifically for MacOS.
-					if (build_context.metrics.os == TargetOs_darwin) {
-						if (string_ends_with(lib, str_lit(".framework"))) {
-							// framework thingie
-							String lib_name = lib;
-							lib_name = remove_extension_from_path(lib_name);
-							lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
-						} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
-							// For:
-							// object
-							// dynamic lib
-							// static libs, absolute full path relative to the file in which the lib was imported from
-							lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
-						} else {
-							// dynamic or static system lib, just link regularly searching system library paths
-							lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
-						}
-					} else {
-						// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
-						//                since those are statically linked to at link time. shared libraries (.so) has to be
-						//                available at runtime wherever the executable is run, so we make require those to be
-						//                local to the executable (unless the system collection is used, in which case we search
-						//                the system library paths for the library file).
-						if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
-							// static libs and object files, absolute full path relative to the file in which the lib was imported from
-							lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
-						} else if (string_ends_with(lib, str_lit(".so"))) {
-							// dynamic lib, relative path to executable
-							// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
-							//                at runtime to the executable
-							lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
-						} else {
-							// dynamic or static system lib, just link regularly searching system library paths
-							lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
-						}
-					}
-				}
-			}
-
-			for (Entity *e : gen->foreign_libraries) {
-				GB_ASSERT(e->kind == Entity_LibraryName);
-				if (e->LibraryName.extra_linker_flags.len != 0) {
-					lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
-				}
-			}
-
-			gbString object_files = gb_string_make(heap_allocator(), "");
-			defer (gb_string_free(object_files));
-			for (String object_path : gen->output_object_paths) {
-				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
-			}
-
-			gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
-
-			if (build_context.no_crt) {
-				link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
-			}
-
-			// NOTE(dweiler): We use clang as a frontend for the linker as there are
-			// other runtime and compiler support libraries that need to be linked in
-			// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
-			// These are not always typically inside /lib, /lib64, or /usr versions
-			// of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
-			// the distribution of Linux even. The gcc or clang specs is the only
-			// reliable way to query this information to call ld directly.
-			if (build_context.build_mode == BuildMode_DynamicLibrary) {
-				// NOTE(dweiler): Let the frontend know we're building a shared library
-				// so it doesn't generate symbols which cannot be relocated.
-				link_settings = gb_string_appendc(link_settings, "-shared ");
-
-				// NOTE(dweiler): _odin_entry_point must be called at initialization
-				// time of the shared object, similarly, _odin_exit_point must be called
-				// at deinitialization. We can pass both -init and -fini to the linker by
-				// using a comma separated list of arguments to -Wl.
-				//
-				// This previously used ld but ld cannot actually build a shared library
-				// correctly this way since all the other dependencies provided implicitly
-				// by the compiler frontend are still needed and most of the command
-				// line arguments prepared previously are incompatible with ld.
-				if (build_context.metrics.os == TargetOs_darwin) {
-					link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
-					// NOTE(weshardee): __odin_exit_point should also be added, but -fini 
-					// does not exist on MacOS
-				} else {
-					link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
-					link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
-				}
-				
-			} else if (build_context.metrics.os != TargetOs_openbsd) {
-				// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
-				link_settings = gb_string_appendc(link_settings, "-no-pie ");
-			}
-
-			gbString platform_lib_str = gb_string_make(heap_allocator(), "");
-			defer (gb_string_free(platform_lib_str));
-			if (build_context.metrics.os == TargetOs_darwin) {
-				platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
-			} else {
-				platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
-			}
-
-			if (build_context.metrics.os == TargetOs_darwin) {
-				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
-				if (build_context.minimum_os_version_string.len) {
-					link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
-				} else if (build_context.metrics.arch == TargetArch_arm64) {
-					link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0  ");
-				} else {
-					link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
-				}
-				// This points the linker to where the entry point is
-				link_settings = gb_string_appendc(link_settings, " -e _main ");
-			}
-
-			gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
-			defer (gb_string_free(link_command_line));
-
-			link_command_line = gb_string_appendc(link_command_line, object_files);
-			link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
-			link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
-			link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
-			link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
-			link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
-			link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
-
-			result = system_exec_command_line_app("ld-link", link_command_line);
-
-			if (result) {
-				return result;
-			}
-
-			if (is_osx && build_context.ODIN_DEBUG) {
-				// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
-				// to the symbols in the object file
-				result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
-
-				if (result) {
-					return result;
-				}
-			}
-		}
-	}
-
-	return result;
-}
-
 gb_internal Array<String> setup_args(int argc, char const **argv) {
 gb_internal Array<String> setup_args(int argc, char const **argv) {
 	gbAllocator a = heap_allocator();
 	gbAllocator a = heap_allocator();
 
 
@@ -653,8 +248,16 @@ enum BuildFlagKind {
 	BuildFlag_UseSeparateModules,
 	BuildFlag_UseSeparateModules,
 	BuildFlag_NoThreadedChecker,
 	BuildFlag_NoThreadedChecker,
 	BuildFlag_ShowDebugMessages,
 	BuildFlag_ShowDebugMessages,
+
 	BuildFlag_Vet,
 	BuildFlag_Vet,
+	BuildFlag_VetShadowing,
+	BuildFlag_VetUnused,
+	BuildFlag_VetUsingStmt,
+	BuildFlag_VetUsingParam,
+	BuildFlag_VetStyle,
+	BuildFlag_VetSemicolon,
 	BuildFlag_VetExtra,
 	BuildFlag_VetExtra,
+
 	BuildFlag_IgnoreUnknownAttributes,
 	BuildFlag_IgnoreUnknownAttributes,
 	BuildFlag_ExtraLinkerFlags,
 	BuildFlag_ExtraLinkerFlags,
 	BuildFlag_ExtraAssemblerFlags,
 	BuildFlag_ExtraAssemblerFlags,
@@ -671,7 +274,6 @@ enum BuildFlagKind {
 	BuildFlag_DisallowDo,
 	BuildFlag_DisallowDo,
 	BuildFlag_DefaultToNilAllocator,
 	BuildFlag_DefaultToNilAllocator,
 	BuildFlag_StrictStyle,
 	BuildFlag_StrictStyle,
-	BuildFlag_StrictStyleInitOnly,
 	BuildFlag_ForeignErrorProcedures,
 	BuildFlag_ForeignErrorProcedures,
 	BuildFlag_NoRTTI,
 	BuildFlag_NoRTTI,
 	BuildFlag_DynamicMapCalls,
 	BuildFlag_DynamicMapCalls,
@@ -695,6 +297,8 @@ enum BuildFlagKind {
 	BuildFlag_InternalIgnoreLazy,
 	BuildFlag_InternalIgnoreLazy,
 	BuildFlag_InternalIgnoreLLVMBuild,
 	BuildFlag_InternalIgnoreLLVMBuild,
 
 
+	BuildFlag_Tilde,
+
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
 	BuildFlag_IgnoreVsSearch,
 	BuildFlag_IgnoreVsSearch,
 	BuildFlag_ResourceFile,
 	BuildFlag_ResourceFile,
@@ -830,8 +434,16 @@ gb_internal bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_UseSeparateModules,      str_lit("use-separate-modules"),      BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_UseSeparateModules,      str_lit("use-separate-modules"),      BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_NoThreadedChecker,       str_lit("no-threaded-checker"),       BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_NoThreadedChecker,       str_lit("no-threaded-checker"),       BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_ShowDebugMessages,       str_lit("show-debug-messages"),       BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_ShowDebugMessages,       str_lit("show-debug-messages"),       BuildFlagParam_None,    Command_all);
+
 	add_flag(&build_flags, BuildFlag_Vet,                     str_lit("vet"),                       BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_Vet,                     str_lit("vet"),                       BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_VetUnused,               str_lit("vet-unused"),                BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_VetShadowing,            str_lit("vet-shadowing"),             BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_VetUsingStmt,            str_lit("vet-using-stmt"),            BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_VetUsingParam,           str_lit("vet-using-param"),           BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_VetStyle,                str_lit("vet-style"),                 BuildFlagParam_None,    Command__does_check);
+	add_flag(&build_flags, BuildFlag_VetSemicolon,            str_lit("vet-semicolon"),             BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_VetExtra,                str_lit("vet-extra"),                 BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_VetExtra,                str_lit("vet-extra"),                 BuildFlagParam_None,    Command__does_check);
+
 	add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_ExtraLinkerFlags,        str_lit("extra-linker-flags"),        BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ExtraLinkerFlags,        str_lit("extra-linker-flags"),        BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags,     str_lit("extra-assembler-flags"),     BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags,     str_lit("extra-assembler-flags"),     BuildFlagParam_String,  Command__does_build);
@@ -847,7 +459,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_DisallowDo,              str_lit("disallow-do"),               BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_DisallowDo,              str_lit("disallow-do"),               BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_DefaultToNilAllocator,   str_lit("default-to-nil-allocator"),  BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_DefaultToNilAllocator,   str_lit("default-to-nil-allocator"),  BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_StrictStyle,             str_lit("strict-style"),              BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_StrictStyle,             str_lit("strict-style"),              BuildFlagParam_None,    Command__does_check);
-	add_flag(&build_flags, BuildFlag_StrictStyleInitOnly,     str_lit("strict-style-init-only"),    BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_ForeignErrorProcedures,  str_lit("foreign-error-procedures"),  BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_ForeignErrorProcedures,  str_lit("foreign-error-procedures"),  BuildFlagParam_None,    Command__does_check);
 
 
 	add_flag(&build_flags, BuildFlag_NoRTTI,                  str_lit("no-rtti"),                   BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_NoRTTI,                  str_lit("no-rtti"),                   BuildFlagParam_None,    Command__does_check);
@@ -869,6 +480,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_InternalIgnoreLazy,      str_lit("internal-ignore-lazy"),      BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_InternalIgnoreLazy,      str_lit("internal-ignore-lazy"),      BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None,    Command_all);
 
 
+#if ALLOW_TILDE
+	add_flag(&build_flags, BuildFlag_Tilde,                   str_lit("tilde"),                     BuildFlagParam_None,    Command__does_build);
+#endif
+
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch,          str_lit("ignore-vs-search"),          BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch,          str_lit("ignore-vs-search"),          BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_ResourceFile,            str_lit("resource"),                  BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ResourceFile,            str_lit("resource"),                  BuildFlagParam_String,  Command__does_build);
@@ -1362,13 +977,25 @@ gb_internal bool parse_build_flags(Array<String> args) {
 							build_context.show_debug_messages = true;
 							build_context.show_debug_messages = true;
 							break;
 							break;
 						case BuildFlag_Vet:
 						case BuildFlag_Vet:
-							build_context.vet = true;
+							if (build_context.vet_flags & VetFlag_Extra) {
+								build_context.vet_flags |= VetFlag_All;
+							} else {
+								build_context.vet_flags &= ~VetFlag_Extra;
+								build_context.vet_flags |= VetFlag_All;
+							}
 							break;
 							break;
-						case BuildFlag_VetExtra: {
-							build_context.vet = true;
-							build_context.vet_extra = true;
+
+						case BuildFlag_VetUnused:     build_context.vet_flags |= VetFlag_Unused;     break;
+						case BuildFlag_VetShadowing:  build_context.vet_flags |= VetFlag_Shadowing;  break;
+						case BuildFlag_VetUsingStmt:  build_context.vet_flags |= VetFlag_UsingStmt;  break;
+						case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break;
+						case BuildFlag_VetStyle:      build_context.vet_flags |= VetFlag_Style;      break;
+						case BuildFlag_VetSemicolon:  build_context.vet_flags |= VetFlag_Semicolon;  break;
+
+						case BuildFlag_VetExtra:
+							build_context.vet_flags = VetFlag_All | VetFlag_Extra;
 							break;
 							break;
-						}
+
 						case BuildFlag_IgnoreUnknownAttributes:
 						case BuildFlag_IgnoreUnknownAttributes:
 							build_context.ignore_unknown_attributes = true;
 							build_context.ignore_unknown_attributes = true;
 							break;
 							break;
@@ -1456,20 +1083,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
 						case BuildFlag_ForeignErrorProcedures:
 						case BuildFlag_ForeignErrorProcedures:
 							build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
 							build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
 							break;
 							break;
-						case BuildFlag_StrictStyle: {
-							if (build_context.strict_style_init_only) {
-								gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n");
-							}
+						case BuildFlag_StrictStyle:
 							build_context.strict_style = true;
 							build_context.strict_style = true;
 							break;
 							break;
-						}
-						case BuildFlag_StrictStyleInitOnly: {
-							if (build_context.strict_style) {
-								gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n");
-							}
-							build_context.strict_style_init_only = true;
-							break;
-						}
 						case BuildFlag_Short:
 						case BuildFlag_Short:
 							build_context.cmd_doc_flags |= CmdDocFlag_Short;
 							build_context.cmd_doc_flags |= CmdDocFlag_Short;
 							break;
 							break;
@@ -1536,6 +1152,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
 						case BuildFlag_InternalIgnoreLLVMBuild:
 						case BuildFlag_InternalIgnoreLLVMBuild:
 							build_context.ignore_llvm_build = true;
 							build_context.ignore_llvm_build = true;
 							break;
 							break;
+						case BuildFlag_Tilde:
+							build_context.tilde_backend = true;
+							break;
+
 					#if defined(GB_SYSTEM_WINDOWS)
 					#if defined(GB_SYSTEM_WINDOWS)
 						case BuildFlag_IgnoreVsSearch: {
 						case BuildFlag_IgnoreVsSearch: {
 							GB_ASSERT(value.kind == ExactValue_Invalid);
 							GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1572,7 +1192,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
 								if (path_is_directory(path)) {
 								if (path_is_directory(path)) {
 									gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
 									gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
 									bad_flags = true;
 									bad_flags = true;
-									break;									
+									break;
 								}
 								}
 								// #if defined(GB_SYSTEM_WINDOWS)
 								// #if defined(GB_SYSTEM_WINDOWS)
 								// 	String ext = path_extension(path);
 								// 	String ext = path_extension(path);
@@ -2005,6 +1625,10 @@ gb_internal void print_show_help(String const arg0, String const &command) {
 		print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds");
 		print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds");
 		print_usage_line(0, "");
 		print_usage_line(0, "");
 
 
+		print_usage_line(1, "-show-system-calls");
+		print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler");
+		print_usage_line(0, "");
+
 		print_usage_line(1, "-export-timings:<format>");
 		print_usage_line(1, "-export-timings:<format>");
 		print_usage_line(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`");
 		print_usage_line(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`");
 		print_usage_line(2, "Available options:");
 		print_usage_line(2, "Available options:");
@@ -2114,29 +1738,55 @@ gb_internal void print_show_help(String const arg0, String const &command) {
 	}
 	}
 
 
 	if (check) {
 	if (check) {
-		#if defined(GB_SYSTEM_WINDOWS)
 		print_usage_line(1, "-no-threaded-checker");
 		print_usage_line(1, "-no-threaded-checker");
 		print_usage_line(2, "Disabled multithreading in the semantic checker stage");
 		print_usage_line(2, "Disabled multithreading in the semantic checker stage");
 		print_usage_line(0, "");
 		print_usage_line(0, "");
-		#else
-		print_usage_line(1, "-threaded-checker");
-		print_usage_line(1, "[EXPERIMENTAL]");
-		print_usage_line(2, "Multithread the semantic checker stage");
-		print_usage_line(0, "");
-		#endif
+	}
 
 
+	if (check) {
 		print_usage_line(1, "-vet");
 		print_usage_line(1, "-vet");
 		print_usage_line(2, "Do extra checks on the code");
 		print_usage_line(2, "Do extra checks on the code");
 		print_usage_line(2, "Extra checks include:");
 		print_usage_line(2, "Extra checks include:");
-		print_usage_line(3, "Variable shadowing within procedures");
-		print_usage_line(3, "Unused declarations");
+		print_usage_line(2, "-vet-unused");
+		print_usage_line(2, "-vet-shadowing");
+		print_usage_line(2, "-vet-using-stmt");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-vet-unused");
+		print_usage_line(2, "Checks for unused declarations");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-vet-shadowing");
+		print_usage_line(2, "Checks for variable shadowing within procedures");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-vet-using-stmt");
+		print_usage_line(2, "Checks for the use of 'using' as a statement");
+		print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-vet-using-param");
+		print_usage_line(2, "Checks for the use of 'using' on procedure parameters");
+		print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-vet-style");
+		print_usage_line(2, "Errs on missing trailing commas followed by a newline");
+		print_usage_line(2, "Errs on deprecated syntax");
+		print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style)");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-vet-semicolon");
+		print_usage_line(2, "Errs on unneeded semicolons");
 		print_usage_line(0, "");
 		print_usage_line(0, "");
 
 
 		print_usage_line(1, "-vet-extra");
 		print_usage_line(1, "-vet-extra");
 		print_usage_line(2, "Do even more checks than standard vet on the code");
 		print_usage_line(2, "Do even more checks than standard vet on the code");
 		print_usage_line(2, "To treat the extra warnings as errors, use -warnings-as-errors");
 		print_usage_line(2, "To treat the extra warnings as errors, use -warnings-as-errors");
 		print_usage_line(0, "");
 		print_usage_line(0, "");
+	}
 
 
+	if (check) {
 		print_usage_line(1, "-ignore-unknown-attributes");
 		print_usage_line(1, "-ignore-unknown-attributes");
 		print_usage_line(2, "Ignores unknown attributes");
 		print_usage_line(2, "Ignores unknown attributes");
 		print_usage_line(2, "This can be used with metaprogramming tools");
 		print_usage_line(2, "This can be used with metaprogramming tools");
@@ -2206,10 +1856,8 @@ gb_internal void print_show_help(String const arg0, String const &command) {
 
 
 		print_usage_line(1, "-strict-style");
 		print_usage_line(1, "-strict-style");
 		print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons");
 		print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons");
-		print_usage_line(0, "");
-
-		print_usage_line(1, "-strict-style-init-only");
-		print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons, only on the initial project");
+		print_usage_line(2, "Errs on missing trailing commas followed by a newline");
+		print_usage_line(2, "Errs on deprecated syntax");
 		print_usage_line(0, "");
 		print_usage_line(0, "");
 
 
 		print_usage_line(1, "-ignore-warnings");
 		print_usage_line(1, "-ignore-warnings");
@@ -2347,6 +1995,8 @@ gb_internal void print_show_unused(Checker *c) {
 }
 }
 
 
 gb_internal bool check_env(void) {
 gb_internal bool check_env(void) {
+	TIME_SECTION("init check env");
+
 	gbAllocator a = heap_allocator();
 	gbAllocator a = heap_allocator();
 	char const *odin_root = gb_get_env("ODIN_ROOT", a);
 	char const *odin_root = gb_get_env("ODIN_ROOT", a);
 	defer (gb_free(a, cast(void *)odin_root));
 	defer (gb_free(a, cast(void *)odin_root));
@@ -2548,6 +2198,7 @@ gb_internal int strip_semicolons(Parser *parser) {
 }
 }
 
 
 gb_internal void init_terminal(void) {
 gb_internal void init_terminal(void) {
+	TIME_SECTION("init terminal");
 	build_context.has_ansi_terminal_colours = false;
 	build_context.has_ansi_terminal_colours = false;
 
 
 	gbAllocator a = heap_allocator();
 	gbAllocator a = heap_allocator();
@@ -2614,11 +2265,13 @@ int main(int arg_count, char const **arg_ptr) {
 		return 1;
 		return 1;
 	}
 	}
 
 
+	TIME_SECTION("init default library collections");
 	array_init(&library_collections, heap_allocator());
 	array_init(&library_collections, heap_allocator());
 	// NOTE(bill): 'core' cannot be (re)defined by the user
 	// NOTE(bill): 'core' cannot be (re)defined by the user
 	add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
 	add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
 	add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
 	add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
 
 
+	TIME_SECTION("init args");
 	map_init(&build_context.defined_values);
 	map_init(&build_context.defined_values);
 	build_context.extra_packages.allocator = heap_allocator();
 	build_context.extra_packages.allocator = heap_allocator();
 	string_set_init(&build_context.test_names);
 	string_set_init(&build_context.test_names);
@@ -2814,12 +2467,14 @@ int main(int arg_count, char const **arg_ptr) {
 		for_array(i, build_context.build_paths) {
 		for_array(i, build_context.build_paths) {
 			String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
 			String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
 			debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
 			debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
-		}		
+		}
 	}
 	}
 
 
+	TIME_SECTION("init thread pool");
 	init_global_thread_pool();
 	init_global_thread_pool();
 	defer (thread_pool_destroy(&global_thread_pool));
 	defer (thread_pool_destroy(&global_thread_pool));
 
 
+	TIME_SECTION("init universal");
 	init_universal();
 	init_universal();
 	// TODO(bill): prevent compiling without a linker
 	// TODO(bill): prevent compiling without a linker
 
 
@@ -2882,16 +2537,18 @@ int main(int arg_count, char const **arg_ptr) {
 		return 0;
 		return 0;
 	}
 	}
 
 
-	MAIN_TIME_SECTION("LLVM API Code Gen");
-	lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
-	if (!lb_init_generator(gen, checker)) {
-		return 1;
-	}
-	if (lb_generate_code(gen)) {
+#if ALLOW_TILDE
+	if (build_context.tilde_backend) {
+		LinkerData linker_data = {};
+		MAIN_TIME_SECTION("Tilde Code Gen");
+		if (!cg_generate_code(checker, &linker_data)) {
+			return 1;
+		}
+
 		switch (build_context.build_mode) {
 		switch (build_context.build_mode) {
 		case BuildMode_Executable:
 		case BuildMode_Executable:
 		case BuildMode_DynamicLibrary:
 		case BuildMode_DynamicLibrary:
-			i32 result = linker_stage(gen);
+			i32 result = linker_stage(&linker_data);
 			if (result) {
 			if (result) {
 				if (build_context.show_timings) {
 				if (build_context.show_timings) {
 					show_timings(checker, &global_timings);
 					show_timings(checker, &global_timings);
@@ -2900,9 +2557,31 @@ int main(int arg_count, char const **arg_ptr) {
 			}
 			}
 			break;
 			break;
 		}
 		}
-	}
+	} else
+#endif
+	{
+		MAIN_TIME_SECTION("LLVM API Code Gen");
+		lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
+		if (!lb_init_generator(gen, checker)) {
+			return 1;
+		}
+		if (lb_generate_code(gen)) {
+			switch (build_context.build_mode) {
+			case BuildMode_Executable:
+			case BuildMode_DynamicLibrary:
+				i32 result = linker_stage(gen);
+				if (result) {
+					if (build_context.show_timings) {
+						show_timings(checker, &global_timings);
+					}
+					return result;
+				}
+				break;
+			}
+		}
 
 
-	remove_temp_files(gen);
+		remove_temp_files(gen);
+	}
 
 
 	if (build_context.show_timings) {
 	if (build_context.show_timings) {
 		show_timings(checker, &global_timings);
 		show_timings(checker, &global_timings);

+ 139 - 34
src/parser.cpp

@@ -1,7 +1,21 @@
 #include "parser_pos.cpp"
 #include "parser_pos.cpp"
 
 
-// #undef at the bottom of this file
-#define ALLOW_NEWLINE (!build_context.strict_style)
+gb_internal u64 ast_file_vet_flags(AstFile *f) {
+	if (f->vet_flags_set) {
+		return f->vet_flags;
+	}
+	return build_context.vet_flags;
+}
+
+gb_internal bool ast_file_vet_style(AstFile *f) {
+	return (ast_file_vet_flags(f) & VetFlag_Style) != 0;
+}
+
+
+gb_internal bool file_allow_newline(AstFile *f) {
+	bool is_strict = build_context.strict_style || ast_file_vet_style(f);
+	return !is_strict;
+}
 
 
 gb_internal Token token_end_of_line(AstFile *f, Token tok) {
 gb_internal Token token_end_of_line(AstFile *f, Token tok) {
 	u8 const *start = f->tokenizer.start + tok.pos.offset;
 	u8 const *start = f->tokenizer.start + tok.pos.offset;
@@ -1567,29 +1581,29 @@ gb_internal void assign_removal_flag_to_semicolon(AstFile *f) {
 	Token *prev_token = &f->tokens[f->prev_token_index];
 	Token *prev_token = &f->tokens[f->prev_token_index];
 	Token *curr_token = &f->tokens[f->curr_token_index];
 	Token *curr_token = &f->tokens[f->curr_token_index];
 	GB_ASSERT(prev_token->kind == Token_Semicolon);
 	GB_ASSERT(prev_token->kind == Token_Semicolon);
-	if (prev_token->string == ";") {
-		bool ok = false;
-		if (curr_token->pos.line > prev_token->pos.line) {
+	if (prev_token->string != ";") {
+		return;
+	}
+	bool ok = false;
+	if (curr_token->pos.line > prev_token->pos.line) {
+		ok = true;
+	} else if (curr_token->pos.line == prev_token->pos.line) {
+		switch (curr_token->kind) {
+		case Token_CloseBrace:
+		case Token_CloseParen:
+		case Token_EOF:
 			ok = true;
 			ok = true;
-		} else if (curr_token->pos.line == prev_token->pos.line) {
-			switch (curr_token->kind) {
-			case Token_CloseBrace:
-			case Token_CloseParen:
-			case Token_EOF:
-				ok = true;
-				break;
-			}
-		}
-			
-		if (ok) {
-			if (build_context.strict_style) {
-				syntax_error(*prev_token, "Found unneeded semicolon");
-			} else if (build_context.strict_style_init_only && f->pkg->kind == Package_Init) {
-				syntax_error(*prev_token, "Found unneeded semicolon");
-			}
-			prev_token->flags |= TokenFlag_Remove;
+			break;
 		}
 		}
 	}
 	}
+	if (!ok) {
+		return;
+	}
+
+	if (build_context.strict_style || (ast_file_vet_flags(f) & VetFlag_Semicolon)) {
+		syntax_error(*prev_token, "Found unneeded semicolon");
+	}
+	prev_token->flags |= TokenFlag_Remove;
 }
 }
 
 
 gb_internal void expect_semicolon(AstFile *f) {
 gb_internal void expect_semicolon(AstFile *f) {
@@ -2221,7 +2235,11 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
 			return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert);
 			return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert);
 		} else if (name.string == "relative") {
 		} else if (name.string == "relative") {
 			Ast *tag = ast_basic_directive(f, token, name);
 			Ast *tag = ast_basic_directive(f, token, name);
-			tag = parse_call_expr(f, tag);
+			if (f->curr_token.kind != Token_OpenParen) {
+				syntax_error(tag, "expected #relative(<integer type>) <type>");
+			} else {
+				tag = parse_call_expr(f, tag);
+			}
 			Ast *type = parse_type(f);
 			Ast *type = parse_type(f);
 			return ast_relative_type(f, tag, type);
 			return ast_relative_type(f, tag, type);
 		} else if (name.string == "force_inline" ||
 		} else if (name.string == "force_inline" ||
@@ -2748,7 +2766,7 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) {
 	isize prev_expr_level = f->expr_level;
 	isize prev_expr_level = f->expr_level;
 	bool prev_allow_newline = f->allow_newline;
 	bool prev_allow_newline = f->allow_newline;
 	f->expr_level = 0;
 	f->expr_level = 0;
-	f->allow_newline = ALLOW_NEWLINE;
+	f->allow_newline = file_allow_newline(f);
 
 
 	open_paren = expect_token(f, Token_OpenParen);
 	open_paren = expect_token(f, Token_OpenParen);
 
 
@@ -3147,7 +3165,7 @@ gb_internal Ast *parse_expr(AstFile *f, bool lhs) {
 
 
 gb_internal Array<Ast *> parse_expr_list(AstFile *f, bool lhs) {
 gb_internal Array<Ast *> parse_expr_list(AstFile *f, bool lhs) {
 	bool allow_newline = f->allow_newline;
 	bool allow_newline = f->allow_newline;
-	f->allow_newline = ALLOW_NEWLINE;
+	f->allow_newline = file_allow_newline(f);
 
 
 	auto list = array_make<Ast *>(heap_allocator());
 	auto list = array_make<Ast *>(heap_allocator());
 	for (;;) {
 	for (;;) {
@@ -3472,7 +3490,7 @@ gb_internal Ast *parse_results(AstFile *f, bool *diverging) {
 	Ast *list = nullptr;
 	Ast *list = nullptr;
 	expect_token(f, Token_OpenParen);
 	expect_token(f, Token_OpenParen);
 	list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false);
 	list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false);
-	if (ALLOW_NEWLINE) {
+	if (file_allow_newline(f)) {
 		skip_possible_newline(f);
 		skip_possible_newline(f);
 	}
 	}
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	expect_token_after(f, Token_CloseParen, "parameter list");
@@ -3532,7 +3550,7 @@ gb_internal Ast *parse_proc_type(AstFile *f, Token proc_token) {
 
 
 	expect_token(f, Token_OpenParen);
 	expect_token(f, Token_OpenParen);
 	params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
 	params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
-	if (ALLOW_NEWLINE) {
+	if (file_allow_newline(f)) {
 		skip_possible_newline(f);
 		skip_possible_newline(f);
 	}
 	}
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	expect_token_after(f, Token_CloseParen, "parameter list");
@@ -3754,7 +3772,7 @@ gb_internal bool allow_field_separator(AstFile *f) {
 	}
 	}
 	if (token.kind == Token_Semicolon) {
 	if (token.kind == Token_Semicolon) {
 		bool ok = false;
 		bool ok = false;
-		if (ALLOW_NEWLINE && token_is_newline(token)) {
+		if (file_allow_newline(f) && token_is_newline(token)) {
 			TokenKind next = peek_token(f).kind;
 			TokenKind next = peek_token(f).kind;
 			switch (next) {
 			switch (next) {
 			case Token_CloseBrace:
 			case Token_CloseBrace:
@@ -3818,7 +3836,7 @@ gb_internal bool check_procedure_name_list(Array<Ast *> const &names) {
 gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) {
 gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) {
 	bool prev_allow_newline = f->allow_newline;
 	bool prev_allow_newline = f->allow_newline;
 	defer (f->allow_newline = prev_allow_newline);
 	defer (f->allow_newline = prev_allow_newline);
-	f->allow_newline = ALLOW_NEWLINE;
+	f->allow_newline = file_allow_newline(f);
 
 
 	Token start_token = f->curr_token;
 	Token start_token = f->curr_token;
 
 
@@ -4169,6 +4187,8 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
 		syntax_error(f->curr_token, "Expected condition for when statement");
 		syntax_error(f->curr_token, "Expected condition for when statement");
 	}
 	}
 
 
+	bool was_in_when_statement = f->in_when_statement;
+	f->in_when_statement = true;
 	if (allow_token(f, Token_do)) {
 	if (allow_token(f, Token_do)) {
 		body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement");
 		body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement");
 	} else {
 	} else {
@@ -4195,6 +4215,7 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
 			break;
 			break;
 		}
 		}
 	}
 	}
+	f->in_when_statement = was_in_when_statement;
 
 
 	return ast_when_stmt(f, token, cond, body, else_stmt);
 	return ast_when_stmt(f, token, cond, body, else_stmt);
 }
 }
@@ -4456,6 +4477,10 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
 		array_add(&f->imports, s);
 		array_add(&f->imports, s);
 	}
 	}
 
 
+	if (f->in_when_statement) {
+		syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags");
+	}
+
 	if (kind != ImportDecl_Standard) {
 	if (kind != ImportDecl_Standard) {
 		syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly");
 		syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly");
 	}
 	}
@@ -4954,7 +4979,6 @@ gb_internal bool init_parser(Parser *p) {
 
 
 gb_internal void destroy_parser(Parser *p) {
 gb_internal void destroy_parser(Parser *p) {
 	GB_ASSERT(p != nullptr);
 	GB_ASSERT(p != nullptr);
-	// TODO(bill): Fix memory leak
 	for (AstPackage *pkg : p->packages) {
 	for (AstPackage *pkg : p->packages) {
 		for (AstFile *file : pkg->files) {
 		for (AstFile *file : pkg->files) {
 			destroy_ast_file(file);
 			destroy_ast_file(file);
@@ -4998,7 +5022,6 @@ gb_internal WORKER_TASK_PROC(parser_worker_proc) {
 
 
 
 
 gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) {
 gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) {
-	// TODO(bill): Use a better allocator
 	ImportedFile f = {pkg, fi, pos, p->file_to_process_count++};
 	ImportedFile f = {pkg, fi, pos, p->file_to_process_count++};
 	auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData);
 	auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData);
 	wd->parser = p;
 	wd->parser = p;
@@ -5528,6 +5551,88 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
 	return any_correct;
 	return any_correct;
 }
 }
 
 
+gb_internal String vet_tag_get_token(String s, String *out) {
+	s = string_trim_whitespace(s);
+	isize n = 0;
+	while (n < s.len) {
+		Rune rune = 0;
+		isize width = utf8_decode(&s[n], s.len-n, &rune);
+		if (n == 0 && rune == '!') {
+
+		} else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-') {
+			isize k = gb_max(gb_max(n, width), 1);
+			*out = substring(s, k, s.len);
+			return substring(s, 0, k);
+		}
+		n += width;
+	}
+	out->len = 0;
+	return s;
+}
+
+
+gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
+	String const prefix = str_lit("+vet");
+	GB_ASSERT(string_starts_with(s, prefix));
+	s = string_trim_whitespace(substring(s, prefix.len, s.len));
+
+	if (s.len == 0) {
+		return VetFlag_All;
+	}
+
+
+	u64 vet_flags = 0;
+	u64 vet_not_flags = 0;
+
+	while (s.len > 0) {
+		String p = string_trim_whitespace(vet_tag_get_token(s, &s));
+		if (p.len == 0) {
+			break;
+		}
+
+		bool is_notted = false;
+		if (p[0] == '!') {
+			is_notted = true;
+			p = substring(p, 1, p.len);
+			if (p.len == 0) {
+				syntax_error(token_for_pos, "Expected a vet flag name after '!'");
+				return build_context.vet_flags;
+			}
+		}
+
+		u64 flag = get_vet_flag_from_name(p);
+		if (flag != VetFlag_NONE) {
+			if (is_notted) {
+				vet_not_flags |= flag;
+			} else {
+				vet_flags     |= flag;
+			}
+		} else {
+			ERROR_BLOCK();
+			syntax_error(token_for_pos, "Invalid vet flag name: %.*s", LIT(p));
+			error_line("\tExpected one of the following\n");
+			error_line("\tunused\n");
+			error_line("\tshadowing\n");
+			error_line("\tusing-stmt\n");
+			error_line("\tusing-param\n");
+			error_line("\textra\n");
+			return build_context.vet_flags;
+		}
+	}
+
+	if (vet_flags == 0 && vet_not_flags == 0) {
+		return build_context.vet_flags;
+	}
+	if (vet_flags == 0 && vet_not_flags != 0) {
+		return build_context.vet_flags &~ vet_not_flags;
+	}
+	if (vet_flags != 0 && vet_not_flags == 0) {
+		return vet_flags;
+	}
+	GB_ASSERT(vet_flags != 0 && vet_not_flags != 0);
+	return vet_flags &~ vet_not_flags;
+}
+
 gb_internal String dir_from_path(String path) {
 gb_internal String dir_from_path(String path) {
 	String base_dir = path;
 	String base_dir = path;
 	for (isize i = path.len-1; i >= 0; i--) {
 	for (isize i = path.len-1; i >= 0; i--) {
@@ -5679,6 +5784,9 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
 						if (!parse_build_tag(tok, lc)) {
 						if (!parse_build_tag(tok, lc)) {
 							return false;
 							return false;
 						}
 						}
+					} else if (string_starts_with(lc, str_lit("+vet"))) {
+						f->vet_flags = parse_vet_tag(tok, lc);
+						f->vet_flags_set = true;
 					} else if (string_starts_with(lc, str_lit("+ignore"))) {
 					} else if (string_starts_with(lc, str_lit("+ignore"))) {
 							return false;
 							return false;
 					} else if (string_starts_with(lc, str_lit("+private"))) {
 					} else if (string_starts_with(lc, str_lit("+private"))) {
@@ -5920,6 +6028,3 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
 	return ParseFile_None;
 	return ParseFile_None;
 }
 }
 
 
-
-
-#undef ALLOW_NEWLINE

+ 3 - 0
src/parser.hpp

@@ -104,6 +104,8 @@ struct AstFile {
 	Token        package_token;
 	Token        package_token;
 	String       package_name;
 	String       package_name;
 
 
+	u64          vet_flags;
+	bool         vet_flags_set;
 
 
 	// >= 0: In Expression
 	// >= 0: In Expression
 	// <  0: In Control Clause
 	// <  0: In Control Clause
@@ -114,6 +116,7 @@ struct AstFile {
 	bool         allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases
 	bool         allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases
 	bool         in_foreign_block;
 	bool         in_foreign_block;
 	bool         allow_type;
 	bool         allow_type;
+	bool         in_when_statement;
 
 
 	isize total_file_decl_count;
 	isize total_file_decl_count;
 	isize delayed_decl_count;
 	isize delayed_decl_count;

+ 813 - 0
src/tilde.cpp

@@ -0,0 +1,813 @@
+#include "tilde.hpp"
+
+
+gb_global Slice<TB_Arena> global_tb_arenas;
+
+gb_internal TB_Arena *cg_arena(void) {
+	return &global_tb_arenas[current_thread_index()];
+}
+
+gb_internal void cg_global_arena_init(void) {
+	global_tb_arenas = slice_make<TB_Arena>(permanent_allocator(), global_thread_pool.threads.count);
+	for_array(i, global_tb_arenas) {
+		tb_arena_create(&global_tb_arenas[i], 2ull<<20);
+	}
+}
+
+// returns TB_TYPE_VOID if not trivially possible
+gb_internal TB_DataType cg_data_type(Type *t) {
+	GB_ASSERT(t != nullptr);
+	t = core_type(t);
+	i64 sz = type_size_of(t);
+	switch (t->kind) {
+	case Type_Basic:
+		switch (t->Basic.kind) {
+		case Basic_bool:
+		case Basic_b8:
+		case Basic_b16:
+		case Basic_b32:
+		case Basic_b64:
+
+		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_rune:
+
+		case Basic_int:
+		case Basic_uint:
+		case Basic_uintptr:
+		case Basic_typeid:
+			return TB_TYPE_INTN(cast(u16)gb_min(8*sz, 64));
+
+		case Basic_f16: return TB_TYPE_F16;
+		case Basic_f32: return TB_TYPE_F32;
+		case Basic_f64: return TB_TYPE_F64;
+
+		case Basic_rawptr:  return TB_TYPE_PTR;
+		case Basic_cstring: return TB_TYPE_PTR;
+
+
+		// Endian Specific Types
+		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:
+			return TB_TYPE_INTN(cast(u16)gb_min(8*sz, 64));
+
+		case Basic_f16le: return TB_TYPE_F16;
+		case Basic_f32le: return TB_TYPE_F32;
+		case Basic_f64le: return TB_TYPE_F64;
+
+		case Basic_f16be: return TB_TYPE_F16;
+		case Basic_f32be: return TB_TYPE_F32;
+		case Basic_f64be: return TB_TYPE_F64;
+		}
+		break;
+
+	case Type_Pointer:
+	case Type_MultiPointer:
+	case Type_Proc:
+		return TB_TYPE_PTR;
+
+	case Type_BitSet:
+		return cg_data_type(bit_set_to_int(t));
+
+	case Type_RelativePointer:
+		return cg_data_type(t->RelativePointer.base_integer);
+	}
+
+	// unknown
+	return {};
+}
+
+
+gb_internal cgValue cg_value(TB_Global *g, Type *type) {
+	return cg_value((TB_Symbol *)g, type);
+}
+gb_internal cgValue cg_value(TB_External *e, Type *type) {
+	return cg_value((TB_Symbol *)e, type);
+}
+gb_internal cgValue cg_value(TB_Function *f, Type *type) {
+	return cg_value((TB_Symbol *)f, type);
+}
+gb_internal cgValue cg_value(TB_Symbol *s, Type *type) {
+	cgValue v = {};
+	v.kind = cgValue_Symbol;
+	v.type = type;
+	v.symbol = s;
+	return v;
+}
+gb_internal cgValue cg_value(TB_Node *node, Type *type) {
+	cgValue v = {};
+	v.kind = cgValue_Value;
+	v.type = type;
+	v.node = node;
+	return v;
+}
+gb_internal cgValue cg_lvalue_addr(TB_Node *node, Type *type) {
+	GB_ASSERT(node->dt.type == TB_PTR);
+	cgValue v = {};
+	v.kind = cgValue_Addr;
+	v.type = type;
+	v.node = node;
+	return v;
+}
+
+gb_internal cgValue cg_lvalue_addr_to_value(cgValue v) {
+	if (v.kind == cgValue_Value) {
+		GB_ASSERT(is_type_pointer(v.type));
+		GB_ASSERT(v.node->dt.type == TB_PTR);
+	} else {
+		GB_ASSERT(v.kind == cgValue_Addr);
+		GB_ASSERT(v.node->dt.type == TB_PTR);
+		v.kind = cgValue_Value;
+		v.type = alloc_type_pointer(v.type);
+	}
+	return v;
+}
+
+gb_internal cgValue cg_value_multi(cgValueMulti *multi, Type *type) {
+	GB_ASSERT(type->kind == Type_Tuple);
+	GB_ASSERT(multi != nullptr);
+	GB_ASSERT(type->Tuple.variables.count > 1);
+	GB_ASSERT(multi->values.count == type->Tuple.variables.count);
+	cgValue v = {};
+	v.kind = cgValue_Multi;
+	v.type = type;
+	v.multi = multi;
+	return v;
+}
+
+gb_internal cgValue cg_value_multi(Slice<cgValue> const &values, Type *type) {
+	cgValueMulti *multi = gb_alloc_item(permanent_allocator(), cgValueMulti);
+	multi->values = values;
+	return cg_value_multi(multi, type);
+}
+
+
+gb_internal cgValue cg_value_multi2(cgValue const &x, cgValue const &y, Type *type) {
+	GB_ASSERT(type->kind == Type_Tuple);
+	GB_ASSERT(type->Tuple.variables.count == 2);
+	cgValueMulti *multi = gb_alloc_item(permanent_allocator(), cgValueMulti);
+	multi->values = slice_make<cgValue>(permanent_allocator(), 2);
+	multi->values[0] = x;
+	multi->values[1] = y;
+	return cg_value_multi(multi, type);
+}
+
+
+gb_internal cgAddr cg_addr(cgValue const &value) {
+	GB_ASSERT(value.kind != cgValue_Multi);
+	cgAddr addr = {};
+	addr.kind = cgAddr_Default;
+	addr.addr = value;
+	if (addr.addr.kind == cgValue_Addr) {
+		GB_ASSERT(addr.addr.node != nullptr);
+		addr.addr.kind = cgValue_Value;
+		addr.addr.type = alloc_type_pointer(addr.addr.type);
+	}
+	return addr;
+}
+
+gb_internal void cg_set_debug_pos_from_node(cgProcedure *p, Ast *node) {
+	if (node) {
+		TokenPos pos = ast_token(node).pos;
+		TB_FileID *file_id = map_get(&p->module->file_id_map, cast(uintptr)pos.file_id);
+		if (file_id) {
+			tb_inst_set_location(p->func, *file_id, pos.line);
+		}
+	}
+}
+
+gb_internal void cg_add_symbol(cgModule *m, Entity *e, TB_Symbol *symbol) {
+	if (e) {
+		rw_mutex_lock(&m->values_mutex);
+		map_set(&m->symbols, e, symbol);
+		rw_mutex_unlock(&m->values_mutex);
+	}
+}
+
+gb_internal void cg_add_entity(cgModule *m, Entity *e, cgValue const &val) {
+	if (e) {
+		rw_mutex_lock(&m->values_mutex);
+		GB_ASSERT(val.node != nullptr);
+		map_set(&m->values, e, val);
+		rw_mutex_unlock(&m->values_mutex);
+	}
+}
+
+gb_internal void cg_add_member(cgModule *m, String const &name, cgValue const &val) {
+	if (name.len > 0) {
+		rw_mutex_lock(&m->values_mutex);
+		string_map_set(&m->members, name, val);
+		rw_mutex_unlock(&m->values_mutex);
+	}
+}
+
+gb_internal void cg_add_procedure_value(cgModule *m, cgProcedure *p) {
+	rw_mutex_lock(&m->values_mutex);
+	if (p->entity != nullptr) {
+		map_set(&m->procedure_values, p->func, p->entity);
+		if (p->symbol != nullptr) {
+			map_set(&m->symbols, p->entity, p->symbol);
+		}
+	}
+	string_map_set(&m->procedures, p->name, p);
+	rw_mutex_unlock(&m->values_mutex);
+
+}
+
+gb_internal TB_Symbol *cg_find_symbol_from_entity(cgModule *m, Entity *e) {
+	GB_ASSERT(e != nullptr);
+
+	rw_mutex_lock(&m->values_mutex);
+	TB_Symbol **found = map_get(&m->symbols, e);
+	if (found) {
+		rw_mutex_unlock(&m->values_mutex);
+		return *found;
+	}
+
+	String link_name = cg_get_entity_name(m, e);
+	cgProcedure **proc_found = string_map_get(&m->procedures, link_name);
+	if (proc_found) {
+		TB_Symbol *symbol = (*proc_found)->symbol;
+		map_set(&m->symbols, e, symbol);
+		rw_mutex_unlock(&m->values_mutex);
+		return symbol;
+	}
+	rw_mutex_unlock(&m->values_mutex);
+
+	if (e->kind == Entity_Procedure) {
+		debugf("[Tilde] try to generate procedure %.*s as it was not in the minimum_dependency_set", LIT(e->token.string));
+		// IMPORTANT TODO(bill): This is an utter bodge, try and fix this shit
+		cgProcedure *p = cg_procedure_create(m, e);
+		if (p != nullptr) {
+			GB_ASSERT(p->symbol != nullptr);
+			cg_add_procedure_to_queue(p);
+			return p->symbol;
+		}
+	}
+
+
+	GB_PANIC("could not find entity's symbol %.*s", LIT(e->token.string));
+	return nullptr;
+}
+
+
+struct cgGlobalVariable {
+	cgValue var;
+	cgValue init;
+	DeclInfo *decl;
+	bool is_initialized;
+};
+
+// Returns already_has_entry_point
+gb_internal bool cg_global_variables_create(cgModule *m, Array<cgGlobalVariable> *global_variables) {
+	isize global_variable_max_count = 0;
+	bool already_has_entry_point = false;
+
+	for (Entity *e : m->info->entities) {
+		String name = e->token.string;
+
+		if (e->kind == Entity_Variable) {
+			global_variable_max_count++;
+		} else if (e->kind == Entity_Procedure) {
+			if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
+				GB_ASSERT(e == m->info->entry_point);
+			}
+			if (build_context.command_kind == Command_test &&
+			    (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
+				String link_name = e->Procedure.link_name;
+				if (e->pkg->kind == Package_Runtime) {
+					if (link_name == "main"           ||
+					    link_name == "DllMain"        ||
+					    link_name == "WinMain"        ||
+					    link_name == "wWinMain"       ||
+					    link_name == "mainCRTStartup" ||
+					    link_name == "_start") {
+						already_has_entry_point = true;
+					}
+				}
+			}
+		}
+	}
+	*global_variables = array_make<cgGlobalVariable>(permanent_allocator(), 0, global_variable_max_count);
+
+	auto *min_dep_set = &m->info->minimum_dependency_set;
+
+	for (DeclInfo *d : m->info->variable_init_order) {
+		Entity *e = d->entity;
+
+		if ((e->scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
+
+		if (!ptr_set_exists(min_dep_set, e)) {
+			continue;
+		}
+
+		DeclInfo *decl = decl_info_of_entity(e);
+		if (decl == nullptr) {
+			continue;
+		}
+		GB_ASSERT(e->kind == Entity_Variable);
+
+		bool is_foreign = e->Variable.is_foreign;
+		bool is_export  = e->Variable.is_export;
+
+		String name = cg_get_entity_name(m, e);
+
+		TB_Linkage linkage = TB_LINKAGE_PRIVATE;
+
+		if (is_foreign) {
+			linkage = TB_LINKAGE_PUBLIC;
+			// lb_add_foreign_library_path(m, e->Variable.foreign_library);
+			// lb_set_wasm_import_attributes(g.value, e, name);
+		} else if (is_export) {
+			linkage = TB_LINKAGE_PUBLIC;
+		}
+		// lb_set_linkage_from_entity_flags(m, g.value, e->flags);
+
+		TB_DebugType *debug_type = cg_debug_type(m, e->type);
+		TB_Global *global = tb_global_create(m->mod, name.len, cast(char const *)name.text, debug_type, linkage);
+		cgValue g = cg_value(global, alloc_type_pointer(e->type));
+
+		TB_ModuleSection *section = tb_module_get_data(m->mod);
+
+		if (e->Variable.thread_local_model != "") {
+			section = tb_module_get_tls(m->mod);
+		}
+		if (e->Variable.link_section.len > 0) {
+			// TODO(bill): custom module sections
+			// LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
+		}
+
+
+		cgGlobalVariable var = {};
+		var.var = g;
+		var.decl = decl;
+
+		if (decl->init_expr != nullptr) {
+			TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
+
+			isize max_regions = cg_global_const_calculate_region_count(tav.value, e->type);
+			tb_global_set_storage(m->mod, section, global, type_size_of(e->type), type_align_of(e->type), max_regions);
+
+			if (tav.mode == Addressing_Constant &&
+			    tav.value.kind != ExactValue_Invalid) {
+				cg_global_const_add_region(m, tav.value, e->type, global, 0);
+				var.is_initialized = true;
+			}
+			if (!var.is_initialized && is_type_untyped_nil(tav.type)) {
+				var.is_initialized = true;
+			}
+		} else {
+			var.is_initialized = true;
+			// TODO(bill): is this even needed;
+			i64 max_regions = cg_global_const_calculate_region_count_from_basic_type(e->type);
+			tb_global_set_storage(m->mod, section, global, type_size_of(e->type), type_align_of(e->type), max_regions);
+		}
+
+		array_add(global_variables, var);
+
+		cg_add_symbol(m, e, cast(TB_Symbol *)global);
+		cg_add_entity(m, e, g);
+		cg_add_member(m, name, g);
+	}
+
+	cg_setup_type_info_data(m);
+
+	return already_has_entry_point;
+}
+
+gb_internal void cg_global_variables_initialize(cgProcedure *p, Array<cgGlobalVariable> *global_variables) {
+	for (cgGlobalVariable &var : *global_variables) {
+		if (var.is_initialized) {
+			continue;
+		}
+		cgValue src = cg_build_expr(p, var.decl->init_expr);
+		cgValue dst = cg_flatten_value(p, var.var);
+		cg_emit_store(p, dst, src);
+	}
+}
+
+
+gb_internal cgModule *cg_module_create(Checker *c) {
+	cgModule *m = gb_alloc_item(permanent_allocator(), cgModule);
+
+	m->checker = c;
+	m->info = &c->info;
+
+
+	TB_FeatureSet feature_set = {};
+	bool is_jit = false;
+	m->mod = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_WINDOWS, &feature_set, is_jit);
+	tb_module_set_tls_index(m->mod, 10, "_tls_index");
+
+	map_init(&m->values);
+	map_init(&m->symbols);
+	map_init(&m->file_id_map);
+	map_init(&m->debug_type_map);
+	map_init(&m->proc_debug_type_map);
+	map_init(&m->proc_proto_map);
+	map_init(&m->anonymous_proc_lits_map);
+	map_init(&m->equal_procs);
+	map_init(&m->hasher_procs);
+	map_init(&m->map_get_procs);
+	map_init(&m->map_set_procs);
+
+	array_init(&m->single_threaded_procedure_queue, heap_allocator());
+
+
+	for_array(id, global_files) {
+		if (AstFile *f = global_files[id]) {
+			char const *path = alloc_cstring(permanent_allocator(), f->fullpath);
+			map_set(&m->file_id_map, cast(uintptr)id, tb_file_create(m->mod, path));
+		}
+	}
+
+	return m;
+}
+
+gb_internal void cg_module_destroy(cgModule *m) {
+	map_destroy(&m->values);
+	map_destroy(&m->symbols);
+	map_destroy(&m->file_id_map);
+	map_destroy(&m->debug_type_map);
+	map_destroy(&m->proc_debug_type_map);
+	map_destroy(&m->proc_proto_map);
+	map_destroy(&m->anonymous_proc_lits_map);
+	map_destroy(&m->equal_procs);
+	map_destroy(&m->hasher_procs);
+	map_destroy(&m->map_get_procs);
+	map_destroy(&m->map_set_procs);
+
+	array_free(&m->single_threaded_procedure_queue);
+
+	tb_module_destroy(m->mod);
+}
+
+gb_internal String cg_set_nested_type_name_ir_mangled_name(Entity *e, cgProcedure *p) {
+	// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
+	// and as a result, the declaration does not have time to determine what it should be
+
+	GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
+	if (e->TypeName.ir_mangled_name.len != 0)  {
+		return e->TypeName.ir_mangled_name;
+	}
+	GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
+
+	if (p == nullptr) {
+		Entity *proc = nullptr;
+		if (e->parent_proc_decl != nullptr) {
+			proc = e->parent_proc_decl->entity;
+		} else {
+			Scope *scope = e->scope;
+			while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
+				scope = scope->parent;
+			}
+			GB_ASSERT(scope != nullptr);
+			GB_ASSERT(scope->flags & ScopeFlag_Proc);
+			proc = scope->procedure_entity;
+		}
+		GB_ASSERT(proc->kind == Entity_Procedure);
+		if (proc->cg_procedure != nullptr) {
+			p = proc->cg_procedure;
+		}
+	}
+
+	// NOTE(bill): Generate a new name
+	// parent_proc.name-guid
+	String ts_name = e->token.string;
+
+	if (p != nullptr) {
+		isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
+		char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+		u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
+		name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid);
+
+		String name = make_string(cast(u8 *)name_text, name_len-1);
+		e->TypeName.ir_mangled_name = name;
+		return name;
+	} else {
+		// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
+		isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
+		char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+		static std::atomic<u32> guid;
+		name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
+
+		String name = make_string(cast(u8 *)name_text, name_len-1);
+		e->TypeName.ir_mangled_name = name;
+		return name;
+	}
+}
+
+gb_internal String cg_mangle_name(cgModule *m, Entity *e) {
+	String name = e->token.string;
+
+	AstPackage *pkg = e->pkg;
+	GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
+	String pkgn = pkg->name;
+	GB_ASSERT(!rune_is_digit(pkgn[0]));
+	if (pkgn == "llvm") {
+		GB_PANIC("llvm. entities are not allowed with the tilde backend");
+	}
+
+	isize max_len = pkgn.len + 1 + name.len + 1;
+	bool require_suffix_id = is_type_polymorphic(e->type, true);
+
+	if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
+		require_suffix_id = true;
+	} else if (is_blank_ident(e->token)) {
+		require_suffix_id = true;
+	}if (e->flags & EntityFlag_NotExported) {
+		require_suffix_id = true;
+	}
+
+	if (require_suffix_id) {
+		max_len += 21;
+	}
+
+	char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
+	isize new_name_len = gb_snprintf(
+		new_name, max_len,
+		"%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name)
+	);
+	if (require_suffix_id) {
+		char *str = new_name + new_name_len-1;
+		isize len = max_len-new_name_len;
+		isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
+		new_name_len += extra-1;
+	}
+
+	String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
+	return mangled_name;
+}
+
+gb_internal String cg_get_entity_name(cgModule *m, Entity *e) {
+	if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
+		return e->TypeName.ir_mangled_name;
+	}
+	GB_ASSERT(e != nullptr);
+
+	if (e->pkg == nullptr) {
+		return e->token.string;
+	}
+
+	if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
+		return cg_set_nested_type_name_ir_mangled_name(e, nullptr);
+	}
+
+	String name = {};
+
+	bool no_name_mangle = false;
+
+	if (e->kind == Entity_Variable) {
+		bool is_foreign = e->Variable.is_foreign;
+		bool is_export  = e->Variable.is_export;
+		no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
+		if (e->Variable.link_name.len > 0) {
+			return e->Variable.link_name;
+		}
+	} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
+		return e->Procedure.link_name;
+	} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
+		no_name_mangle = true;
+	}
+
+	if (!no_name_mangle) {
+		name = cg_mangle_name(m, e);
+	}
+	if (name.len == 0) {
+		name = e->token.string;
+	}
+
+	if (e->kind == Entity_TypeName) {
+		e->TypeName.ir_mangled_name = name;
+	} else if (e->kind == Entity_Procedure) {
+		e->Procedure.link_name = name;
+	}
+
+	return name;
+}
+
+#include "tilde_const.cpp"
+#include "tilde_debug.cpp"
+#include "tilde_expr.cpp"
+#include "tilde_builtin.cpp"
+#include "tilde_type_info.cpp"
+#include "tilde_proc.cpp"
+#include "tilde_stmt.cpp"
+
+
+gb_internal String cg_filepath_obj_for_module(cgModule *m) {
+	String path = concatenate3_strings(permanent_allocator(),
+		build_context.build_paths[BuildPath_Output].basename,
+		STR_LIT("/"),
+		build_context.build_paths[BuildPath_Output].name
+	);
+
+	// if (m->file) {
+	// 	char buf[32] = {};
+	// 	isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
+	// 	String suffix = make_string((u8 *)buf, n-1);
+	// 	path = concatenate_strings(permanent_allocator(), path, suffix);
+	// } else if (m->pkg) {
+	// 	path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
+	// }
+
+	String ext = {};
+
+	if (build_context.build_mode == BuildMode_Assembly) {
+		ext = STR_LIT(".S");
+	} else {
+		if (is_arch_wasm()) {
+			ext = STR_LIT(".wasm.o");
+		} else {
+			switch (build_context.metrics.os) {
+			case TargetOs_windows:
+				ext = STR_LIT(".obj");
+				break;
+			default:
+			case TargetOs_darwin:
+			case TargetOs_linux:
+			case TargetOs_essence:
+				ext = STR_LIT(".o");
+				break;
+
+			case TargetOs_freestanding:
+				switch (build_context.metrics.abi) {
+				default:
+				case TargetABI_Default:
+				case TargetABI_SysV:
+					ext = STR_LIT(".o");
+					break;
+				case TargetABI_Win64:
+					ext = STR_LIT(".obj");
+					break;
+				}
+				break;
+			}
+		}
+	}
+
+	return concatenate_strings(permanent_allocator(), path, ext);
+}
+
+
+gb_internal WORKER_TASK_PROC(cg_procedure_generate_worker_proc) {
+	cgProcedure *p = cast(cgProcedure *)data;
+	cg_procedure_generate(p);
+	return 0;
+}
+
+gb_internal void cg_add_procedure_to_queue(cgProcedure *p) {
+	if (p == nullptr) {
+		return;
+	}
+	cgModule *m = p->module;
+	if (m->do_threading) {
+		thread_pool_add_task(cg_procedure_generate_worker_proc, p);
+	} else {
+		array_add(&m->single_threaded_procedure_queue, p);
+	}
+}
+
+gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
+	TIME_SECTION("Tilde Module Initializtion");
+
+	CheckerInfo *info = &c->info;
+
+	linker_data_init(linker_data, info, c->parser->init_fullpath);
+
+	cg_global_arena_init();
+
+	cgModule *m = cg_module_create(c);
+	defer (cg_module_destroy(m));
+
+	m->do_threading = false;
+
+	TIME_SECTION("Tilde Global Variables");
+
+	Array<cgGlobalVariable> global_variables = {};
+	bool already_has_entry_point = cg_global_variables_create(m, &global_variables);
+	gb_unused(already_has_entry_point);
+
+	if (true) {
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+		cgProcedure *p = cg_procedure_create_dummy(m, str_lit(CG_STARTUP_RUNTIME_PROC_NAME), proc_type);
+		p->is_startup = true;
+		cg_startup_runtime_proc = p;
+	}
+
+	if (true) {
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+		cgProcedure *p = cg_procedure_create_dummy(m, str_lit(CG_CLEANUP_RUNTIME_PROC_NAME), proc_type);
+		p->is_startup = true;
+		cg_cleanup_runtime_proc = p;
+	}
+
+	auto *min_dep_set = &info->minimum_dependency_set;
+
+	Array<cgProcedure *> procedures_to_generate = {};
+	array_init(&procedures_to_generate, heap_allocator());
+	defer (array_free(&procedures_to_generate));
+
+	for (Entity *e : info->entities) {
+		String name  = e->token.string;
+		Scope *scope = e->scope;
+
+		if ((scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
+
+		Scope *package_scope = scope->parent;
+		GB_ASSERT(package_scope->flags & ScopeFlag_Pkg);
+
+		if (e->kind != Entity_Procedure) {
+			continue;
+		}
+
+		if (!ptr_set_exists(min_dep_set, e)) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
+		if (cgProcedure *p = cg_procedure_create(m, e)) {
+			array_add(&procedures_to_generate, p);
+		}
+	}
+	for (cgProcedure *p : procedures_to_generate) {
+		cg_add_procedure_to_queue(p);
+	}
+
+	if (!m->do_threading) {
+		for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) {
+			cgProcedure *p = m->single_threaded_procedure_queue[i];
+			cg_procedure_generate(p);
+		}
+	}
+
+	thread_pool_wait();
+
+	{
+		cgProcedure *p = cg_startup_runtime_proc;
+		cg_procedure_begin(p);
+		cg_global_variables_initialize(p, &global_variables);
+		tb_inst_ret(p->func, 0, nullptr);
+		cg_procedure_end(p);
+	}
+	{
+		cgProcedure *p = cg_cleanup_runtime_proc;
+		cg_procedure_begin(p);
+		tb_inst_ret(p->func, 0, nullptr);
+		cg_procedure_end(p);
+	}
+
+
+
+	TB_DebugFormat debug_format = TB_DEBUGFMT_NONE;
+	if (build_context.ODIN_DEBUG) {
+		switch (build_context.metrics.os) {
+		case TargetOs_windows:
+			debug_format = TB_DEBUGFMT_CODEVIEW;
+			break;
+		case TargetOs_darwin:
+		case TargetOs_linux:
+		case TargetOs_essence:
+		case TargetOs_freebsd:
+		case TargetOs_openbsd:
+			debug_format = TB_DEBUGFMT_DWARF;
+			break;
+		}
+	}
+	TB_ExportBuffer export_buffer = tb_module_object_export(m->mod, debug_format);
+	defer (tb_export_buffer_free(export_buffer));
+
+	String filepath_obj = cg_filepath_obj_for_module(m);
+	array_add(&linker_data->output_object_paths, filepath_obj);
+	GB_ASSERT(tb_export_buffer_to_file(export_buffer, cast(char const *)filepath_obj.text));
+
+	return true;
+}
+
+#undef ABI_PKG_NAME_SEPARATOR

+ 373 - 0
src/tilde.hpp

@@ -0,0 +1,373 @@
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(push)
+	#pragma warning(disable: 4200)
+	#pragma warning(disable: 4201)
+	#define restrict gb_restrict
+#endif
+
+#include "tilde/tb.h"
+#include "tilde/tb_arena.h"
+
+#define TB_TYPE_F16    TB_DataType{ { TB_INT, 0, 16 } }
+#define TB_TYPE_I128   TB_DataType{ { TB_INT, 0, 128 } }
+#define TB_TYPE_INT    TB_TYPE_INTN(cast(u16)(8*build_context.int_size))
+#define TB_TYPE_INTPTR TB_TYPE_INTN(cast(u16)(8*build_context.ptr_size))
+
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(pop)
+#endif
+
+#define CG_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
+#define CG_CLEANUP_RUNTIME_PROC_NAME   "__$cleanup_runtime"
+#define CG_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
+#define CG_TYPE_INFO_DATA_NAME         "__$type_info_data"
+#define CG_TYPE_INFO_TYPES_NAME        "__$type_info_types_data"
+#define CG_TYPE_INFO_NAMES_NAME        "__$type_info_names_data"
+#define CG_TYPE_INFO_OFFSETS_NAME      "__$type_info_offsets_data"
+#define CG_TYPE_INFO_USINGS_NAME       "__$type_info_usings_data"
+#define CG_TYPE_INFO_TAGS_NAME         "__$type_info_tags_data"
+#define CG_TYPE_INFO_ENUM_VALUES_NAME  "__$type_info_enum_values_data"
+
+struct cgModule;
+
+
+enum cgValueKind : u32 {
+	cgValue_Value,  // rvalue
+	cgValue_Addr,   // lvalue
+	cgValue_Symbol, // global
+	cgValue_Multi,  // multiple values
+};
+
+struct cgValueMulti;
+
+struct cgValue {
+	cgValueKind kind;
+	Type *      type;
+	union {
+		// NOTE: any value in this union must be a pointer
+		TB_Symbol *   symbol;
+		TB_Node *     node;
+		cgValueMulti *multi;
+	};
+};
+
+struct cgValueMulti {
+	Slice<cgValue> values;
+};
+
+
+enum cgAddrKind {
+	cgAddr_Default,
+	cgAddr_Map,
+	cgAddr_Context,
+	cgAddr_SoaVariable,
+
+	cgAddr_RelativePointer,
+	cgAddr_RelativeSlice,
+
+	cgAddr_Swizzle,
+	cgAddr_SwizzleLarge,
+};
+
+struct cgAddr {
+	cgAddrKind kind;
+	cgValue addr;
+	union {
+		struct {
+			cgValue key;
+			Type *type;
+			Type *result;
+		} map;
+		struct {
+			Selection sel;
+		} ctx;
+		struct {
+			cgValue index;
+			Ast *index_expr;
+		} soa;
+		struct {
+			cgValue index;
+			Ast *node;
+		} index_set;
+		struct {
+			bool deref;
+		} relative;
+		struct {
+			Type *type;
+			u8 count;      // 2, 3, or 4 components
+			u8 indices[4];
+		} swizzle;
+		struct {
+			Type *type;
+			Slice<i32> indices;
+		} swizzle_large;
+	};
+};
+
+
+struct cgTargetList {
+	cgTargetList *prev;
+	bool          is_block;
+	// control regions
+	TB_Node *     break_;
+	TB_Node *     continue_;
+	TB_Node *     fallthrough_;
+};
+
+struct cgBranchRegions {
+	Ast *    label;
+	TB_Node *break_;
+	TB_Node *continue_;
+};
+
+enum cgDeferExitKind {
+	cgDeferExit_Default,
+	cgDeferExit_Return,
+	cgDeferExit_Branch,
+};
+
+enum cgDeferKind {
+	cgDefer_Node,
+	cgDefer_Proc,
+};
+
+struct cgDefer {
+	cgDeferKind kind;
+	isize       scope_index;
+	isize       context_stack_count;
+	TB_Node *   control_region;
+	union {
+		Ast *stmt;
+		struct {
+			cgValue deferred;
+			Slice<cgValue> result_as_args;
+		} proc;
+	};
+};
+
+
+struct cgContextData {
+	cgAddr ctx;
+	isize scope_index;
+	isize uses;
+};
+
+struct cgControlRegion {
+	TB_Node *control_region;
+	isize    scope_index;
+};
+
+struct cgProcedure {
+	u32 flags;
+	u16 state_flags;
+
+	cgProcedure *parent;
+	Array<cgProcedure *> children;
+
+	TB_Function *func;
+	TB_FunctionPrototype *proto;
+	TB_Symbol *symbol;
+
+	Entity *  entity;
+	cgModule *module;
+	String    name;
+	Type *    type;
+	Ast *     type_expr;
+	Ast *     body;
+	u64       tags;
+	ProcInlining inlining;
+	bool         is_foreign;
+	bool         is_export;
+	bool         is_entry_point;
+	bool         is_startup;
+
+	TB_DebugType *debug_type;
+
+	cgValue value;
+
+	Ast *curr_stmt;
+
+	cgTargetList *         target_list;
+	Array<cgDefer>         defer_stack;
+	Array<Scope *>         scope_stack;
+	Array<cgContextData>   context_stack;
+
+	Array<cgControlRegion> control_regions;
+	Array<cgBranchRegions> branch_regions;
+
+	Scope *curr_scope;
+	i32    scope_index;
+	bool   in_multi_assignment;
+	isize  split_returns_index;
+	bool   return_by_ptr;
+
+	PtrMap<Entity *, cgAddr> variable_map;
+};
+
+
+struct cgModule {
+	TB_Module *  mod;
+	Checker *    checker;
+	CheckerInfo *info;
+	LinkerData * linker_data;
+
+	bool do_threading;
+	Array<cgProcedure *> single_threaded_procedure_queue;
+
+	RwMutex values_mutex;
+	PtrMap<Entity *, cgValue>       values;
+	PtrMap<Entity *, TB_Symbol *>   symbols;
+	StringMap<cgValue>              members;
+	StringMap<cgProcedure *>        procedures;
+	PtrMap<TB_Function *, Entity *> procedure_values;
+
+	RecursiveMutex debug_type_mutex;
+	PtrMap<Type *, TB_DebugType *> debug_type_map;
+	PtrMap<Type *, TB_DebugType *> proc_debug_type_map; // not pointer to
+
+	RecursiveMutex proc_proto_mutex;
+	PtrMap<Type *, TB_FunctionPrototype *> proc_proto_map;
+
+	BlockingMutex anonymous_proc_lits_mutex;
+	PtrMap<Ast *, cgProcedure *> anonymous_proc_lits_map;
+
+	RecursiveMutex generated_procs_mutex;
+	PtrMap<Type *, cgProcedure *> equal_procs;
+	PtrMap<Type *, cgProcedure *> hasher_procs;
+	PtrMap<Type *, cgProcedure *> map_get_procs;
+	PtrMap<Type *, cgProcedure *> map_set_procs;
+
+
+	// NOTE(bill): no need to protect this with a mutex
+	PtrMap<uintptr, TB_FileID> file_id_map; // Key: AstFile.id (i32 cast to uintptr)
+
+	std::atomic<u32> nested_type_name_guid;
+	std::atomic<u32> const_nil_guid;
+};
+
+#ifndef ABI_PKG_NAME_SEPARATOR
+#define ABI_PKG_NAME_SEPARATOR "@"
+#endif
+
+struct GlobalTypeInfoData {
+	TB_Global *global;
+	Type *     array_type;
+	Type *     elem_type;
+	isize      index;
+};
+
+gb_global Entity *cg_global_type_info_data_entity   = {};
+gb_global GlobalTypeInfoData cg_global_type_info_member_types       = {};
+gb_global GlobalTypeInfoData cg_global_type_info_member_names       = {};
+gb_global GlobalTypeInfoData cg_global_type_info_member_offsets     = {};
+gb_global GlobalTypeInfoData cg_global_type_info_member_usings      = {};
+gb_global GlobalTypeInfoData cg_global_type_info_member_tags        = {};
+gb_global GlobalTypeInfoData cg_global_type_info_member_enum_values = {};
+
+gb_global cgProcedure *cg_startup_runtime_proc = nullptr;
+gb_global cgProcedure *cg_cleanup_runtime_proc = nullptr;
+
+
+
+gb_internal TB_Arena *cg_arena(void);
+
+gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool ignore_body=false);
+gb_internal void cg_add_procedure_to_queue(cgProcedure *p);
+gb_internal void cg_setup_type_info_data(cgModule *m);
+gb_internal cgProcedure *cg_procedure_generate_anonymous(cgModule *m, Ast *expr, cgProcedure *parent);
+
+gb_internal isize cg_global_const_calculate_region_count(ExactValue const &value, Type *type);
+gb_internal i64   cg_global_const_calculate_region_count_from_basic_type(Type *type);
+gb_internal bool  cg_global_const_add_region(cgModule *m, ExactValue const &value, Type *type, TB_Global *global, i64 offset);
+
+gb_internal String cg_get_entity_name(cgModule *m, Entity *e);
+
+gb_internal cgValue cg_value(TB_Global *  g,    Type *type);
+gb_internal cgValue cg_value(TB_External *e,    Type *type);
+gb_internal cgValue cg_value(TB_Function *f,    Type *type);
+gb_internal cgValue cg_value(TB_Symbol *  s,    Type *type);
+gb_internal cgValue cg_value(TB_Node *    node, Type *type);
+
+gb_internal cgAddr cg_addr(cgValue const &value);
+
+gb_internal u64 cg_typeid_as_u64(cgModule *m, Type *type);
+gb_internal cgValue cg_type_info(cgProcedure *p, Type *type);
+gb_internal isize cg_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true);
+
+gb_internal cgValue cg_const_value(cgProcedure *p, Type *type, ExactValue const &value);
+gb_internal cgValue cg_const_nil(cgProcedure *p, Type *type);
+
+gb_internal cgValue cg_flatten_value(cgProcedure *p, cgValue value);
+
+gb_internal void cg_build_stmt(cgProcedure *p, Ast *stmt);
+gb_internal void cg_build_stmt_list(cgProcedure *p, Slice<Ast *> const &stmts);
+gb_internal void cg_build_when_stmt(cgProcedure *p, AstWhenStmt *ws);
+
+
+gb_internal cgValue cg_build_expr(cgProcedure *p, Ast *expr);
+gb_internal cgAddr  cg_build_addr(cgProcedure *p, Ast *expr);
+gb_internal cgValue cg_build_addr_ptr(cgProcedure *p, Ast *expr);
+gb_internal cgValue cg_build_cond(cgProcedure *p, Ast *cond, TB_Node *true_block, TB_Node *false_block);
+
+gb_internal Type *  cg_addr_type(cgAddr const &addr);
+gb_internal cgValue cg_addr_load(cgProcedure *p, cgAddr addr);
+gb_internal void    cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value);
+gb_internal cgValue cg_addr_get_ptr(cgProcedure *p, cgAddr const &addr);
+
+gb_internal cgValue cg_emit_load(cgProcedure *p, cgValue const &ptr, bool is_volatile=false);
+gb_internal void    cg_emit_store(cgProcedure *p, cgValue dst, cgValue src, bool is_volatile=false);
+
+gb_internal cgAddr  cg_add_local (cgProcedure *p, Type *type, Entity *e, bool zero_init);
+gb_internal cgAddr  cg_add_global(cgProcedure *p, Type *type, Entity *e);
+gb_internal cgValue cg_address_from_load_or_generate_local(cgProcedure *p, cgValue value);
+gb_internal cgValue cg_copy_value_to_ptr(cgProcedure *p, cgValue value, Type *original_type, isize min_alignment);
+
+gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr);
+
+gb_internal void cg_build_return_stmt(cgProcedure *p, Slice<Ast *> const &return_results);
+gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results);
+gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result);
+gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node);
+
+gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e);
+gb_internal cgValue cg_find_procedure_value_from_entity(cgModule *m, Entity *e);
+
+gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type);
+
+gb_internal String cg_get_entity_name(cgModule *m, Entity *e);
+
+gb_internal cgValue cg_typeid(cgProcedure *m, Type *t);
+
+gb_internal cgValue cg_emit_ptr_offset(cgProcedure *p, cgValue ptr, cgValue index);
+gb_internal cgValue cg_emit_array_ep(cgProcedure *p, cgValue s, cgValue index);
+gb_internal cgValue cg_emit_array_epi(cgProcedure *p, cgValue s, i64 index);
+gb_internal cgValue cg_emit_struct_ep(cgProcedure *p, cgValue s, i64 index);
+gb_internal cgValue cg_emit_deep_field_gep(cgProcedure *p, cgValue e, Selection const &sel);
+gb_internal cgValue cg_emit_struct_ev(cgProcedure *p, cgValue s, i64 index);
+
+gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t);
+gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x);
+gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right);
+gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type);
+gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x, Type *type);
+gb_internal void    cg_emit_increment(cgProcedure *p, cgValue addr);
+
+gb_internal cgProcedure *cg_equal_proc_for_type (cgModule *m, Type *type);
+gb_internal cgProcedure *cg_hasher_proc_for_type(cgModule *m, Type *type);
+gb_internal cgValue     cg_hasher_proc_value_for_type(cgProcedure *p, Type *type);
+gb_internal cgValue     cg_equal_proc_value_for_type(cgProcedure *p, Type *type);
+
+gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue> const &args);
+gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args);
+
+gb_internal bool    cg_emit_goto(cgProcedure *p, TB_Node *control_region);
+
+gb_internal TB_Node *cg_control_region(cgProcedure *p, char const *name);
+
+gb_internal isize cg_append_tuple_values(cgProcedure *p, Array<cgValue> *dst_values, cgValue src_value);
+
+gb_internal cgValue cg_handle_param_value(cgProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
+
+gb_internal cgValue cg_builtin_len(cgProcedure *p, cgValue value);
+gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &x);
+

+ 1101 - 0
src/tilde/tb.h

@@ -0,0 +1,1101 @@
+#ifndef TB_CORE_H
+#define TB_CORE_H
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+// https://semver.org/
+#define TB_VERSION_MAJOR 0
+#define TB_VERSION_MINOR 2
+#define TB_VERSION_PATCH 0
+
+#ifndef TB_API
+#  ifdef __cplusplus
+#    define TB_EXTERN extern "C"
+#  else
+#    define TB_EXTERN
+#  endif
+#  ifdef TB_DLL
+#    ifdef TB_IMPORT_DLL
+#      define TB_API TB_EXTERN __declspec(dllimport)
+#    else
+#      define TB_API TB_EXTERN __declspec(dllexport)
+#    endif
+#  else
+#    define TB_API TB_EXTERN
+#  endif
+#endif
+
+// These are flags
+typedef enum TB_ArithmeticBehavior {
+    TB_ARITHMATIC_NONE = 0,
+    TB_ARITHMATIC_NSW  = 1,
+    TB_ARITHMATIC_NUW  = 2,
+} TB_ArithmeticBehavior;
+
+typedef enum TB_DebugFormat {
+    TB_DEBUGFMT_NONE,
+
+    TB_DEBUGFMT_DWARF,
+    TB_DEBUGFMT_CODEVIEW,
+
+    TB_DEBUGFMT_COLINPILLED
+} TB_DebugFormat;
+
+typedef enum TB_Arch {
+    TB_ARCH_UNKNOWN,
+
+    TB_ARCH_X86_64,
+    TB_ARCH_AARCH64, // unsupported but planned
+    TB_ARCH_WASM32,
+} TB_Arch;
+
+typedef enum TB_System {
+    TB_SYSTEM_WINDOWS,
+    TB_SYSTEM_LINUX,
+    TB_SYSTEM_MACOS,
+    TB_SYSTEM_ANDROID, // Not supported yet
+    TB_SYSTEM_WEB,
+
+    TB_SYSTEM_MAX,
+} TB_System;
+
+typedef enum TB_WindowsSubsystem {
+    TB_WIN_SUBSYSTEM_UNKNOWN,
+
+    TB_WIN_SUBSYSTEM_WINDOWS,
+    TB_WIN_SUBSYSTEM_CONSOLE,
+    TB_WIN_SUBSYSTEM_EFI_APP,
+} TB_WindowsSubsystem;
+
+typedef enum TB_ABI {
+    // Used on 64bit Windows platforms
+    TB_ABI_WIN64,
+
+    // Used on Mac, BSD and Linux platforms
+    TB_ABI_SYSTEMV,
+} TB_ABI;
+
+typedef enum TB_OutputFlavor {
+    TB_FLAVOR_OBJECT,     // .o  .obj
+    TB_FLAVOR_SHARED,     // .so .dll
+    TB_FLAVOR_STATIC,     // .a  .lib
+    TB_FLAVOR_EXECUTABLE, //     .exe
+} TB_OutputFlavor;
+
+typedef enum TB_CallingConv {
+    TB_CDECL,
+    TB_STDCALL
+} TB_CallingConv;
+
+typedef enum TB_FeatureSet_X64 {
+    TB_FEATURE_X64_SSE3   = (1u << 0u),
+    TB_FEATURE_X64_SSE41  = (1u << 1u),
+    TB_FEATURE_X64_SSE42  = (1u << 2u),
+
+    TB_FEATURE_X64_POPCNT = (1u << 3u),
+    TB_FEATURE_X64_LZCNT  = (1u << 4u),
+
+    TB_FEATURE_X64_CLMUL  = (1u << 5u),
+    TB_FEATURE_X64_F16C   = (1u << 6u),
+
+    TB_FEATURE_X64_BMI1   = (1u << 7u),
+    TB_FEATURE_X64_BMI2   = (1u << 8u),
+
+    TB_FEATURE_X64_AVX    = (1u << 9u),
+    TB_FEATURE_X64_AVX2   = (1u << 10u),
+} TB_FeatureSet_X64;
+
+typedef struct TB_FeatureSet {
+    TB_FeatureSet_X64 x64;
+} TB_FeatureSet;
+
+typedef enum TB_BranchHint {
+    TB_BRANCH_HINT_NONE,
+    TB_BRANCH_HINT_LIKELY,
+    TB_BRANCH_HINT_UNLIKELY
+} TB_BranchHint;
+
+typedef enum TB_Linkage {
+    TB_LINKAGE_PUBLIC,
+    TB_LINKAGE_PRIVATE
+} TB_Linkage;
+
+typedef enum {
+    TB_COMDAT_NONE,
+
+    TB_COMDAT_MATCH_ANY,
+} TB_ComdatType;
+
+typedef enum TB_MemoryOrder {
+    TB_MEM_ORDER_RELAXED,
+    TB_MEM_ORDER_CONSUME,
+    TB_MEM_ORDER_ACQUIRE,
+    TB_MEM_ORDER_RELEASE,
+    TB_MEM_ORDER_ACQ_REL,
+    TB_MEM_ORDER_SEQ_CST,
+} TB_MemoryOrder;
+
+typedef enum TB_ISelMode {
+    // FastISel
+    TB_ISEL_FAST,
+    TB_ISEL_COMPLEX
+} TB_ISelMode;
+
+typedef enum TB_DataTypeEnum {
+    // Integers, note void is an i0 and bool is an i1
+    //   i(0-2047)
+    TB_INT,
+    // Floating point numbers
+    //   f{32,64}
+    TB_FLOAT,
+    // Pointers
+    //   ptr(0-2047)
+    TB_PTR,
+    // Tuples, these cannot be used in memory ops, just accessed via projections
+    TB_TUPLE,
+    // represents control flow as a kind of data
+    TB_CONTROL,
+} TB_DataTypeEnum;
+
+typedef enum TB_FloatFormat {
+    // IEEE 754 floats
+    TB_FLT_32, TB_FLT_64
+} TB_FloatFormat;
+
+typedef union TB_DataType {
+    struct {
+        uint8_t type;
+        // Only integers and floats can be wide.
+        uint8_t width;
+        // for integers it's the bitwidth
+        uint16_t data;
+    };
+    uint32_t raw;
+} TB_DataType;
+
+// classify data types
+#define TB_IS_VOID_TYPE(x)     ((x).type == TB_INT && (x).data == 0)
+#define TB_IS_BOOL_TYPE(x)     ((x).type == TB_INT && (x).data == 1)
+#define TB_IS_INTEGER_TYPE(x)  ((x).type == TB_INT)
+#define TB_IS_FLOAT_TYPE(x)    ((x).type == TB_FLOAT)
+#define TB_IS_POINTER_TYPE(x)  ((x).type == TB_PTR)
+
+// accessors
+#define TB_GET_INT_BITWIDTH(x) ((x).data)
+#define TB_GET_FLOAT_FORMAT(x) ((x).data)
+#define TB_GET_PTR_ADDRSPACE(x) ((x).data)
+
+typedef enum TB_NodeTypeEnum {
+    TB_NULL = 0,
+
+    // Immediates
+    TB_INTEGER_CONST,
+    TB_FLOAT32_CONST,
+    TB_FLOAT64_CONST,
+
+    // only one per function
+    TB_START, // fn()
+
+    // regions represent the begining of BBs
+    TB_REGION, // fn(preds: []region)
+
+    // projection
+    TB_PROJ,
+
+    TB_CALL,  // normal call
+    TB_SCALL, // system call
+
+    // Managed ops
+    TB_SAFEPOINT,
+
+    // Memory operations
+    TB_STORE, // fn(r: control, addr: data, src: data)
+    TB_MEMCPY,
+    TB_MEMSET,
+
+    // Atomics
+    TB_ATOMIC_TEST_AND_SET,
+    TB_ATOMIC_CLEAR,
+
+    TB_ATOMIC_LOAD,
+    TB_ATOMIC_XCHG,
+    TB_ATOMIC_ADD,
+    TB_ATOMIC_SUB,
+    TB_ATOMIC_AND,
+    TB_ATOMIC_XOR,
+    TB_ATOMIC_OR,
+
+    TB_ATOMIC_CMPXCHG,
+    TB_DEBUGBREAK,
+
+    // Terminators
+    TB_BRANCH,
+    TB_RET,
+    TB_UNREACHABLE,
+    TB_TRAP,
+
+    TB_POISON,
+
+    // Load
+    TB_LOAD,
+
+    // Pointers
+    TB_LOCAL,
+
+    TB_GET_SYMBOL_ADDRESS,
+
+    TB_MEMBER_ACCESS,
+    TB_ARRAY_ACCESS,
+
+    // Conversions
+    TB_TRUNCATE,
+    TB_FLOAT_EXT,
+    TB_SIGN_EXT,
+    TB_ZERO_EXT,
+    TB_INT2PTR,
+    TB_PTR2INT,
+    TB_UINT2FLOAT,
+    TB_FLOAT2UINT,
+    TB_INT2FLOAT,
+    TB_FLOAT2INT,
+    TB_BITCAST,
+
+    // Select
+    TB_SELECT,
+
+    // Bitmagic
+    TB_BSWAP,
+    TB_CLZ,
+    TB_CTZ,
+    TB_POPCNT,
+
+    // Unary operations
+    TB_NOT,
+    TB_NEG,
+
+    // Integer arithmatic
+    TB_AND,
+    TB_OR,
+    TB_XOR,
+    TB_ADD,
+    TB_SUB,
+    TB_MUL,
+
+    TB_SHL,
+    TB_SHR,
+    TB_SAR,
+    TB_ROL,
+    TB_ROR,
+    TB_UDIV,
+    TB_SDIV,
+    TB_UMOD,
+    TB_SMOD,
+
+    // Float arithmatic
+    TB_FADD,
+    TB_FSUB,
+    TB_FMUL,
+    TB_FDIV,
+
+    // Comparisons
+    TB_CMP_EQ,
+    TB_CMP_NE,
+    TB_CMP_ULT,
+    TB_CMP_ULE,
+    TB_CMP_SLT,
+    TB_CMP_SLE,
+    TB_CMP_FLT,
+    TB_CMP_FLE,
+
+    // Special ops
+    // does full multiplication (64x64=128 and so on) returning
+    // the low and high values in separate projections
+    TB_MULPAIR,
+
+    // PHI
+    TB_PHI, // fn(r: region, x: []data)
+
+    // variadic
+    TB_VA_START,
+
+    // x86 intrinsics
+    TB_X86INTRIN_RDTSC,
+    TB_X86INTRIN_LDMXCSR,
+    TB_X86INTRIN_STMXCSR,
+    TB_X86INTRIN_SQRT,
+    TB_X86INTRIN_RSQRT,
+} TB_NodeTypeEnum;
+typedef uint8_t TB_NodeType;
+
+typedef int TB_Label;
+
+// just represents some region of bytes, usually in file parsing crap
+typedef struct {
+    size_t length;
+    const uint8_t* data;
+} TB_Slice;
+
+// represents byte counts
+typedef uint32_t TB_CharUnits;
+
+typedef unsigned int TB_FileID;
+
+// SO refers to shared objects which mean either shared libraries (.so or .dll)
+// or executables (.exe or ELF executables)
+typedef enum {
+    // exports to the rest of the shared object
+    TB_EXTERNAL_SO_LOCAL,
+
+    // exports outside of the shared object
+    TB_EXTERNAL_SO_EXPORT,
+} TB_ExternalType;
+
+typedef struct TB_Global            TB_Global;
+typedef struct TB_External          TB_External;
+typedef struct TB_Function          TB_Function;
+
+typedef struct TB_Module            TB_Module;
+typedef struct TB_Attrib            TB_Attrib;
+typedef struct TB_DebugType         TB_DebugType;
+typedef struct TB_ModuleSection     TB_ModuleSection;
+typedef struct TB_FunctionPrototype TB_FunctionPrototype;
+
+// Refers generically to objects within a module
+//
+// TB_Function, TB_Global, and TB_External are all subtypes of TB_Symbol
+// and thus are safely allowed to cast into a symbol for operations.
+typedef struct TB_Symbol {
+    enum TB_SymbolTag {
+        TB_SYMBOL_NONE,
+
+        // symbol is dead now
+        TB_SYMBOL_TOMBSTONE,
+
+        TB_SYMBOL_EXTERNAL,
+        TB_SYMBOL_GLOBAL,
+        TB_SYMBOL_FUNCTION,
+
+        TB_SYMBOL_MAX,
+    } tag;
+
+    // refers to the prev or next symbol with the same tag
+    struct TB_Symbol* next;
+    char* name;
+
+    // It's kinda a weird circular reference but yea
+    TB_Module* module;
+
+    // helpful for sorting and getting consistent builds
+    uint64_t ordinal;
+
+    union {
+        // if we're JITing then this maps to the address of the symbol
+        void* address;
+        size_t symbol_id;
+    };
+
+    // after this point it's tag-specific storage
+} TB_Symbol;
+
+typedef int TB_Reg;
+
+typedef struct TB_Node TB_Node;
+struct TB_Node {
+    TB_NodeType type;
+    TB_DataType dt;
+    uint16_t input_count; // number of node inputs
+    uint16_t extra_count; // number of bytes for extra operand data
+
+    TB_Attrib* first_attrib;
+    TB_Node** inputs;
+
+    char extra[];
+};
+
+#define TB_KILL_NODE(n) ((n)->type = TB_NULL)
+
+// These are the extra data in specific nodes
+#define TB_NODE_GET_EXTRA(n)         ((void*) n->extra)
+#define TB_NODE_GET_EXTRA_T(n, T)    ((T*) (n)->extra)
+#define TB_NODE_SET_EXTRA(n, T, ...) (*((T*) (n)->extra) = (T){ __VA_ARGS__ })
+
+// this represents switch (many targets), if (one target) and goto (only default) logic.
+typedef struct { // TB_BRANCH
+    // avoid empty structs with flexible members
+    int64_t _;
+    int64_t keys[];
+} TB_NodeBranch;
+
+typedef struct { // TB_PROJ
+    int index;
+} TB_NodeProj;
+
+typedef struct { // TB_INT
+    uint64_t num_words;
+    uint64_t words[];
+} TB_NodeInt;
+
+typedef struct { // any compare operator
+    TB_DataType cmp_dt;
+} TB_NodeCompare;
+
+typedef struct { // any integer binary operator
+    TB_ArithmeticBehavior ab;
+} TB_NodeBinopInt;
+
+typedef struct { // TB_MULPAIR
+    TB_Node *lo, *hi;
+} TB_NodeMulPair;
+
+typedef struct {
+    TB_CharUnits align;
+    bool is_volatile;
+} TB_NodeMemAccess;
+
+typedef struct {
+    TB_CharUnits size, align;
+} TB_NodeLocal;
+
+typedef struct {
+    TB_FileID file;
+    int line;
+} TB_NodeLine;
+
+typedef struct {
+    float value;
+} TB_NodeFloat32;
+
+typedef struct {
+    double value;
+} TB_NodeFloat64;
+
+typedef struct {
+    int64_t stride;
+} TB_NodeArray;
+
+typedef struct {
+    int64_t offset;
+} TB_NodeMember;
+
+typedef struct {
+    TB_Symbol* sym;
+} TB_NodeSymbol;
+
+typedef struct {
+    TB_MemoryOrder order;
+    TB_MemoryOrder order2;
+} TB_NodeAtomic;
+
+typedef struct {
+    TB_FunctionPrototype* proto;
+    TB_Node* projs[];
+} TB_NodeCall;
+
+typedef struct {
+    uint32_t id;
+} TB_NodeSafepoint;
+
+typedef struct {
+    TB_Node* end;
+    const char* tag;
+
+    size_t succ_count;
+    TB_Node** succ;
+
+    size_t proj_count;
+    TB_Node** projs;
+} TB_NodeRegion;
+
+typedef struct TB_MultiOutput {
+    size_t count;
+    union {
+        // count = 1
+        TB_Node* single;
+        // count > 1
+        TB_Node** multiple;
+    };
+} TB_MultiOutput;
+#define TB_MULTI_OUTPUT(o) ((o).count > 1 ? (o).multiple : &(o).single)
+
+typedef struct {
+    int64_t key;
+    TB_Node* value;
+} TB_SwitchEntry;
+
+typedef enum {
+    TB_EXECUTABLE_UNKNOWN,
+    TB_EXECUTABLE_PE,
+    TB_EXECUTABLE_ELF,
+} TB_ExecutableType;
+
+typedef struct {
+    TB_Node* node; // type == TB_SAFEPOINT
+    void* userdata;
+
+    uint32_t ip;    // relative to the function body.
+    uint32_t count; // same as node->input_count
+    int32_t values[];
+} TB_Safepoint;
+
+// *******************************
+// Public macros
+// *******************************
+#ifdef __cplusplus
+
+#define TB_TYPE_TUPLE   TB_DataType{ { TB_TUPLE } }
+#define TB_TYPE_CONTROL TB_DataType{ { TB_CONTROL } }
+#define TB_TYPE_VOID    TB_DataType{ { TB_INT,   0, 0 } }
+#define TB_TYPE_I8      TB_DataType{ { TB_INT,   0, 8 } }
+#define TB_TYPE_I16     TB_DataType{ { TB_INT,   0, 16 } }
+#define TB_TYPE_I32     TB_DataType{ { TB_INT,   0, 32 } }
+#define TB_TYPE_I64     TB_DataType{ { TB_INT,   0, 64 } }
+#define TB_TYPE_F32     TB_DataType{ { TB_FLOAT, 0, TB_FLT_32 } }
+#define TB_TYPE_F64     TB_DataType{ { TB_FLOAT, 0, TB_FLT_64 } }
+#define TB_TYPE_BOOL    TB_DataType{ { TB_INT,   0, 1 } }
+#define TB_TYPE_PTR     TB_DataType{ { TB_PTR,   0, 0 } }
+
+#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT,   0, (N) } }
+#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR,   0, (N) } }
+
+#else
+
+#define TB_TYPE_TUPLE   (TB_DataType){ { TB_TUPLE } }
+#define TB_TYPE_CONTROL (TB_DataType){ { TB_CONTROL } }
+#define TB_TYPE_VOID    (TB_DataType){ { TB_INT,   0, 0 } }
+#define TB_TYPE_I8      (TB_DataType){ { TB_INT,   0, 8 } }
+#define TB_TYPE_I16     (TB_DataType){ { TB_INT,   0, 16 } }
+#define TB_TYPE_I32     (TB_DataType){ { TB_INT,   0, 32 } }
+#define TB_TYPE_I64     (TB_DataType){ { TB_INT,   0, 64 } }
+#define TB_TYPE_F32     (TB_DataType){ { TB_FLOAT, 0, TB_FLT_32 } }
+#define TB_TYPE_F64     (TB_DataType){ { TB_FLOAT, 0, TB_FLT_64 } }
+#define TB_TYPE_BOOL    (TB_DataType){ { TB_INT,   0, 1 } }
+#define TB_TYPE_PTR     (TB_DataType){ { TB_PTR,   0, 0 } }
+#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT,  0, (N) } }
+#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR,  0, (N) } }
+
+#endif
+
+typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...);
+
+// defined in common/arena.h
+typedef struct TB_Arena TB_Arena;
+
+// 0 for default
+TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
+TB_API void tb_arena_destroy(TB_Arena* restrict arena);
+TB_API bool tb_arena_is_empty(TB_Arena* arena);
+
+////////////////////////////////
+// Module management
+////////////////////////////////
+// Creates a module with the correct target and settings
+TB_API TB_Module* tb_module_create(TB_Arch arch, TB_System sys, const TB_FeatureSet* features, bool is_jit);
+
+// Creates a module but defaults on the architecture and system based on the host machine
+TB_API TB_Module* tb_module_create_for_host(const TB_FeatureSet* features, bool is_jit);
+
+TB_API size_t tb_module_get_function_count(TB_Module* m);
+
+// Frees all resources for the TB_Module and it's functions, globals and
+// compiled code.
+TB_API void tb_module_destroy(TB_Module* m);
+
+// When targetting windows & thread local storage, you'll need to bind a tls index
+// which is usually just a global that the runtime support has initialized, if you
+// dont and the tls_index is used, it'll crash
+TB_API void tb_module_set_tls_index(TB_Module* m, ptrdiff_t len, const char* name);
+
+// You don't need to manually call this unless you want to resolve locations before
+// exporting.
+TB_API void tb_module_layout_sections(TB_Module* m);
+
+////////////////////////////////
+// Compiled code introspection
+////////////////////////////////
+enum { TB_ASSEMBLY_CHUNK_CAP = 4*1024 - sizeof(size_t[2]) };
+
+typedef struct TB_Assembly TB_Assembly;
+struct TB_Assembly {
+    TB_Assembly* next;
+
+    // nice chunk of text here
+    size_t length;
+    char data[];
+};
+
+// this is where the machine code and other relevant pieces go.
+typedef struct TB_FunctionOutput TB_FunctionOutput;
+
+TB_API void tb_output_print_asm(TB_FunctionOutput* out, FILE* fp);
+
+TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length);
+
+// returns NULL if no assembly was generated
+TB_API TB_Assembly* tb_output_get_asm(TB_FunctionOutput* out);
+
+// this is relative to the start of the function (the start of the prologue)
+TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip);
+
+////////////////////////////////
+// Exporter
+////////////////////////////////
+// Export buffers are generated in chunks because it's easier, usually the
+// chunks are "massive" (representing some connected piece of the buffer)
+// but they don't have to be.
+typedef struct TB_ExportChunk TB_ExportChunk;
+struct TB_ExportChunk {
+    TB_ExportChunk* next;
+    size_t pos, size;
+    uint8_t data[];
+};
+
+typedef struct {
+    size_t total;
+    TB_ExportChunk *head, *tail;
+} TB_ExportBuffer;
+
+TB_API TB_ExportBuffer tb_module_object_export(TB_Module* m, TB_DebugFormat debug_fmt);
+TB_API bool tb_export_buffer_to_file(TB_ExportBuffer buffer, const char* path);
+TB_API void tb_export_buffer_free(TB_ExportBuffer buffer);
+
+////////////////////////////////
+// Linker exporter
+////////////////////////////////
+// This is used to export shared objects or executables
+typedef struct TB_Linker TB_Linker;
+typedef struct TB_LinkerSection TB_LinkerSection;
+typedef struct TB_LinkerSectionPiece TB_LinkerSectionPiece;
+
+typedef struct {
+    enum {
+        TB_LINKER_MSG_NULL,
+
+        // pragma comment(lib, "blah")
+        TB_LINKER_MSG_IMPORT,
+    } tag;
+    union {
+        // pragma lib request
+        TB_Slice import_path;
+    };
+} TB_LinkerMsg;
+
+TB_API TB_ExecutableType tb_system_executable_format(TB_System s);
+
+TB_API TB_Linker* tb_linker_create(TB_ExecutableType type, TB_Arch arch);
+TB_API TB_ExportBuffer tb_linker_export(TB_Linker* l);
+TB_API void tb_linker_destroy(TB_Linker* l);
+
+TB_API bool tb_linker_get_msg(TB_Linker* l, TB_LinkerMsg* msg);
+
+// windows only
+TB_API void tb_linker_set_subsystem(TB_Linker* l, TB_WindowsSubsystem subsystem);
+
+TB_API void tb_linker_set_entrypoint(TB_Linker* l, const char* name);
+
+// Links compiled module into output
+TB_API void tb_linker_append_module(TB_Linker* l, TB_Module* m);
+
+// Adds object file to output
+TB_API void tb_linker_append_object(TB_Linker* l, TB_Slice obj_name, TB_Slice content);
+
+// Adds static library to output
+//   this can include imports (wrappers for DLL symbols) along with
+//   normal sections.
+TB_API void tb_linker_append_library(TB_Linker* l, TB_Slice ar_name, TB_Slice content);
+
+////////////////////////////////
+// JIT compilation
+////////////////////////////////
+typedef struct TB_JITContext TB_JITContext;
+
+// passing 0 to jit_heap_capacity will default to 4MiB
+TB_API TB_JITContext* tb_module_begin_jit(TB_Module* m, size_t jit_heap_capacity);
+TB_API void* tb_module_apply_function(TB_JITContext* jit, TB_Function* f);
+TB_API void* tb_module_apply_global(TB_JITContext* jit, TB_Global* g);
+// fixes page permissions, applies missing relocations
+TB_API void tb_module_ready_jit(TB_JITContext* jit);
+TB_API void tb_module_end_jit(TB_JITContext* jit);
+
+#define TB_FOR_FUNCTIONS(it, module) for (TB_Function* it = tb_first_function(module); it != NULL; it = tb_next_function(it))
+TB_API TB_Function* tb_first_function(TB_Module* m);
+TB_API TB_Function* tb_next_function(TB_Function* f);
+
+#define TB_FOR_EXTERNALS(it, module) for (TB_External* it = tb_first_external(module); it != NULL; it = tb_next_external(it))
+TB_API TB_External* tb_first_external(TB_Module* m);
+TB_API TB_External* tb_next_external(TB_External* e);
+
+// this is used JIT scenarios to tell the compiler what externals map to
+TB_API TB_ExternalType tb_extern_get_type(TB_External* e);
+TB_Global* tb_extern_transmute(TB_External* e, TB_DebugType* dbg_type, TB_Linkage linkage);
+
+TB_API TB_External* tb_extern_create(TB_Module* m, ptrdiff_t len, const char* name, TB_ExternalType type);
+TB_API TB_FileID tb_file_create(TB_Module* m, const char* path);
+
+// Called once you're done with TB operations on a thread (or i guess when it's
+// about to be killed :p), not calling it can only result in leaks on that thread
+// and calling it too early will result in TB potentially reallocating it but there's
+// should be no crashes from this, just potential slowdown or higher than expected memory
+// usage.
+TB_API void tb_free_thread_resources(void);
+
+////////////////////////////////
+// Function Prototypes
+////////////////////////////////
+typedef struct TB_PrototypeParam {
+    TB_DataType dt;
+    TB_DebugType* debug_type;
+
+    // does not apply for returns
+    const char* name;
+} TB_PrototypeParam;
+
+struct TB_FunctionPrototype {
+    // header
+    TB_CallingConv call_conv;
+    uint16_t return_count, param_count;
+    bool has_varargs;
+
+    // params are directly followed by returns
+    TB_PrototypeParam params[];
+};
+#define TB_PROTOTYPE_RETURNS(p) ((p)->params + (p)->param_count)
+
+// creates a function prototype used to define a function's parameters and returns.
+//
+// function prototypes do not get freed individually and last for the entire run
+// of the backend, they can also be reused for multiple functions which have
+// matching signatures.
+TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc, size_t param_count, const TB_PrototypeParam* params, size_t return_count, const TB_PrototypeParam* returns, bool has_varargs);
+
+// same as tb_function_set_prototype except it will handle lowering from types like the TB_DebugType
+// into the correct ABI and exposing sane looking nodes to the parameters.
+//
+// returns the parameters
+TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count);
+TB_API TB_FunctionPrototype* tb_prototype_from_dbg(TB_Module* m, TB_DebugType* dbg);
+
+// used for ABI parameter passing
+typedef enum {
+    // needs a direct value
+    TB_PASSING_DIRECT,
+
+    // needs an address to the value
+    TB_PASSING_INDIRECT,
+
+    // doesn't use this parameter
+    TB_PASSING_IGNORE,
+} TB_PassingRule;
+
+TB_API TB_PassingRule tb_get_passing_rule_from_dbg(TB_Module* mod, TB_DebugType* param_type, bool is_return);
+
+////////////////////////////////
+// Globals
+////////////////////////////////
+TB_API TB_Global* tb_global_create(TB_Module* m, ptrdiff_t len, const char* name, TB_DebugType* dbg_type, TB_Linkage linkage);
+
+// allocate space for the global
+TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSection* section, TB_Global* global, size_t size, size_t align, size_t max_objects);
+
+// returns a buffer which the user can fill to then have represented in the initializer
+TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset, size_t size);
+
+// places a relocation for a global at offset, the size of the relocation
+// depends on the pointer size
+TB_API void tb_global_add_symbol_reloc(TB_Module* m, TB_Global* global, size_t offset, const TB_Symbol* symbol);
+
+TB_API TB_ModuleSection* tb_module_get_text(TB_Module* m);
+TB_API TB_ModuleSection* tb_module_get_rdata(TB_Module* m);
+TB_API TB_ModuleSection* tb_module_get_data(TB_Module* m);
+TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m);
+
+////////////////////////////////
+// Function Attributes
+////////////////////////////////
+TB_API void tb_node_append_attrib(TB_Node* n, TB_Attrib* a);
+
+// These are parts of a function that describe metadata for instructions
+TB_API TB_Attrib* tb_function_attrib_variable(TB_Function* f, ptrdiff_t len, const char* name, TB_DebugType* type);
+TB_API TB_Attrib* tb_function_attrib_scope(TB_Function* f, TB_Attrib* parent_scope);
+
+////////////////////////////////
+// Debug info Generation
+////////////////////////////////
+TB_API TB_DebugType* tb_debug_get_void(TB_Module* m);
+TB_API TB_DebugType* tb_debug_get_bool(TB_Module* m);
+TB_API TB_DebugType* tb_debug_get_integer(TB_Module* m, bool is_signed, int bits);
+TB_API TB_DebugType* tb_debug_get_float(TB_Module* m, TB_FloatFormat fmt);
+TB_API TB_DebugType* tb_debug_create_ptr(TB_Module* m, TB_DebugType* base);
+TB_API TB_DebugType* tb_debug_create_array(TB_Module* m, TB_DebugType* base, size_t count);
+TB_API TB_DebugType* tb_debug_create_alias(TB_Module* m, TB_DebugType* base, ptrdiff_t len, const char* tag);
+TB_API TB_DebugType* tb_debug_create_struct(TB_Module* m, ptrdiff_t len, const char* tag);
+TB_API TB_DebugType* tb_debug_create_union(TB_Module* m, ptrdiff_t len, const char* tag);
+TB_API TB_DebugType* tb_debug_create_field(TB_Module* m, TB_DebugType* type, ptrdiff_t len, const char* name, TB_CharUnits offset);
+
+// returns the array you need to fill with fields
+TB_API TB_DebugType** tb_debug_record_begin(TB_DebugType* type, size_t count);
+TB_API void tb_debug_record_end(TB_DebugType* type, TB_CharUnits size, TB_CharUnits align);
+
+TB_API TB_DebugType* tb_debug_create_func(TB_Module* m, TB_CallingConv cc, size_t param_count, size_t return_count, bool has_varargs);
+
+TB_API TB_DebugType* tb_debug_field_type(TB_DebugType* type);
+
+TB_API size_t tb_debug_func_return_count(TB_DebugType* type);
+TB_API size_t tb_debug_func_param_count(TB_DebugType* type);
+
+// you'll need to fill these if you make a function
+TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
+TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
+
+////////////////////////////////
+// IR access
+////////////////////////////////
+// it is an index to the input
+#define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++)
+
+////////////////////////////////
+// Symbols
+////////////////////////////////
+TB_API bool tb_symbol_is_comdat(const TB_Symbol* s);
+
+// returns NULL if the tag doesn't match
+TB_API TB_Function* tb_symbol_as_function(TB_Symbol* s);
+TB_API TB_External* tb_symbol_as_external(TB_Symbol* s);
+TB_API TB_Global* tb_symbol_as_global(TB_Symbol* s);
+
+////////////////////////////////
+// Function IR Generation
+////////////////////////////////
+TB_API void tb_get_data_type_size(TB_Module* mod, TB_DataType dt, size_t* size, size_t* align);
+
+// the user_data is expected to be a valid FILE*
+TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...);
+
+TB_API void tb_inst_set_location(TB_Function* f, TB_FileID file, int line);
+
+// if section is NULL, default to .text
+TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat);
+
+TB_API void* tb_function_get_jit_pos(TB_Function* f);
+
+// if len is -1, it's null terminated
+TB_API void tb_symbol_set_name(TB_Symbol* s, ptrdiff_t len, const char* name);
+
+TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr);
+TB_API const char* tb_symbol_get_name(TB_Symbol* s);
+
+// if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources
+TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena);
+TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f);
+
+TB_API void tb_function_print(TB_Function* f, TB_PrintCallback callback, void* user_data);
+
+TB_API void tb_inst_set_control(TB_Function* f, TB_Node* control);
+TB_API TB_Node* tb_inst_get_control(TB_Function* f);
+
+TB_API TB_Node* tb_inst_region(TB_Function* f);
+
+// if len is -1, it's null terminated
+TB_API void tb_inst_set_region_name(TB_Node* n, ptrdiff_t len, const char* name);
+
+TB_API void tb_inst_unreachable(TB_Function* f);
+TB_API void tb_inst_debugbreak(TB_Function* f);
+TB_API void tb_inst_trap(TB_Function* f);
+TB_API TB_Node* tb_inst_poison(TB_Function* f);
+
+TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id);
+
+TB_API TB_Node* tb_inst_fpxt(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_sxt(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_zxt(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_trunc(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_int2ptr(TB_Function* f, TB_Node* src);
+TB_API TB_Node* tb_inst_ptr2int(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_int2float(TB_Function* f, TB_Node* src, TB_DataType dt, bool is_signed);
+TB_API TB_Node* tb_inst_float2int(TB_Function* f, TB_Node* src, TB_DataType dt, bool is_signed);
+TB_API TB_Node* tb_inst_bitcast(TB_Function* f, TB_Node* src, TB_DataType dt);
+
+TB_API TB_Node* tb_inst_local(TB_Function* f, TB_CharUnits size, TB_CharUnits align);
+TB_API TB_Node* tb_inst_load(TB_Function* f, TB_DataType dt, TB_Node* addr, TB_CharUnits align, bool is_volatile);
+TB_API void tb_inst_store(TB_Function* f, TB_DataType dt, TB_Node* addr, TB_Node* val, TB_CharUnits align, bool is_volatile);
+
+TB_API TB_Node* tb_inst_bool(TB_Function* f, bool imm);
+TB_API TB_Node* tb_inst_sint(TB_Function* f, TB_DataType dt, int64_t imm);
+TB_API TB_Node* tb_inst_uint(TB_Function* f, TB_DataType dt, uint64_t imm);
+TB_API TB_Node* tb_inst_float32(TB_Function* f, float imm);
+TB_API TB_Node* tb_inst_float64(TB_Function* f, double imm);
+TB_API TB_Node* tb_inst_cstring(TB_Function* f, const char* str);
+TB_API TB_Node* tb_inst_string(TB_Function* f, size_t len, const char* str);
+
+// write 'val' over 'count' bytes on 'dst'
+TB_API void tb_inst_memset(TB_Function* f, TB_Node* dst, TB_Node* val, TB_Node* count, TB_CharUnits align, bool is_volatile);
+
+// zero 'count' bytes on 'dst'
+TB_API void tb_inst_memzero(TB_Function* f, TB_Node* dst, TB_Node* count, TB_CharUnits align, bool is_volatile);
+
+// performs a copy of 'count' elements from one memory location to another
+// both locations cannot overlap.
+TB_API void tb_inst_memcpy(TB_Function* f, TB_Node* dst, TB_Node* src, TB_Node* count, TB_CharUnits align, bool is_volatile);
+
+// result = base + (index * stride)
+TB_API TB_Node* tb_inst_array_access(TB_Function* f, TB_Node* base, TB_Node* index, int64_t stride);
+
+// result = base + offset
+// where base is a pointer
+TB_API TB_Node* tb_inst_member_access(TB_Function* f, TB_Node* base, int64_t offset);
+
+TB_API TB_Node* tb_inst_get_symbol_address(TB_Function* f, TB_Symbol* target);
+
+// Performs a conditional select between two values, if the operation is
+// performed wide then the cond is expected to be the same type as a and b where
+// the condition is resolved as true if the MSB (per component) is 1.
+//
+// result = cond ? a : b
+// a, b must match in type
+TB_API TB_Node* tb_inst_select(TB_Function* f, TB_Node* cond, TB_Node* a, TB_Node* b);
+
+// Integer arithmatic
+TB_API TB_Node* tb_inst_add(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_sub(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_mul(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_div(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_mod(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+
+// Bitmagic operations
+TB_API TB_Node* tb_inst_bswap(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_clz(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_ctz(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_popcount(TB_Function* f, TB_Node* n);
+
+// Bitwise operations
+TB_API TB_Node* tb_inst_not(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_neg(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_and(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_or(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_xor(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_sar(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_shl(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_shr(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_rol(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_ror(TB_Function* f, TB_Node* a, TB_Node* b);
+
+// Atomics
+// By default you can use TB_MEM_ORDER_SEQ_CST for the memory order to get
+// correct but possibly slower results on certain platforms (those with relaxed
+// memory models).
+
+// Must be aligned to the natural alignment of dt
+TB_API TB_Node* tb_inst_atomic_load(TB_Function* f, TB_Node* addr, TB_DataType dt, TB_MemoryOrder order);
+
+// All atomic operations here return the old value and the operations are
+// performed in the same data type as 'src' with alignment of 'addr' being
+// the natural alignment of 'src'
+TB_API TB_Node* tb_inst_atomic_xchg(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_add(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_sub(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_and(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_xor(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_or(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+
+// returns old_value from *addr
+TB_API TB_Node* tb_inst_atomic_cmpxchg(TB_Function* f, TB_Node* addr, TB_Node* expected, TB_Node* desired, TB_MemoryOrder succ, TB_MemoryOrder fail);
+
+// Float math
+TB_API TB_Node* tb_inst_fadd(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_fsub(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_fmul(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_fdiv(TB_Function* f, TB_Node* a, TB_Node* b);
+
+// Comparisons
+TB_API TB_Node* tb_inst_cmp_eq(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_ne(TB_Function* f, TB_Node* a, TB_Node* b);
+
+TB_API TB_Node* tb_inst_cmp_ilt(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_cmp_ile(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_cmp_igt(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_cmp_ige(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+
+TB_API TB_Node* tb_inst_cmp_flt(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_fle(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_fgt(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_fge(TB_Function* f, TB_Node* a, TB_Node* b);
+
+// General intrinsics
+TB_API TB_Node* tb_inst_va_start(TB_Function* f, TB_Node* a);
+
+// x86 Intrinsics
+TB_API TB_Node* tb_inst_x86_rdtsc(TB_Function* f);
+TB_API TB_Node* tb_inst_x86_ldmxcsr(TB_Function* f, TB_Node* a);
+TB_API TB_Node* tb_inst_x86_stmxcsr(TB_Function* f);
+TB_API TB_Node* tb_inst_x86_sqrt(TB_Function* f, TB_Node* a);
+TB_API TB_Node* tb_inst_x86_rsqrt(TB_Function* f, TB_Node* a);
+
+// Control flow
+TB_API TB_Node* tb_inst_syscall(TB_Function* f, TB_DataType dt, TB_Node* syscall_num, size_t param_count, TB_Node** params);
+TB_API TB_MultiOutput tb_inst_call(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params);
+
+// Managed
+TB_API TB_Node* tb_inst_safepoint(TB_Function* f, size_t param_count, TB_Node** params);
+
+TB_API TB_Node* tb_inst_incomplete_phi(TB_Function* f, TB_DataType dt, TB_Node* region, size_t preds);
+TB_API bool tb_inst_add_phi_operand(TB_Function* f, TB_Node* phi, TB_Node* region, TB_Node* val);
+
+TB_API TB_Node* tb_inst_phi2(TB_Function* f, TB_Node* region, TB_Node* a, TB_Node* b);
+TB_API void tb_inst_goto(TB_Function* f, TB_Node* target);
+TB_API void tb_inst_if(TB_Function* f, TB_Node* cond, TB_Node* true_case, TB_Node* false_case);
+TB_API void tb_inst_branch(TB_Function* f, TB_DataType dt, TB_Node* key, TB_Node* default_case, size_t entry_count, const TB_SwitchEntry* keys);
+
+TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values);
+
+////////////////////////////////
+// Passes
+////////////////////////////////
+// Function analysis, optimizations, and codegen are all part of this
+typedef struct TB_Passes TB_Passes;
+
+// the arena is used to allocate the nodes while passes are being done.
+TB_API TB_Passes* tb_pass_enter(TB_Function* f, TB_Arena* arena);
+TB_API void tb_pass_exit(TB_Passes* opt);
+
+// transformation passes:
+//   peephole: runs most simple reductions on the code,
+//     should be run after any bigger passes (it's incremental
+//     so it's not that bad)
+//
+//   mem2reg: lowers TB_LOCALs into SSA values, this makes more
+//     data flow analysis possible on the code and allows to codegen
+//     to place variables into registers.
+//
+//   loop: NOT READY
+//
+TB_API bool tb_pass_peephole(TB_Passes* opt);
+TB_API bool tb_pass_mem2reg(TB_Passes* opt);
+TB_API bool tb_pass_loop(TB_Passes* opt);
+
+// analysis
+//   print: prints IR in a flattened text form.
+TB_API bool tb_pass_print(TB_Passes* opt);
+
+// codegen
+TB_API TB_FunctionOutput* tb_pass_codegen(TB_Passes* opt, bool emit_asm);
+
+TB_API void tb_pass_kill_node(TB_Passes* opt, TB_Node* n);
+TB_API bool tb_pass_mark(TB_Passes* opt, TB_Node* n);
+TB_API void tb_pass_mark_users(TB_Passes* opt, TB_Node* n);
+
+////////////////////////////////
+// IR access
+////////////////////////////////
+TB_API const char* tb_node_get_name(TB_Node* n);
+
+TB_API TB_Node* tb_get_parent_region(TB_Node* n);
+TB_API bool tb_node_is_constant_non_zero(TB_Node* n);
+TB_API bool tb_node_is_constant_zero(TB_Node* n);
+
+#endif /* TB_CORE_H */

BIN
src/tilde/tb.lib


+ 76 - 0
src/tilde/tb_arena.h

@@ -0,0 +1,76 @@
+#pragma once
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifndef TB_API
+#  ifdef __cplusplus
+#    define TB_EXTERN extern "C"
+#  else
+#    define TB_EXTERN
+#  endif
+#  ifdef TB_DLL
+#    ifdef TB_IMPORT_DLL
+#      define TB_API TB_EXTERN __declspec(dllimport)
+#    else
+#      define TB_API TB_EXTERN __declspec(dllexport)
+#    endif
+#  else
+#    define TB_API TB_EXTERN
+#  endif
+#endif
+
+enum {
+    TB_ARENA_SMALL_CHUNK_SIZE  =        4 * 1024,
+    TB_ARENA_MEDIUM_CHUNK_SIZE =      512 * 1024,
+    TB_ARENA_LARGE_CHUNK_SIZE  = 2 * 1024 * 1024,
+
+    TB_ARENA_ALIGNMENT = 16,
+};
+
+typedef struct TB_ArenaChunk TB_ArenaChunk;
+struct TB_ArenaChunk {
+    TB_ArenaChunk* next;
+    size_t pad;
+    char data[];
+};
+
+typedef struct TB_Arena {
+    size_t chunk_size;
+    TB_ArenaChunk* base;
+    TB_ArenaChunk* top;
+
+    // top of the allocation space
+    char* watermark;
+    char* high_point; // &top->data[chunk_size]
+} TB_Arena;
+
+typedef struct TB_ArenaSavepoint {
+    TB_ArenaChunk* top;
+    char* watermark;
+} TB_ArenaSavepoint;
+
+#define TB_ARENA_FOR(it, arena) for (TB_ArenaChunk* it = (arena)->base; it != NULL; it = it->next)
+
+#define TB_ARENA_ALLOC(arena, T) tb_arena_alloc(arena, sizeof(T))
+#define TB_ARENA_ARR_ALLOC(arena, count, T) tb_arena_alloc(arena, (count) * sizeof(T))
+
+TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
+TB_API void tb_arena_destroy(TB_Arena* restrict arena);
+
+TB_API void* tb_arena_unaligned_alloc(TB_Arena* restrict arena, size_t size);
+TB_API void* tb_arena_alloc(TB_Arena* restrict arena, size_t size);
+
+// asserts if ptr+size != watermark
+TB_API void tb_arena_pop(TB_Arena* restrict arena, void* ptr, size_t size);
+
+// in case you wanna mix unaligned and aligned arenas
+TB_API void tb_arena_realign(TB_Arena* restrict arena);
+
+TB_API bool tb_arena_is_empty(TB_Arena* arena);
+
+// savepoints
+TB_API TB_ArenaSavepoint tb_arena_save(TB_Arena* arena);
+TB_API void tb_arena_restore(TB_Arena* arena, TB_ArenaSavepoint sp);
+
+// resets to only having one chunk
+TB_API void tb_arena_clear(TB_Arena* arena);

+ 330 - 0
src/tilde/tb_coff.h

@@ -0,0 +1,330 @@
+// PE/COFF is the executable/object format used by Microsoft.
+#ifndef TB_COFF_H
+#define TB_COFF_H
+
+#include "tb_formats.h"
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000
+
+#define IMAGE_SYM_CLASS_EXTERNAL      0x0002
+#define IMAGE_SYM_CLASS_STATIC        0x0003
+#define IMAGE_SYM_CLASS_LABEL         0x0006
+#define IMAGE_SYM_CLASS_FILE          0x0067
+#define IMAGE_SYM_CLASS_SECTION       0x0068
+#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069
+
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
+
+#define IMAGE_REL_AMD64_ADDR64   0x0001
+#define IMAGE_REL_AMD64_ADDR32   0x0002
+#define IMAGE_REL_AMD64_ADDR32NB 0x0003
+#define IMAGE_REL_AMD64_REL32    0x0004
+#define IMAGE_REL_AMD64_REL32_1  0x0005
+#define IMAGE_REL_AMD64_REL32_2  0x0006
+#define IMAGE_REL_AMD64_REL32_3  0x0007
+#define IMAGE_REL_AMD64_REL32_4  0x0008
+#define IMAGE_REL_AMD64_REL32_5  0x0009
+#define IMAGE_REL_AMD64_SECTION  0x000A
+#define IMAGE_REL_AMD64_SECREL   0x000B
+
+#define IMAGE_SCN_LNK_REMOVE      0x00000800
+#define IMAGE_SCN_LNK_COMDAT      0x00001000
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE     0x20000000
+#define IMAGE_SCN_MEM_READ        0x40000000
+#define IMAGE_SCN_MEM_WRITE       0x80000000
+
+#define IMAGE_SCN_CNT_CODE                   0x00000020  /* Section contains code. */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  /* Section contains initialized data. */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  /* Section contains uninitialized data. */
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
+#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
+#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
+#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
+#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
+#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
+#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor
+
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
+
+typedef enum {
+    TB_COFF_SECTION_NO_PAD      = 0x00000008,
+    TB_COFF_SECTION_CODE        = 0x00000020,
+    TB_COFF_SECTION_INIT        = 0x00000040,
+    TB_COFF_SECTION_UNINIT      = 0x00000080,
+    TB_COFF_SECTION_OTHER       = 0x00000100,
+    TB_COFF_SECTION_INFO        = 0x00000200,
+    TB_COFF_SECTION_REMOVE      = 0x00000800,
+    TB_COFF_SECTION_COMDAT      = 0x00001000,
+
+    // this is actually a 4bit field
+    TB_COFF_SECTION_ALIGN       = 0x00F00000,
+
+    // if we have more than 65535 relocations we do this
+    TB_COFF_SECTION_RELOC_OVR   = 0x00F00000,
+
+    // memory flags
+    TB_COFF_SECTION_DISCARDABLE = 0x02000000,
+    TB_COFF_SECTION_NOT_CACHED  = 0x04000000,
+    TB_COFF_SECTION_NOT_PAGED   = 0x08000000,
+    TB_COFF_SECTION_SHARED      = 0x10000000,
+    TB_COFF_SECTION_EXECUTE     = 0x20000000,
+    TB_COFF_SECTION_READ        = 0x40000000,
+    TB_COFF_SECTION_WRITE       = 0x80000000,
+} TB_COFF_SectionFlags;
+
+typedef struct TB_COFF_Parser {
+    // inputs
+    TB_Slice name, file;
+
+    // results
+    size_t section_count;
+    size_t symbol_table, symbol_count;
+
+    // private
+    TB_Slice string_table;
+} TB_COFF_Parser;
+
+// fills the parser with results from the COFF header
+bool tb_coff_parse_init(TB_COFF_Parser* restrict parser);
+bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* out_sec);
+
+// how many symbols does this one symbol take up (basically 1 + aux symbols).
+// returns 0 if error.
+size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym);
+
+#endif // TB_COFF_H
+
+#ifdef TB_COFF_IMPL
+#include <common.h>
+
+#pragma pack(push, 2)
+typedef struct COFF_SectionHeader {
+    char name[8];
+    union {
+        uint32_t physical_address;
+        uint32_t virtual_size;
+    } misc;
+    uint32_t virtual_address;
+    uint32_t raw_data_size;
+    uint32_t raw_data_pos;
+    uint32_t pointer_to_reloc;
+    uint32_t pointer_to_lineno;
+    uint16_t num_reloc;
+    uint16_t num_lineno;
+    uint32_t characteristics;
+} COFF_SectionHeader;
+
+typedef struct COFF_FileHeader {
+    uint16_t machine;
+    uint16_t section_count;
+    uint32_t timestamp;
+    uint32_t symbol_table;
+    uint32_t symbol_count;
+    uint16_t optional_header_size;
+    uint16_t flags;
+} COFF_FileHeader;
+
+typedef struct COFF_Symbol {
+    union {
+        uint8_t  short_name[8];
+        uint32_t long_name[2];
+    };
+    uint32_t value;
+    int16_t  section_number;
+    uint16_t type;
+    uint8_t  storage_class;
+    uint8_t  aux_symbols_count;
+} COFF_Symbol;
+
+typedef struct COFF_ImageReloc {
+    union {
+        uint32_t VirtualAddress;
+        uint32_t RelocCount;
+    };
+    uint32_t SymbolTableIndex;
+    uint16_t Type;
+} COFF_ImageReloc;
+#pragma pack(pop)
+
+// sanity checks
+static_assert(sizeof(COFF_SectionHeader) == 40, "COFF Section header size != 40 bytes");
+static_assert(sizeof(COFF_ImageReloc) == 10,    "COFF Image Relocation size != 10 bytes");
+static_assert(sizeof(COFF_FileHeader) == 20,    "COFF File header size != 20 bytes");
+static_assert(sizeof(COFF_Symbol) == 18,        "COFF Symbol size != 18 bytes");
+
+bool tb_coff_parse_init(TB_COFF_Parser* restrict parser) {
+    TB_Slice file = parser->file;
+
+    if (file.length < sizeof(COFF_FileHeader)) return false;
+    COFF_FileHeader* header = (COFF_FileHeader*) &parser->file.data[0];
+
+    // locate string table (it spans until the end of the file)
+    size_t string_table_pos = header->symbol_table + (header->symbol_count * sizeof(COFF_Symbol));
+    if (file.length < string_table_pos) return false;
+
+    parser->symbol_count = header->symbol_count;
+    parser->symbol_table = header->symbol_table;
+    parser->section_count = header->section_count;
+    parser->string_table = (TB_Slice){
+        .length = file.length - string_table_pos,
+        .data   = &file.data[string_table_pos]
+    };
+
+    return true;
+}
+
+static long long tb__parse_decimal_int(size_t n, const char* str) {
+    const char* end = &str[n];
+
+    int result = 0;
+    while (str != end) {
+        if (*str < '0' || *str > '9') break;
+
+        result *= 10;
+        result += *str - '0';
+        str++;
+    }
+
+    return result;
+}
+
+bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* restrict out_sec) {
+    TB_Slice file = parser->file;
+    size_t section_offset = sizeof(COFF_FileHeader) + (i * sizeof(COFF_SectionHeader));
+
+    if (file.length < section_offset + sizeof(COFF_SectionHeader)) {
+        return false;
+    }
+
+    COFF_SectionHeader* sec = (COFF_SectionHeader*) &file.data[section_offset];
+    *out_sec = (TB_ObjectSection) { .flags = sec->characteristics };
+
+    // Parse string table name stuff
+    if (sec->name[0] == '/') {
+        // string table access
+        int offset = tb__parse_decimal_int(7, &sec->name[1]);
+        if (file.length > offset) {
+            return false;
+        }
+
+        const uint8_t* data = &parser->string_table.data[offset];
+        out_sec->name = (TB_Slice){ strlen((const char*) data), data };
+    } else {
+        // normal inplace string
+        size_t len = strlen(sec->name);
+        out_sec->name = (TB_Slice){ len, (uint8_t*) sec->name };
+    }
+
+    // Parse relocations
+    if (sec->num_reloc > 0) {
+        out_sec->relocation_count = sec->num_reloc;
+        COFF_ImageReloc* src_relocs = (COFF_ImageReloc*) &file.data[sec->pointer_to_reloc];
+
+        TB_ObjectReloc* dst_relocs = tb_platform_heap_alloc(sec->num_reloc * sizeof(TB_ObjectReloc));
+        FOREACH_N(j, 0, sec->num_reloc) {
+            dst_relocs[j] = (TB_ObjectReloc){ 0 };
+            switch (src_relocs[j].Type) {
+                case IMAGE_REL_AMD64_ADDR32NB: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32NB; break;
+                case IMAGE_REL_AMD64_ADDR32:   dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32; break;
+                case IMAGE_REL_AMD64_ADDR64:   dst_relocs[j].type = TB_OBJECT_RELOC_ADDR64; break;
+                case IMAGE_REL_AMD64_SECREL:   dst_relocs[j].type = TB_OBJECT_RELOC_SECREL; break;
+                case IMAGE_REL_AMD64_SECTION:  dst_relocs[j].type = TB_OBJECT_RELOC_SECTION; break;
+
+                case IMAGE_REL_AMD64_REL32:
+                case IMAGE_REL_AMD64_REL32_1:
+                case IMAGE_REL_AMD64_REL32_2:
+                case IMAGE_REL_AMD64_REL32_3:
+                case IMAGE_REL_AMD64_REL32_4:
+                case IMAGE_REL_AMD64_REL32_5:
+                dst_relocs[j].type = TB_OBJECT_RELOC_REL32;
+                break;
+
+                default: tb_todo();
+            }
+
+            if (src_relocs[j].Type >= IMAGE_REL_AMD64_REL32 && src_relocs[j].Type <= IMAGE_REL_AMD64_REL32_5) {
+                dst_relocs[j].addend = 4 + (src_relocs[j].Type - IMAGE_REL_AMD64_REL32);
+            }
+
+            dst_relocs[j].symbol_index = src_relocs[j].SymbolTableIndex;
+            dst_relocs[j].virtual_address = src_relocs[j].VirtualAddress;
+        }
+
+        out_sec->relocations = dst_relocs;
+    }
+
+    // Parse virtual region
+    out_sec->virtual_address = sec->virtual_address;
+    out_sec->virtual_size = sec->misc.virtual_size;
+
+    // Read raw data (if applies)
+    if (sec->raw_data_size) {
+        assert(sec->raw_data_pos + sec->raw_data_size < file.length);
+        out_sec->raw_data = (TB_Slice){ sec->raw_data_size, &file.data[sec->raw_data_pos] };
+    }
+
+    return true;
+}
+
+TB_ObjectSymbolType classify_symbol_type(uint16_t st_class) {
+    switch (st_class) {
+        case 2:    return TB_OBJECT_SYMBOL_EXTERN;
+        case 3:    return TB_OBJECT_SYMBOL_STATIC;
+        case 6:    return TB_OBJECT_SYMBOL_STATIC;
+        case 0x68: return TB_OBJECT_SYMBOL_SECTION;
+        case 0x69: return TB_OBJECT_SYMBOL_WEAK_EXTERN;
+        default: return TB_OBJECT_SYMBOL_UNKNOWN;
+    }
+}
+
+size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym) {
+    TB_Slice file = parser->file;
+    size_t symbol_offset = parser->symbol_table + (i * sizeof(COFF_Symbol));
+
+    if (file.length < symbol_offset + sizeof(COFF_Symbol)) {
+        return 0;
+    }
+
+    COFF_Symbol* sym = (COFF_Symbol*) &file.data[symbol_offset];
+    *out_sym = (TB_ObjectSymbol) {
+        .ordinal = i,
+        .type = classify_symbol_type(sym->storage_class),
+        .section_num = sym->section_number,
+        .value = sym->value
+    };
+
+    // Parse string table name stuff
+    if (sym->long_name[0] == 0) {
+        // string table access (read a cstring)
+        // TODO(NeGate): bounds check this
+        const uint8_t* data = &parser->string_table.data[sym->long_name[1]];
+        out_sym->name = (TB_Slice){ strlen((const char*) data), data };
+    } else {
+        // normal inplace string
+        size_t len = strlen((const char*) sym->short_name);
+        out_sym->name = (TB_Slice){ len, sym->short_name };
+    }
+
+    // TODO(NeGate): Process aux symbols
+    if (sym->aux_symbols_count) {
+        out_sym->extra = &sym[1];
+
+        // FOREACH_N(j, 0, sym->aux_symbols_count) {}
+    }
+
+    return sym->aux_symbols_count + 1;
+}
+
+#endif // TB_COFF_IMPL

+ 170 - 0
src/tilde/tb_elf.h

@@ -0,0 +1,170 @@
+#ifndef TB_ELF_H
+#define TB_ELF_H
+
+#include <stdint.h>
+
+#define TB_EI_MAG0       0
+#define TB_EI_MAG1       1
+#define TB_EI_MAG2       2
+#define TB_EI_MAG3       3
+#define TB_EI_CLASS      4  /* Class of machine. */
+#define TB_EI_DATA       5  /* Data format. */
+#define TB_EI_VERSION    6  /* ELF format version. */
+#define TB_EI_OSABI      7  /* Operating system / ABI identification */
+#define TB_EI_ABIVERSION 8  /* ABI version */
+#define TB_OLD_EI_BRAND  8  /* Start of architecture identification. */
+#define TB_EI_PAD        9  /* Start of padding (per SVR4 ABI). */
+#define TB_EI_NIDENT     16 /* Size of e_ident array. */
+
+/* Values for e_type. */
+#define TB_ET_NONE   0      /* Unknown type. */
+#define TB_ET_REL    1      /* Relocatable. */
+#define TB_ET_EXEC   2      /* Executable. */
+#define TB_ET_DYN    3      /* Shared object. */
+#define TB_ET_CORE   4      /* Core file. */
+#define TB_ET_LOOS   0xfe00 /* First operating system specific. */
+#define TB_ET_HIOS   0xfeff /* Last operating system-specific. */
+#define TB_ET_LOPROC 0xff00 /* First processor-specific. */
+#define TB_ET_HIPROC 0xffff /* Last processor-specific. */
+
+/* Values for e_machine. */
+#define TB_EM_NONE    0   /* Unknown machine. */
+#define TB_EM_X86_64  62  /* Advanced Micro Devices x86-64 */
+#define TB_EM_AARCH64 183 /* AArch64 (64-bit ARM) */
+
+/* sh_type */
+#define TB_SHT_NULL     0 /* inactive */
+#define TB_SHT_PROGBITS 1 /* program defined information */
+#define TB_SHT_SYMTAB   2 /* symbol table section */
+#define TB_SHT_STRTAB   3 /* string table section */
+#define TB_SHT_RELA     4 /* relocation section with addends */
+#define TB_SHT_NOBITS   8 /* no space section */
+
+/* Flags for sh_flags. */
+#define TB_SHF_WRITE            0x1        /* Section contains writable data. */
+#define TB_SHF_ALLOC            0x2        /* Section occupies memory. */
+#define TB_SHF_EXECINSTR        0x4        /* Section contains instructions. */
+#define TB_SHF_MERGE            0x10       /* Section may be merged. */
+#define TB_SHF_STRINGS          0x20       /* Section contains strings. */
+#define TB_SHF_INFO_LINK        0x40       /* sh_info holds section index. */
+#define TB_SHF_LINK_ORDER       0x80       /* Special ordering requirements. */
+#define TB_SHF_OS_NONCONFORMING 0x100      /* OS-specific processing required. */
+#define TB_SHF_GROUP            0x200      /* Member of section group. */
+#define TB_SHF_TLS              0x400      /* Section contains TLS data. */
+#define TB_SHF_MASKOS           0x0ff00000 /* OS-specific semantics. */
+#define TB_SHF_MASKPROC         0xf0000000 /* Processor-specific semantics. */
+
+/* Values for p_flags. */
+#define TB_PF_X		0x1        /* Executable. */
+#define TB_PF_W		0x2        /* Writable. */
+#define TB_PF_R		0x4        /* Readable. */
+#define TB_PF_MASKOS   0x0ff00000 /* Operating system-specific. */
+#define TB_PF_MASKPROC 0xf0000000 /* Processor-specific. */
+
+/* Values for p_type. */
+#define TB_PT_NULL      0	/* Unused entry. */
+#define TB_PT_LOAD      1	/* Loadable segment. */
+#define TB_PT_DYNAMIC   2	/* Dynamic linking information segment. */
+#define TB_PT_INTERP    3	/* Pathname of interpreter. */
+#define TB_PT_NOTE      4	/* Auxiliary information. */
+#define TB_PT_SHLIB     5	/* Reserved (not used). */
+#define TB_PT_PHDR      6	/* Location of program header itself. */
+#define TB_PT_TLS       7	/* Thread local storage segment */
+
+/* Values for relocation */
+typedef enum {
+    TB_ELF_X86_64_NONE     = 0,
+    TB_ELF_X86_64_64       = 1,
+    TB_ELF_X86_64_PC32     = 2,
+    TB_ELF_X86_64_GOT32    = 3,
+    TB_ELF_X86_64_PLT32    = 4,
+    TB_ELF_X86_64_GOTPCREL = 9,
+} TB_ELF_RelocType;
+
+// ST_TYPE
+#define TB_ELF64_STT_NOTYPE  0
+#define TB_ELF64_STT_OBJECT  1
+#define TB_ELF64_STT_FUNC    2
+#define TB_ELF64_STT_SECTION 3
+
+// ST_INFO
+#define TB_ELF64_STB_LOCAL  0
+#define TB_ELF64_STB_GLOBAL 1
+#define TB_ELF64_STB_WEAK   2
+
+/* Macros for accessing the fields of st_info. */
+#define TB_ELF64_ST_BIND(info) ((info) >> 4)
+#define TB_ELF64_ST_TYPE(info) ((info) & 0xf)
+
+#define TB_ELF64_ST_INFO(b, t) (((b) << 4) | ((t) & 0xF))
+
+#define TB_ELF64_R_SYM(i)     ((i) >> 32u)
+#define TB_ELF64_R_TYPE(i)    ((i)&0xffffffffULL)
+#define TB_ELF64_R_INFO(s, t) (((uint64_t)(s) << 32ULL) + ((uint64_t)(t) & 0xffffffffULL))
+
+// http://web.mit.edu/freebsd/head/sys/sys/elf64.h
+// https://cirosantilli.com/elf-hello-world#minimal-elf-file
+// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+typedef struct {
+    uint8_t  ident[16];
+    uint16_t type;
+    uint16_t machine;
+    uint32_t version;
+    uint64_t entry;
+    uint64_t phoff;
+    uint64_t shoff;
+    uint32_t flags;
+    uint16_t ehsize;
+    uint16_t phentsize;
+    uint16_t phnum;
+    uint16_t shentsize;
+    uint16_t shnum;
+    uint16_t shstrndx;
+} TB_Elf64_Ehdr;
+
+typedef struct {
+    uint32_t name;
+    uint32_t type;
+    uint64_t flags;
+    uint64_t addr;
+    uint64_t offset;
+    uint64_t size;
+    uint32_t link;
+    uint32_t info;
+    uint64_t addralign;
+    uint64_t entsize;
+} TB_Elf64_Shdr;
+
+// Segment header for ELF64.
+typedef struct {
+    uint32_t type;   // Type of segment
+    uint32_t flags;  // Segment flags
+    uint64_t offset; // File offset where segment is located, in bytes
+    uint64_t vaddr;  // Virtual address of beginning of segment
+    uint64_t paddr;  // Physical addr of beginning of segment (OS-specific)
+    uint64_t filesz; // Num. of bytes in file image of segment (may be zero)
+    uint64_t memsz;  // Num. of bytes in mem image of segment (may be zero)
+    uint64_t align;  // Segment alignment constraint
+} TB_Elf64_Phdr;
+
+typedef struct {
+    uint32_t name;
+    uint8_t  info;
+    uint8_t  other;
+    uint16_t shndx;
+    uint64_t value;
+    uint64_t size;
+} TB_Elf64_Sym;
+
+typedef struct {
+    uint64_t offset;
+    uint64_t info;
+    int64_t  addend;
+} TB_Elf64_Rela;
+
+typedef struct {
+    uint64_t offset;
+    uint64_t info;
+} TB_Elf64_Rel;
+
+#endif /* TB_ELF_H */

+ 132 - 0
src/tilde/tb_formats.h

@@ -0,0 +1,132 @@
+// This handles the generalized executable/object format parsing stuff
+#ifndef TB_OBJECT_H
+#define TB_OBJECT_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+typedef enum {
+    TB_OBJECT_RELOC_NONE, // how?
+
+    // Target independent
+    TB_OBJECT_RELOC_ADDR32,
+    TB_OBJECT_RELOC_ADDR64, // unsupported on 32bit platforms
+    TB_OBJECT_RELOC_SECREL,
+    TB_OBJECT_RELOC_SECTION,
+
+    // COFF only
+    TB_OBJECT_RELOC_ADDR32NB, // Relative virtual address
+
+    // x64 only
+    TB_OBJECT_RELOC_REL32,    // relative 32bit displacement
+
+    // Aarch64 only
+    TB_OBJECT_RELOC_BRANCH26, // 26bit displacement for B and BL instructions
+    TB_OBJECT_RELOC_REL21,    // for ADR instructions
+
+    // TODO(NeGate): fill in the rest of this later
+} TB_ObjectRelocType;
+
+typedef struct {
+    TB_ObjectRelocType type;
+    uint32_t symbol_index;
+    size_t virtual_address;
+    size_t addend;
+} TB_ObjectReloc;
+
+typedef enum {
+    TB_OBJECT_SYMBOL_UNKNOWN,
+    TB_OBJECT_SYMBOL_EXTERN,      // exported
+    TB_OBJECT_SYMBOL_WEAK_EXTERN, // weak
+    TB_OBJECT_SYMBOL_IMPORT,      // forward decl
+    TB_OBJECT_SYMBOL_STATIC,      // local
+    TB_OBJECT_SYMBOL_SECTION,     // local
+} TB_ObjectSymbolType;
+
+typedef struct {
+    TB_ObjectSymbolType type;
+    int section_num;
+
+    uint32_t ordinal;
+    uint32_t value;
+
+    TB_Slice name;
+
+    // for COFF, this is the auxillary
+    void* extra;
+
+    // this is zeroed out by the loader and left for the user to do crap with
+    void* user_data;
+} TB_ObjectSymbol;
+
+typedef struct {
+    TB_Slice name;
+    uint32_t flags;
+
+    size_t virtual_address;
+    size_t virtual_size;
+
+    // You can have a virtual size without having a raw
+    // data size, that's how the BSS section works
+    TB_Slice raw_data;
+
+    size_t relocation_count;
+    TB_ObjectReloc* relocations;
+
+    // this is zeroed out by the loader and left for the user to do crap with
+    void* user_data;
+} TB_ObjectSection;
+
+typedef enum {
+    TB_OBJECT_FILE_UNKNOWN,
+
+    TB_OBJECT_FILE_COFF,
+    TB_OBJECT_FILE_ELF64
+} TB_ObjectFileType;
+
+typedef struct {
+    TB_ObjectFileType type;
+    TB_Arch           arch;
+
+    TB_Slice          name;
+    TB_Slice          ar_name;
+
+    size_t           symbol_count;
+    TB_ObjectSymbol* symbols;
+
+    size_t           section_count;
+    TB_ObjectSection sections[];
+} TB_ObjectFile;
+
+////////////////////////////////
+// Archive parser
+////////////////////////////////
+typedef struct {
+    TB_Slice name;
+
+    // if import_name is empty, we're dealing with an object file
+    TB_Slice import_name;
+    uint16_t ordinal;
+
+    TB_Slice content;
+} TB_ArchiveEntry;
+
+typedef struct {
+    TB_Slice file;
+    size_t pos;
+
+    size_t member_count;
+    uint32_t* members;
+
+    size_t symbol_count;
+    uint16_t* symbols;
+
+    TB_Slice strtbl;
+} TB_ArchiveFileParser;
+
+// We do this to parse the header
+bool tb_archive_parse(TB_Slice file, TB_ArchiveFileParser* restrict out_parser);
+// After that we can enumerate any symbol entries to resolve imports
+size_t tb_archive_parse_entries(TB_ArchiveFileParser* restrict parser, size_t i, size_t count, TB_ArchiveEntry* out_entry);
+
+#endif // TB_OBJECT_H

+ 90 - 0
src/tilde/tb_x64.h

@@ -0,0 +1,90 @@
+#ifndef TB_X64_H
+#define TB_X64_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef enum {
+    // uses xmm registers for the reg array
+    TB_X86_INSTR_XMMREG = (1u << 0u),
+
+    // r/m is a memory operand
+    TB_X86_INSTR_USE_MEMOP = (1u << 1u),
+
+    // r/m is a rip-relative address (TB_X86_INSTR_USE_MEMOP is always set when this is set)
+    TB_X86_INSTR_USE_RIPMEM = (1u << 2u),
+
+    // LOCK prefix is present
+    TB_X86_INSTR_LOCK = (1u << 3u),
+
+    // uses a signed immediate
+    TB_X86_INSTR_IMMEDIATE = (1u << 4u),
+
+    // absolute means it's using the 64bit immediate (cannot be applied while a memory operand is active)
+    TB_X86_INSTR_ABSOLUTE = (1u << 5u),
+
+    // set if the r/m can be found on the right hand side
+    TB_X86_INSTR_DIRECTION = (1u << 6u),
+
+    // uses the second data type because the instruction is weird like MOVSX or MOVZX
+    TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u)
+} TB_X86_InstFlags;
+
+typedef enum {
+    TB_X86_SEGMENT_DEFAULT = 0,
+
+    TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS,
+    TB_X86_SEGMENT_SS, TB_X86_SEGMENT_DS,
+    TB_X86_SEGMENT_GS, TB_X86_SEGMENT_FS,
+} TB_X86_Segment;
+
+typedef enum {
+    TB_X86_TYPE_NONE = 0,
+
+    TB_X86_TYPE_BYTE,    // 1
+    TB_X86_TYPE_WORD,    // 2
+    TB_X86_TYPE_DWORD,   // 4
+    TB_X86_TYPE_QWORD,   // 8
+
+    TB_X86_TYPE_PBYTE,   // int8 x 16 = 16
+    TB_X86_TYPE_PWORD,   // int16 x 8 = 16
+    TB_X86_TYPE_PDWORD,  // int32 x 4 = 16
+    TB_X86_TYPE_PQWORD,  // int64 x 2 = 16
+
+    TB_X86_TYPE_SSE_SS,  // float32 x 1 = 4
+    TB_X86_TYPE_SSE_SD,  // float64 x 1 = 8
+    TB_X86_TYPE_SSE_PS,  // float32 x 4 = 16
+    TB_X86_TYPE_SSE_PD,  // float64 x 2 = 16
+
+    TB_X86_TYPE_XMMWORD, // the generic idea of them
+} TB_X86_DataType;
+
+typedef struct {
+    int16_t type;
+
+    // registers (there's 4 max taking up 4bit slots each)
+    uint16_t regs;
+    uint8_t flags;
+
+    // bitpacking amirite
+    TB_X86_DataType data_type  : 4;
+    TB_X86_DataType data_type2 : 4;
+    TB_X86_Segment segment     : 4;
+    uint8_t length             : 4;
+
+    // memory operand
+    //   X86_INSTR_USE_MEMOP
+    int32_t disp;
+
+    // immediate operand
+    //   imm for INSTR_IMMEDIATE
+    //   abs for INSTR_ABSOLUTE
+    union {
+        int32_t  imm;
+        uint64_t abs;
+    };
+} TB_X86_Inst;
+
+TB_X86_Inst tb_x86_disasm(size_t length, const uint8_t data[length]);
+
+#endif /* TB_X64_H */

+ 443 - 0
src/tilde_builtin.cpp

@@ -0,0 +1,443 @@
+gb_internal cgValue cg_builtin_len(cgProcedure *p, cgValue value) {
+	Type *t = base_type(value.type);
+
+	switch (t->kind) {
+	case Type_Basic:
+		switch (t->Basic.kind) {
+		case Basic_string:
+			{
+				GB_ASSERT(value.kind == cgValue_Addr);
+				cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+				cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
+				return cg_emit_load(p, len_ptr);
+			}
+		case Basic_cstring:
+			GB_PANIC("TODO(bill): len(cstring)");
+			break;
+		}
+		break;
+	case Type_Array:
+		return cg_const_int(p, t_int, t->Array.count);
+	case Type_EnumeratedArray:
+		return cg_const_int(p, t_int, t->EnumeratedArray.count);
+	case Type_Slice:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
+			return cg_emit_load(p, len_ptr);
+		}
+	case Type_DynamicArray:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
+			return cg_emit_load(p, len_ptr);
+		}
+	case Type_Map:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
+			return cg_emit_conv(p, cg_emit_load(p, len_ptr), t_int);
+		}
+	case Type_Struct:
+		GB_ASSERT(is_type_soa_struct(t));
+		break;
+	}
+
+	GB_PANIC("TODO(bill): cg_builtin_len %s", type_to_string(t));
+	return {};
+}
+
+gb_internal cgValue cg_builtin_cap(cgProcedure *p, cgValue value) {
+	Type *t = base_type(value.type);
+
+	switch (t->kind) {
+	case Type_Basic:
+		switch (t->Basic.kind) {
+		case Basic_string:
+			{
+				GB_ASSERT(value.kind == cgValue_Addr);
+				cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+				cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
+				return cg_emit_load(p, len_ptr);
+			}
+		case Basic_cstring:
+			GB_PANIC("TODO(bill): cap(cstring)");
+			break;
+		}
+		break;
+	case Type_Array:
+		return cg_const_int(p, t_int, t->Array.count);
+	case Type_EnumeratedArray:
+		return cg_const_int(p, t_int, t->EnumeratedArray.count);
+	case Type_Slice:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
+			return cg_emit_load(p, len_ptr);
+		}
+	case Type_DynamicArray:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue len_ptr = cg_emit_struct_ep(p, ptr, 2);
+			return cg_emit_load(p, len_ptr);
+		}
+	case Type_Map:
+		{
+			TB_DataType dt_uintptr = cg_data_type(t_uintptr);
+			TB_Node *zero = tb_inst_uint(p->func, dt_uintptr, 0);
+			TB_Node *one  = tb_inst_uint(p->func, dt_uintptr, 0);
+			TB_Node *mask = tb_inst_uint(p->func, dt_uintptr, MAP_CACHE_LINE_SIZE-1);
+
+			TB_Node *data = cg_emit_struct_ev(p, value, 0).node;
+			TB_Node *log2_cap = tb_inst_and(p->func, data, mask);
+			TB_Node *cap = tb_inst_shl(p->func, one, log2_cap, cast(TB_ArithmeticBehavior)0);
+			TB_Node *cmp = tb_inst_cmp_eq(p->func, data, zero);
+
+			cgValue res = cg_value(tb_inst_select(p->func, cmp, zero, cap), t_uintptr);
+			return cg_emit_conv(p, res, t_int);
+		}
+	case Type_Struct:
+		GB_ASSERT(is_type_soa_struct(t));
+		break;
+	}
+
+	GB_PANIC("TODO(bill): cg_builtin_cap %s", type_to_string(t));
+	return {};
+}
+
+
+gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &value) {
+	Type *t = base_type(value.type);
+	cgValue res = {};
+	switch (t->kind) {
+	case Type_Slice:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0);
+			res = cg_emit_load(p, data_ptr);
+			GB_ASSERT(is_type_multi_pointer(res.type));
+		}
+		break;
+	case Type_DynamicArray:
+		{
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0);
+			res = cg_emit_load(p, data_ptr);
+		}
+		break;
+	case Type_Basic:
+		if (t->Basic.kind == Basic_string) {
+			GB_ASSERT(value.kind == cgValue_Addr);
+			cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
+			cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0);
+			res = cg_emit_load(p, data_ptr);
+		} else if (t->Basic.kind == Basic_cstring) {
+			res = cg_emit_conv(p, value, t_u8_multi_ptr);
+		}
+		break;
+	case Type_Pointer:
+		GB_ASSERT(is_type_array_like(t->Pointer.elem));
+		GB_ASSERT(value.kind == cgValue_Value);
+		res = cg_value(value.node, alloc_type_multi_pointer(base_array_type(t->Pointer.elem)));
+		break;
+	case Type_MultiPointer:
+
+		GB_PANIC("TODO(bill) %s", type_to_string(value.type));
+		// res = cg_emit_conv(p, value, tv.type);
+		break;
+	}
+	GB_ASSERT(res.node != nullptr);
+	return res;
+}
+
+gb_internal cgValue cg_builtin_min(cgProcedure *p, Type *t, cgValue x, cgValue y) {
+	x = cg_emit_conv(p, x, t);
+	y = cg_emit_conv(p, y, t);
+	return cg_emit_select(p, cg_emit_comp(p, Token_Lt, x, y), x, y);
+}
+gb_internal cgValue cg_builtin_max(cgProcedure *p, Type *t, cgValue x, cgValue y) {
+	x = cg_emit_conv(p, x, t);
+	y = cg_emit_conv(p, y, t);
+	return cg_emit_select(p, cg_emit_comp(p, Token_Gt, x, y), x, y);
+}
+
+gb_internal cgValue cg_builtin_abs(cgProcedure *p, cgValue const &x) {
+	if (is_type_unsigned(x.type)) {
+		return x;
+	}
+
+	if (is_type_quaternion(x.type)) {
+		GB_PANIC("TODO(bill): abs quaternion");
+	} else if (is_type_complex(x.type)) {
+		GB_PANIC("TODO(bill): abs complex");
+	}
+
+	TB_DataType dt = cg_data_type(x.type);
+	GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+	TB_Node *zero = nullptr;
+	if (dt.type == TB_FLOAT) {
+		if (dt.data == 32) {
+			zero = tb_inst_float32(p->func, 0);
+		} else if (dt.data == 64) {
+			zero = tb_inst_float64(p->func, 0);
+		}
+	} else {
+		zero = tb_inst_uint(p->func, dt, 0);
+	}
+	GB_ASSERT(zero != nullptr);
+
+	cgValue cond = cg_emit_comp(p, Token_Lt, x, cg_value(zero, x.type));
+	cgValue neg = cg_emit_unary_arith(p, Token_Sub, x, x.type);
+	return cg_emit_select(p, cond, neg, x);
+}
+
+gb_internal cgValue cg_builtin_clamp(cgProcedure *p, Type *t, cgValue const &x, cgValue const &min, cgValue const &max) {
+	cgValue z = x;
+	z = cg_builtin_max(p, t, z, min);
+	z = cg_builtin_min(p, t, z, max);
+	return z;
+}
+
+
+
+gb_internal cgValue cg_builtin_mem_zero(cgProcedure *p, cgValue const &ptr, cgValue const &len) {
+	GB_ASSERT(ptr.kind == cgValue_Value);
+	GB_ASSERT(len.kind == cgValue_Value);
+	tb_inst_memzero(p->func, ptr.node, len.node, 1, false);
+	return ptr;
+}
+
+gb_internal cgValue cg_builtin_mem_copy(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len) {
+	GB_ASSERT(dst.kind == cgValue_Value);
+	GB_ASSERT(src.kind == cgValue_Value);
+	GB_ASSERT(len.kind == cgValue_Value);
+	// TODO(bill): This needs to be memmove
+	tb_inst_memcpy(p->func, dst.node, src.node, len.node, 1, false);
+	return dst;
+}
+
+gb_internal cgValue cg_builtin_mem_copy_non_overlapping(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len) {
+	GB_ASSERT(dst.kind == cgValue_Value);
+	GB_ASSERT(src.kind == cgValue_Value);
+	GB_ASSERT(len.kind == cgValue_Value);
+	tb_inst_memcpy(p->func, dst.node, src.node, len.node, 1, false);
+	return dst;
+}
+
+
+gb_internal cgValue cg_build_builtin(cgProcedure *p, BuiltinProcId id, Ast *expr) {
+	ast_node(ce, CallExpr, expr);
+
+	if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
+		GB_PANIC("TODO(bill): cg_build_builtin_simd_proc");
+		// return cg_build_builtin_simd_proc(p, expr, tv, id);
+	}
+
+	String builtin_name = builtin_procs[id].name;
+
+	switch (id) {
+	case BuiltinProc_DIRECTIVE: {
+		ast_node(bd, BasicDirective, ce->proc);
+		String name = bd->name.string;
+		GB_ASSERT(name == "location");
+		String procedure = p->entity->token.string;
+		TokenPos pos = ast_token(ce->proc).pos;
+		if (ce->args.count > 0) {
+			Ast *ident = unselector_expr(ce->args[0]);
+			GB_ASSERT(ident->kind == Ast_Ident);
+			Entity *e = entity_of_node(ident);
+			GB_ASSERT(e != nullptr);
+
+			if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
+				procedure = e->parent_proc_decl->entity->token.string;
+			} else {
+				procedure = str_lit("");
+			}
+			pos = e->token.pos;
+
+		}
+		return cg_emit_source_code_location_as_global(p, procedure, pos);
+	} break;
+
+	case BuiltinProc_len: {
+		cgValue v = cg_build_expr(p, ce->args[0]);
+		Type *t = base_type(v.type);
+		if (is_type_pointer(t)) {
+			// IMPORTANT TODO(bill): Should there be a nil pointer check?
+			v = cg_emit_load(p, v);
+			t = type_deref(t);
+		}
+		return cg_builtin_len(p, v);
+	}
+
+	case BuiltinProc_cap: {
+		cgValue v = cg_build_expr(p, ce->args[0]);
+		Type *t = base_type(v.type);
+		if (is_type_pointer(t)) {
+			// IMPORTANT TODO(bill): Should there be a nil pointer check?
+			v = cg_emit_load(p, v);
+			t = type_deref(t);
+		}
+		return cg_builtin_cap(p, v);
+	}
+
+	case BuiltinProc_raw_data:
+		{
+			cgValue v = cg_build_expr(p, ce->args[0]);
+			return cg_builtin_raw_data(p, v);
+		}
+
+	case BuiltinProc_min:
+		if (ce->args.count == 2) {
+			Type *t = type_of_expr(expr);
+			cgValue x = cg_build_expr(p, ce->args[0]);
+			cgValue y = cg_build_expr(p, ce->args[1]);
+			return cg_builtin_min(p, t, x, y);
+		} else {
+			Type *t = type_of_expr(expr);
+			cgValue x = cg_build_expr(p, ce->args[0]);
+			for (isize i = 1; i < ce->args.count; i++) {
+				cgValue y = cg_build_expr(p, ce->args[i]);
+				x = cg_builtin_min(p, t, x, y);
+			}
+			return x;
+		}
+		break;
+	case BuiltinProc_max:
+		if (ce->args.count == 2) {
+			Type *t = type_of_expr(expr);
+			cgValue x = cg_build_expr(p, ce->args[0]);
+			cgValue y = cg_build_expr(p, ce->args[1]);
+			return cg_builtin_max(p, t, x, y);
+		} else {
+			Type *t = type_of_expr(expr);
+			cgValue x = cg_build_expr(p, ce->args[0]);
+			for (isize i = 1; i < ce->args.count; i++) {
+				cgValue y = cg_build_expr(p, ce->args[i]);
+				x = cg_builtin_max(p, t, x, y);
+			}
+			return x;
+		}
+		break;
+
+	case BuiltinProc_abs:
+		{
+			cgValue x = cg_build_expr(p, ce->args[0]);
+			return cg_builtin_abs(p, x);
+		}
+
+	case BuiltinProc_clamp:
+		{
+			cgValue x   = cg_build_expr(p, ce->args[0]);
+			cgValue min = cg_build_expr(p, ce->args[1]);
+			cgValue max = cg_build_expr(p, ce->args[2]);
+			return cg_builtin_clamp(p, type_of_expr(expr), x, min, max);
+		}
+
+	case BuiltinProc_debug_trap:
+		tb_inst_debugbreak(p->func);
+		return {};
+	case BuiltinProc_trap:
+		tb_inst_trap(p->func);
+		return {};
+
+	case BuiltinProc_mem_zero:
+		{
+			cgValue ptr = cg_build_expr(p, ce->args[0]);
+			cgValue len = cg_build_expr(p, ce->args[1]);
+			return cg_builtin_mem_zero(p, ptr, len);
+		}
+
+	case BuiltinProc_mem_copy:
+		{
+			cgValue dst = cg_build_expr(p, ce->args[0]);
+			cgValue src = cg_build_expr(p, ce->args[1]);
+			cgValue len = cg_build_expr(p, ce->args[2]);
+			return cg_builtin_mem_copy(p, dst, src, len);
+		}
+
+	case BuiltinProc_mem_copy_non_overlapping:
+		{
+			cgValue dst = cg_build_expr(p, ce->args[0]);
+			cgValue src = cg_build_expr(p, ce->args[1]);
+			cgValue len = cg_build_expr(p, ce->args[2]);
+			return cg_builtin_mem_copy_non_overlapping(p, dst, src, len);
+		}
+
+
+	case BuiltinProc_overflow_add:
+		{
+			Type *res_type = type_of_expr(expr);
+			GB_ASSERT(res_type->kind == Type_Tuple);
+			GB_ASSERT(res_type->Tuple.variables.count == 2);
+			// TODO(bill): do a proper overflow add
+			Type *type = res_type->Tuple.variables[0]->type;
+			Type *ok_type = res_type->Tuple.variables[1]->type;
+			cgValue x = cg_build_expr(p, ce->args[0]);
+			cgValue y = cg_build_expr(p, ce->args[1]);
+			x = cg_emit_conv(p, x, type);
+			y = cg_emit_conv(p, y, type);
+			cgValue res = cg_emit_arith(p, Token_Add, x, y, type);
+			cgValue ok  = cg_const_int(p, ok_type, false);
+
+			return cg_value_multi2(res, ok, res_type);
+		}
+
+
+	case BuiltinProc_ptr_offset:
+		{
+			cgValue ptr = cg_build_expr(p, ce->args[0]);
+			cgValue len = cg_build_expr(p, ce->args[1]);
+			len = cg_emit_conv(p, len, t_int);
+			return cg_emit_ptr_offset(p, ptr, len);
+		}
+	case BuiltinProc_ptr_sub:
+		{
+			Type *elem0 = type_deref(type_of_expr(ce->args[0]));
+			Type *elem1 = type_deref(type_of_expr(ce->args[1]));
+			GB_ASSERT(are_types_identical(elem0, elem1));
+			Type *elem = elem0;
+
+			cgValue ptr0 = cg_emit_conv(p, cg_build_expr(p, ce->args[0]), t_uintptr);
+			cgValue ptr1 = cg_emit_conv(p, cg_build_expr(p, ce->args[1]), t_uintptr);
+
+			cgValue diff = cg_emit_arith(p, Token_Sub, ptr0, ptr1, t_uintptr);
+			diff = cg_emit_conv(p, diff, t_int);
+			return cg_emit_arith(p, Token_Quo, diff, cg_const_int(p, t_int, type_size_of(elem)), t_int);
+		}
+
+	case BuiltinProc_type_info_of:
+		{
+			Ast *arg = ce->args[0];
+			TypeAndValue tav = type_and_value_of_expr(arg);
+			if (tav.mode == Addressing_Type) {
+				Type *t = default_type(type_of_expr(arg));
+				return cg_type_info(p, t);
+			}
+			GB_ASSERT(is_type_typeid(tav.type));
+
+			auto args = slice_make<cgValue>(permanent_allocator(), 1);
+			args[0] = cg_build_expr(p, arg);
+			return cg_emit_runtime_call(p, "__type_info_of", args);
+		}
+
+
+	case BuiltinProc_type_equal_proc:
+		return cg_equal_proc_value_for_type(p, ce->args[0]->tav.type);
+
+	case BuiltinProc_type_hasher_proc:
+		return cg_hasher_proc_value_for_type(p, ce->args[0]->tav.type);
+	}
+
+
+	GB_PANIC("TODO(bill): builtin procs %d %.*s", id, LIT(builtin_name));
+	return {};
+}
+

+ 1040 - 0
src/tilde_const.cpp

@@ -0,0 +1,1040 @@
+gb_internal bool cg_is_expr_constant_zero(Ast *expr) {
+	GB_ASSERT(expr != nullptr);
+	auto v = exact_value_to_integer(expr->tav.value);
+	if (v.kind == ExactValue_Integer) {
+		return big_int_cmp_zero(&v.value_integer) == 0;
+	}
+	return false;
+}
+
+gb_internal cgValue cg_const_nil(cgModule *m, cgProcedure *p, Type *type) {
+	GB_ASSERT(m != nullptr);
+	Type *original_type = type;
+	type = core_type(type);
+	i64 size = type_size_of(type);
+	i64 align = type_align_of(type);
+	TB_DataType dt = cg_data_type(type);
+	if (TB_IS_VOID_TYPE(dt)) {
+		char name[32] = {};
+		gb_snprintf(name, 31, "cnil$%u", 1+m->const_nil_guid.fetch_add(1));
+		TB_Global *global = tb_global_create(m->mod, -1, name, cg_debug_type(m, type), TB_LINKAGE_PRIVATE);
+		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), global, size, align, 0);
+
+		TB_Symbol *symbol = cast(TB_Symbol *)global;
+		if (p) {
+			TB_Node *node = tb_inst_get_symbol_address(p->func, symbol);
+			return cg_lvalue_addr(node, type);
+		} else {
+			return cg_value(symbol, type);
+		}
+	}
+
+	if (is_type_internally_pointer_like(type)) {
+		return cg_value(tb_inst_uint(p->func, dt, 0), type);
+	} else if (is_type_integer(type) || is_type_boolean(type) || is_type_bit_set(type) || is_type_typeid(type)) {
+		return cg_value(tb_inst_uint(p->func, dt, 0), type);
+	} else if (is_type_float(type)) {
+		switch (size) {
+		case 2:
+			return cg_value(tb_inst_uint(p->func, dt, 0), type);
+		case 4:
+			return cg_value(tb_inst_float32(p->func, 0), type);
+		case 8:
+			return cg_value(tb_inst_float64(p->func, 0), type);
+		}
+	}
+	GB_PANIC("TODO(bill): cg_const_nil %s", type_to_string(original_type));
+	return {};
+}
+
+gb_internal cgValue cg_const_nil(cgProcedure *p, Type *type) {
+	return cg_const_nil(p->module, p, type);
+}
+
+gb_internal TB_Global *cg_global_const_string(cgModule *m, String const &str, Type *type, TB_Global *global, i64 offset);
+gb_internal void cg_write_int_at_ptr(void *dst, i64 i, Type *original_type);
+
+gb_internal void cg_global_source_code_location_const(cgModule *m, String const &proc_name, TokenPos pos, TB_Global *global, i64 offset) {
+	// Source_Code_Location :: struct {
+	// 	file_path:    string,
+	// 	line, column: i32,
+	// 	procedure:    string,
+	// }
+
+	i64 file_path_offset = type_offset_of(t_source_code_location, 0);
+	i64 line_offset      = type_offset_of(t_source_code_location, 1);
+	i64 column_offset    = type_offset_of(t_source_code_location, 2);
+	i64 procedure_offset = type_offset_of(t_source_code_location, 3);
+
+	String file_path = get_file_path_string(pos.file_id);
+	if (file_path.len != 0) {
+		cg_global_const_string(m, file_path, t_string, global, offset+file_path_offset);
+	}
+
+	void *line_ptr   = tb_global_add_region(m->mod, global, offset+line_offset,   4);
+	void *column_ptr = tb_global_add_region(m->mod, global, offset+column_offset, 4);
+	cg_write_int_at_ptr(line_ptr,   pos.line,   t_i32);
+	cg_write_int_at_ptr(column_ptr, pos.column, t_i32);
+
+	if (proc_name.len != 0) {
+		cg_global_const_string(m, proc_name, t_string, global, offset+procedure_offset);
+	}
+}
+
+
+gb_internal cgValue cg_emit_source_code_location_as_global(cgProcedure *p, String const &proc_name, TokenPos pos) {
+	cgModule *m = p->module;
+	char name[32] = {};
+	gb_snprintf(name, 31, "scl$%u", 1+m->const_nil_guid.fetch_add(1));
+
+	TB_Global *global = tb_global_create(m->mod, -1, name, cg_debug_type(m, t_source_code_location), TB_LINKAGE_PRIVATE);
+	tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), global, type_size_of(t_source_code_location), type_align_of(t_source_code_location), 6);
+
+	cg_global_source_code_location_const(m, proc_name, pos, global, 0);
+
+	TB_Node *ptr = tb_inst_get_symbol_address(p->func, cast(TB_Symbol *)global);
+	return cg_lvalue_addr(ptr, t_source_code_location);
+}
+
+
+
+gb_internal void cg_write_big_int_at_ptr(void *dst, BigInt const *a, Type *original_type) {
+	GB_ASSERT(build_context.endian_kind == TargetEndian_Little);
+	size_t sz = cast(size_t)type_size_of(original_type);
+	if (big_int_is_zero(a)) {
+		gb_memset(dst, 0, sz);
+		return;
+	}
+	u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will be fine :P
+	u8 *rop = cast(u8 *)rop64;
+
+	size_t max_count = 0;
+	size_t written = 0;
+	size_t size = 1;
+	size_t nails = 0;
+	mp_endian endian = MP_LITTLE_ENDIAN;
+
+	max_count = mp_pack_count(a, nails, size);
+	if (sz < max_count) {
+		debug_print_big_int(a);
+		gb_printf_err("%s -> %tu\n", type_to_string(original_type), sz);;
+	}
+	GB_ASSERT_MSG(sz >= max_count, "max_count: %tu, sz: %tu, written: %tu, type %s", max_count, sz, written, type_to_string(original_type));
+	GB_ASSERT(gb_size_of(rop64) >= sz);
+
+	mp_err err = mp_pack(rop, sz, &written,
+	                     MP_LSB_FIRST,
+	                     size, endian, nails,
+	                     a);
+	GB_ASSERT(err == MP_OKAY);
+
+	if (!is_type_endian_little(original_type)) {
+		for (size_t i = 0; i < sz/2; i++) {
+			u8 tmp = rop[i];
+			rop[i] = rop[sz-1-i];
+			rop[sz-1-i] = tmp;
+		}
+	}
+
+	gb_memcopy(dst, rop, sz);
+	return;
+}
+
+
+gb_internal void cg_write_int_at_ptr(void *dst, i64 i, Type *original_type) {
+	ExactValue v = exact_value_i64(i);
+	cg_write_big_int_at_ptr(dst, &v.value_integer, original_type);
+}
+gb_internal void cg_write_uint_at_ptr(void *dst, u64 i, Type *original_type) {
+	ExactValue v = exact_value_u64(i);
+	cg_write_big_int_at_ptr(dst, &v.value_integer, original_type);
+}
+
+gb_internal TB_Global *cg_global_const_string(cgModule *m, String const &str, Type *type, TB_Global *global, i64 offset) {
+	GB_ASSERT(is_type_string(type));
+
+	char name[32] = {};
+	gb_snprintf(name, 31, "csb$%u", 1+m->const_nil_guid.fetch_add(1));
+	TB_Global *str_global = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+	i64 size = str.len+1;
+	tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), str_global, size, 1, 1);
+	u8 *data = cast(u8 *)tb_global_add_region(m->mod, str_global, 0, size);
+	gb_memcopy(data, str.text, str.len);
+	data[str.len] = 0;
+
+	if (is_type_cstring(type)) {
+		if (global) {
+			tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)str_global);
+		}
+		return str_global;
+	}
+
+	if (global == nullptr) {
+		gb_snprintf(name, 31, "cstr$%u", 1+m->const_nil_guid.fetch_add(1));
+		global = tb_global_create(m->mod, -1, name, cg_debug_type(m, type), TB_LINKAGE_PRIVATE);
+		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), global, type_size_of(type), type_align_of(type), 2);
+	}
+
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)str_global);
+	void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, str.len, t_int);
+
+	return global;
+}
+
+gb_internal bool cg_elem_type_can_be_constant(Type *t) {
+	t = base_type(t);
+	if (t == t_invalid) {
+		return false;
+	}
+	if (is_type_dynamic_array(t) || is_type_map(t)) {
+		return false;
+	}
+	return true;
+}
+
+
+gb_internal bool cg_is_elem_const(Ast *elem, Type *elem_type) {
+	if (!cg_elem_type_can_be_constant(elem_type)) {
+		return false;
+	}
+	if (elem->kind == Ast_FieldValue) {
+		elem = elem->FieldValue.value;
+	}
+	TypeAndValue tav = type_and_value_of_expr(elem);
+	GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(elem), type_to_string(tav.type));
+	return tav.value.kind != ExactValue_Invalid;
+}
+
+gb_internal bool cg_is_nested_possibly_constant(Type *ft, Selection const &sel, Ast *elem) {
+	GB_ASSERT(!sel.indirect);
+	for (i32 index : sel.index) {
+		Type *bt = base_type(ft);
+		switch (bt->kind) {
+		case Type_Struct:
+			// if (bt->Struct.is_raw_union) {
+				// return false;
+			// }
+			ft = bt->Struct.fields[index]->type;
+			break;
+		case Type_Array:
+			ft = bt->Array.elem;
+			break;
+		default:
+			return false;
+		}
+	}
+	return cg_is_elem_const(elem, ft);
+}
+
+gb_internal i64 cg_global_const_calculate_region_count_from_basic_type(Type *type) {
+	type = core_type(type);
+
+	switch (type->kind) {
+	case Type_Basic:
+		switch (type->Basic.kind) {
+		case Basic_string:  // ^u8 + int
+		case Basic_any:     // rawptr + typeid
+			return 2;
+		}
+		return 1;
+	case Type_Pointer:
+	case Type_MultiPointer:
+		return 2; // allows for offsets
+	case Type_Proc:
+		return 1;
+	case Type_Slice:
+		return 3; // alows for offsets
+	case Type_DynamicArray:
+		return 5;
+	case Type_Map:
+		return 4;
+
+	case Type_Enum:
+	case Type_BitSet:
+		return 1;
+
+	case Type_RelativePointer:
+	case Type_RelativeMultiPointer:
+		return 2; // allows for offsets
+
+	case Type_Matrix:
+		return 1;
+
+	case Type_Array:
+		{
+			Type *elem = type->Array.elem;
+			i64 count = cg_global_const_calculate_region_count_from_basic_type(elem);
+			return count*type->Array.count;
+		}
+	case Type_EnumeratedArray:
+		{
+			Type *elem = type->EnumeratedArray.elem;
+			i64 count = cg_global_const_calculate_region_count_from_basic_type(elem);
+			return count*type->EnumeratedArray.count;
+		}
+
+	case Type_Struct:
+		if (type->Struct.is_raw_union) {
+			i64 max_count = 0;
+			for (Entity *f : type->Struct.fields) {
+				i64 count = cg_global_const_calculate_region_count_from_basic_type(f->type);
+				max_count = gb_max(count, max_count);
+			}
+			return max_count;
+		} else {
+			i64 max_count = 0;
+			for (Entity *f : type->Struct.fields) {
+				max_count += cg_global_const_calculate_region_count_from_basic_type(f->type);
+			}
+			return max_count;
+		}
+		break;
+	case Type_Union:
+		{
+			i64 max_count = 0;
+			for (Type *t : type->Union.variants) {
+				i64 count = cg_global_const_calculate_region_count_from_basic_type(t);
+				max_count = gb_max(count, max_count);
+			}
+			return max_count+1;
+		}
+		break;
+
+	default:
+		GB_PANIC("TODO(bill): %s", type_to_string(type));
+		break;
+	}
+	return -1;
+}
+gb_internal isize cg_global_const_calculate_region_count(ExactValue const &value, Type *type) {
+	Type *bt = base_type(type);
+	if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
+		if (is_type_rune_array(type)) {
+			return 1;
+		}
+
+		Type *et = base_array_type(type);
+		i64 base_count = 2;
+		if (is_type_cstring(et)) {
+			base_count = 1;
+		}
+		return base_count * bt->Array.count;
+	} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
+		return 1;
+	} else if (is_type_array(type) &&
+		value.kind != ExactValue_Invalid &&
+		value.kind != ExactValue_String &&
+		value.kind != ExactValue_Compound) {
+		Type *elem = type->Array.elem;
+
+		i64 base_count = cg_global_const_calculate_region_count(value, elem);
+		return base_count * type->Array.count;
+	} else if (is_type_matrix(type) &&
+		value.kind != ExactValue_Invalid &&
+		value.kind != ExactValue_Compound) {
+		return 1;
+	} else if (is_type_simd_vector(type) &&
+		value.kind != ExactValue_Invalid &&
+		value.kind != ExactValue_Compound) {
+		return 1;
+	}
+
+	isize count = 0;
+	switch (value.kind) {
+	case ExactValue_Invalid:
+		return 0;
+	case ExactValue_Bool:
+	case ExactValue_Integer:
+	case ExactValue_Float:
+	case ExactValue_Typeid:
+	case ExactValue_Complex:
+	case ExactValue_Quaternion:
+		return 1;
+	case ExactValue_Pointer:
+		return 2;
+
+	case ExactValue_Procedure:
+		return 1;
+
+	case ExactValue_String:
+		if (is_type_string(type)) {
+			return 3;
+		} else if (is_type_cstring(type) || is_type_array_like(type)) {
+			return 2;
+		}
+		return 3;
+
+	case ExactValue_Compound: {
+		ast_node(cl, CompoundLit, value.value_compound);
+		Type *bt = base_type(type);
+		switch (bt->kind) {
+		case Type_Struct:
+			if (cl->elems[0]->kind == Ast_FieldValue) {
+				for (isize i = 0; i < cl->elems.count; i++) {
+					ast_node(fv, FieldValue, cl->elems[i]);
+					String name = fv->field->Ident.token.string;
+
+					Selection sel = lookup_field(type, name, false);
+					GB_ASSERT(!sel.indirect);
+
+					Entity *f = bt->Struct.fields[sel.index[0]];
+
+					if (!cg_elem_type_can_be_constant(f->type)) {
+						continue;
+					}
+
+					if (sel.index.count == 1) {
+						count += cg_global_const_calculate_region_count(fv->value->tav.value, f->type);
+					} else {
+						count += 1; // just in case
+						if (cg_is_nested_possibly_constant(type, sel, fv->value)) {
+							Type *cv_type = sel.entity->type;
+							count += cg_global_const_calculate_region_count(fv->value->tav.value, cv_type);
+						}
+					}
+				}
+			} else {
+				for_array(i, cl->elems) {
+					i64 field_index = i;
+					Ast *elem = cl->elems[i];
+					TypeAndValue tav = elem->tav;
+					Entity *f = bt->Struct.fields[field_index];
+					if (!cg_elem_type_can_be_constant(f->type)) {
+						continue;
+					}
+
+					ExactValue value = {};
+					if (tav.mode != Addressing_Invalid) {
+						value = tav.value;
+					}
+					count += cg_global_const_calculate_region_count(value, type);
+				}
+			}
+			break;
+		case Type_Array:
+		case Type_EnumeratedArray:
+		case Type_SimdVector: {
+			Type *et = base_array_type(bt);
+			if (!cg_elem_type_can_be_constant(et)) {
+				break;
+			}
+			for (Ast *elem : cl->elems) {
+				if (elem->kind == Ast_FieldValue) {
+					ast_node(fv, FieldValue, elem);
+					ExactValue const &value = elem->FieldValue.value->tav.value;
+					if (is_ast_range(fv->field)) {
+						ast_node(ie, BinaryExpr, fv->field);
+						TypeAndValue lo_tav = ie->left->tav;
+						TypeAndValue hi_tav = ie->right->tav;
+						GB_ASSERT(lo_tav.mode == Addressing_Constant);
+						GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+						TokenKind op = ie->op.kind;
+						i64 lo = exact_value_to_i64(lo_tav.value);
+						i64 hi = exact_value_to_i64(hi_tav.value);
+						if (op != Token_RangeHalf) {
+							hi += 1;
+						}
+
+						for (i64 i = lo; i < hi; i++) {
+							count += cg_global_const_calculate_region_count(value, et);
+						}
+					} else {
+						count += cg_global_const_calculate_region_count(value, et);
+					}
+				} else {
+					ExactValue const &value = elem->tav.value;
+					count += cg_global_const_calculate_region_count(value, et);
+				}
+			}
+		} break;
+
+		case Type_BitSet:
+			return 1;
+		case Type_Matrix:
+			return 1;
+
+		case Type_Slice:
+			return 3;
+
+		default:
+			GB_PANIC("TODO(bill): %s", type_to_string(type));
+			break;
+		}
+	}break;
+	}
+	return count;
+}
+
+gb_internal TB_Global *cg_global_const_comp_literal(cgModule *m, Type *type, ExactValue const &value, TB_Global *global, i64 base_offset);
+
+gb_internal bool cg_global_const_add_region(cgModule *m, ExactValue const &value, Type *type, TB_Global *global, i64 offset) {
+	GB_ASSERT(is_type_endian_little(type));
+	GB_ASSERT(!is_type_different_to_arch_endianness(type));
+
+	GB_ASSERT(global != nullptr);
+
+	Type *bt = base_type(type);
+	i64 size = type_size_of(type);
+	if (value.kind == ExactValue_Invalid) {
+		return false;
+	}
+	if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
+		if (is_type_rune_array(type)) {
+			i64 count = type->Array.count;
+			Rune rune;
+			isize rune_offset = 0;
+			isize width = 1;
+			String s = value.value_string;
+
+			Rune *runes = cast(Rune *)tb_global_add_region(m->mod, global, offset, count*4);
+
+			for (i64 i = 0; i < count && rune_offset < s.len; i++) {
+				width = utf8_decode(s.text+rune_offset, s.len-rune_offset, &rune);
+				runes[i] = rune;
+				rune_offset += width;
+
+			}
+			GB_ASSERT(offset == s.len);
+			return true;
+		}
+		Type *et = bt->Array.elem;
+		i64 elem_size = type_size_of(et);
+
+		for (i64 i = 0; i < bt->Array.count; i++) {
+			cg_global_const_add_region(m, value, et, global, offset+(i * elem_size));
+		}
+		return true;
+	} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
+		u8 *dst = cast(u8 *)tb_global_add_region(m->mod, global, offset, size);
+		gb_memcopy(dst, value.value_string.text, gb_min(value.value_string.len, size));
+		return true;
+	} else if (is_type_array(type) &&
+		value.kind != ExactValue_Invalid &&
+		value.kind != ExactValue_String &&
+		value.kind != ExactValue_Compound) {
+
+		Type *et = bt->Array.elem;
+		i64 elem_size = type_size_of(et);
+
+		for (i64 i = 0; i < bt->Array.count; i++) {
+			cg_global_const_add_region(m, value, et, global, offset+(i * elem_size));
+		}
+
+		return true;
+	} else if (is_type_matrix(type) &&
+		value.kind != ExactValue_Invalid &&
+		value.kind != ExactValue_Compound) {
+		GB_PANIC("TODO(bill): matrices");
+
+		i64 row = bt->Matrix.row_count;
+		i64 column = bt->Matrix.column_count;
+		GB_ASSERT(row == column);
+
+		Type *elem = bt->Matrix.elem;
+
+		i64 elem_size = type_size_of(elem);
+		gb_unused(elem_size);
+
+		// 1 region in memory, not many
+
+		return true;
+	} else if (is_type_simd_vector(type) &&
+		value.kind != ExactValue_Invalid &&
+		value.kind != ExactValue_Compound) {
+
+		GB_PANIC("TODO(bill): #simd vectors");
+
+		Type *et = type->SimdVector.elem;
+		i64 elem_size = type_size_of(et);
+		gb_unused(elem_size);
+
+		// 1 region in memory, not many
+
+		return true;
+	}
+
+
+	switch (value.kind) {
+	case ExactValue_Bool:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			bool *res = cast(bool *)tb_global_add_region(m->mod, global, offset, size);
+			*res = !!value.value_bool;
+		}
+		break;
+
+	case ExactValue_Integer:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			void *res = tb_global_add_region(m->mod, global, offset, size);
+			cg_write_big_int_at_ptr(res, &value.value_integer, type);
+		}
+		break;
+
+	case ExactValue_Float:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			f64 f = exact_value_to_f64(value);
+			void *res = tb_global_add_region(m->mod, global, offset, size);
+			switch (size) {
+			case 2: *(u16 *)res = f32_to_f16(cast(f32)f); break;
+			case 4: *(f32 *)res = cast(f32)f;             break;
+			case 8: *(f64 *)res = cast(f64)f;             break;
+			}
+		}
+		break;
+
+	case ExactValue_Pointer:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			void *res = tb_global_add_region(m->mod, global, offset, size);
+			*(u64 *)res = exact_value_to_u64(value);
+		}
+		break;
+
+	case ExactValue_String:
+		if (is_type_array_like(type)) {
+			GB_ASSERT(global != nullptr);
+			void *data = tb_global_add_region(m->mod, global, offset, size);
+			gb_memcopy(data, value.value_string.text, gb_min(value.value_string.len, size));
+		} else {
+			cg_global_const_string(m, value.value_string, type, global, offset);
+		}
+		break;
+
+	case ExactValue_Typeid:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			void *dst = tb_global_add_region(m->mod, global, offset, size);
+			u64 id = cg_typeid_as_u64(m, value.value_typeid);
+			cg_write_uint_at_ptr(dst, id, t_typeid);
+		}
+		break;
+
+	case ExactValue_Compound:
+		{
+			TB_Global *out_global = cg_global_const_comp_literal(m, type, value, global, offset);
+			GB_ASSERT(out_global == global);
+		}
+		break;
+
+	case ExactValue_Procedure:
+		GB_PANIC("TODO(bill): nested procedure values/literals\n");
+		break;
+	case ExactValue_Complex:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			Complex128 c = {};
+			if (value.value_complex) {
+				c = *value.value_complex;
+			}
+			void *res = tb_global_add_region(m->mod, global, offset, size);
+			switch (size) {
+			case 4:
+				((u16 *)res)[0] = f32_to_f16(cast(f32)c.real);
+				((u16 *)res)[1] = f32_to_f16(cast(f32)c.imag);
+				break;
+			case 8:
+				((f32 *)res)[0] = cast(f32)c.real;
+				((f32 *)res)[1] = cast(f32)c.imag;
+				break;
+			case 16:
+				((f64 *)res)[0] = cast(f64)c.real;
+				((f64 *)res)[1] = cast(f64)c.imag;
+				break;
+			}
+		}
+		break;
+	case ExactValue_Quaternion:
+		{
+			GB_ASSERT_MSG(!is_type_array_like(bt), "%s", type_to_string(type));
+			// @QuaternionLayout
+			Quaternion256 q = {};
+			if (value.value_quaternion) {
+				q = *value.value_quaternion;
+			}
+			void *res = tb_global_add_region(m->mod, global, offset, size);
+			switch (size) {
+			case 8:
+				((u16 *)res)[0] = f32_to_f16(cast(f32)q.imag);
+				((u16 *)res)[1] = f32_to_f16(cast(f32)q.jmag);
+				((u16 *)res)[2] = f32_to_f16(cast(f32)q.kmag);
+				((u16 *)res)[3] = f32_to_f16(cast(f32)q.real);
+				break;
+			case 16:
+				((f32 *)res)[0] = cast(f32)q.imag;
+				((f32 *)res)[1] = cast(f32)q.jmag;
+				((f32 *)res)[2] = cast(f32)q.kmag;
+				((f32 *)res)[3] = cast(f32)q.real;
+				break;
+			case 32:
+				((f64 *)res)[0] = cast(f64)q.imag;
+				((f64 *)res)[1] = cast(f64)q.jmag;
+				((f64 *)res)[2] = cast(f64)q.kmag;
+				((f64 *)res)[3] = cast(f64)q.real;
+				break;
+			}
+		}
+		break;
+	default:
+		GB_PANIC("%s", type_to_string(type));
+		break;
+	}
+	return true;
+}
+
+
+gb_internal TB_Global *cg_global_const_comp_literal(cgModule *m, Type *original_type, ExactValue const &value, TB_Global *global, i64 base_offset) {
+	GB_ASSERT(value.kind == ExactValue_Compound);
+	Ast *value_compound = value.value_compound;
+	ast_node(cl, CompoundLit, value_compound);
+
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	if (global == nullptr) {
+		char name[32] = {};
+		gb_snprintf(name, 31, "complit$%u", 1+m->const_nil_guid.fetch_add(1));
+		global = tb_global_create(m->mod, -1, name, cg_debug_type(m, original_type), TB_LINKAGE_PRIVATE);
+		i64 size = type_size_of(original_type);
+		i64 align = type_align_of(original_type);
+
+		// READ ONLY?
+		TB_ModuleSection *section = nullptr;
+		if (is_type_string(original_type) || is_type_cstring(original_type)) {
+			section = tb_module_get_rdata(m->mod);
+		} else {
+			section = tb_module_get_data(m->mod);
+		}
+
+		if (cl->elems.count == 0) {
+			tb_global_set_storage(m->mod, section, global, size, align, 0);
+			return global;
+		}
+
+
+		isize global_region_count = cg_global_const_calculate_region_count(value, original_type);
+		tb_global_set_storage(m->mod, section, global, size, align, global_region_count);
+	}
+
+	if (cl->elems.count == 0) {
+		return global;
+	}
+
+
+	Type *bt = base_type(original_type);
+	i64 bt_size = type_size_of(bt);
+
+	switch (bt->kind) {
+	case Type_Struct:
+		if (cl->elems[0]->kind == Ast_FieldValue) {
+			isize elem_count = cl->elems.count;
+			for (isize i = 0; i < elem_count; i++) {
+				ast_node(fv, FieldValue, cl->elems[i]);
+				String name = fv->field->Ident.token.string;
+
+				TypeAndValue tav = fv->value->tav;
+				GB_ASSERT(tav.mode != Addressing_Invalid);
+				ExactValue value = tav.value;
+
+				Selection sel = lookup_field(bt, name, false);
+				GB_ASSERT(!sel.indirect);
+
+				if (!cg_is_nested_possibly_constant(bt, sel, fv->value)) {
+					continue;
+				}
+
+				i64 offset = type_offset_of_from_selection(bt, sel);
+				cg_global_const_add_region(m, value, sel.entity->type, global, base_offset+offset);
+			}
+		} else {
+			for_array(i, cl->elems) {
+				i64 field_index = i;
+				Ast *elem = cl->elems[i];
+				TypeAndValue tav = elem->tav;
+				Entity *f = bt->Struct.fields[field_index];
+				if (!cg_elem_type_can_be_constant(f->type)) {
+					continue;
+				}
+
+				i64 offset = bt->Struct.offsets[field_index];
+
+				ExactValue value = {};
+				if (tav.mode != Addressing_Invalid) {
+					value = tav.value;
+				}
+				cg_global_const_add_region(m, value, f->type, global, base_offset+offset);
+			}
+		}
+		return global;
+
+	case Type_Array:
+	case Type_EnumeratedArray:
+	case Type_SimdVector:
+		if (cl->elems[0]->kind == Ast_FieldValue) {
+			Type *et = base_array_type(bt);
+			i64 elem_size = type_size_of(et);
+			for (Ast *elem : cl->elems) {
+				ast_node(fv, FieldValue, elem);
+
+				ExactValue const &value = fv->value->tav.value;
+
+				if (is_ast_range(fv->field)) {
+					ast_node(ie, BinaryExpr, fv->field);
+					TypeAndValue lo_tav = ie->left->tav;
+					TypeAndValue hi_tav = ie->right->tav;
+					GB_ASSERT(lo_tav.mode == Addressing_Constant);
+					GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+					TokenKind op = ie->op.kind;
+					i64 lo = exact_value_to_i64(lo_tav.value);
+					i64 hi = exact_value_to_i64(hi_tav.value);
+					if (op != Token_RangeHalf) {
+						hi += 1;
+					}
+
+					for (i64 i = lo; i < hi; i++) {
+						i64 offset = i * elem_size;
+						cg_global_const_add_region(m, value, et, global, base_offset+offset);
+					}
+				} else {
+					TypeAndValue index_tav = fv->field->tav;
+					GB_ASSERT(index_tav.mode == Addressing_Constant);
+					i64 i = exact_value_to_i64(index_tav.value);
+					i64 offset = i * elem_size;
+					cg_global_const_add_region(m, value, et, global, base_offset+offset);
+				}
+			}
+		} else {
+			Type *et = base_array_type(bt);
+			i64 elem_size = type_size_of(et);
+			i64 offset = 0;
+			for (Ast *elem : cl->elems) {
+				ExactValue const &value = elem->tav.value;
+				cg_global_const_add_region(m, value, et, global, base_offset+offset);
+				offset += elem_size;
+			}
+		}
+
+		return global;
+
+	case Type_BitSet:
+		if (bt_size > 0) {
+			BigInt bits = {};
+			BigInt one = {};
+			big_int_from_u64(&one, 1);
+
+			for_array(i, cl->elems) {
+				Ast *e = cl->elems[i];
+				GB_ASSERT(e->kind != Ast_FieldValue);
+
+				TypeAndValue tav = e->tav;
+				if (tav.mode != Addressing_Constant) {
+					continue;
+				}
+				GB_ASSERT(tav.value.kind == ExactValue_Integer);
+				i64 v = big_int_to_i64(&tav.value.value_integer);
+				i64 lower = bt->BitSet.lower;
+				u64 index = cast(u64)(v-lower);
+				BigInt bit = {};
+				big_int_from_u64(&bit, index);
+				big_int_shl(&bit, &one, &bit);
+				big_int_or(&bits, &bits, &bit);
+			}
+
+			void *dst = tb_global_add_region(m->mod, global, base_offset, bt_size);
+			cg_write_big_int_at_ptr(dst, &bits, original_type);
+		}
+		return global;
+
+	case Type_Matrix:
+		GB_PANIC("TODO(bill): constant compound literal for %s", type_to_string(original_type));
+		break;
+
+	case Type_Slice:
+		{
+			i64 count = gb_max(cl->elems.count, cl->max_count);
+			Type *elem = bt->Slice.elem;
+			Type *t = alloc_type_array(elem, count);
+			TB_Global *backing_array = cg_global_const_comp_literal(m, t, value, nullptr, 0);
+
+			tb_global_add_symbol_reloc(m->mod, global, base_offset+0, cast(TB_Symbol *)backing_array);
+
+			void *len_ptr = tb_global_add_region(m->mod, global, base_offset+build_context.int_size, build_context.int_size);
+			cg_write_int_at_ptr(len_ptr, count, t_int);
+		}
+		return global;
+	}
+
+	GB_PANIC("TODO(bill): constant compound literal for %s", type_to_string(original_type));
+	return nullptr;
+}
+
+
+gb_internal cgValue cg_const_value(cgProcedure *p, Type *type, ExactValue const &value) {
+	GB_ASSERT(p != nullptr);
+	TB_Node *node = nullptr;
+
+	if (is_type_untyped(type)) {
+		// TODO(bill): THIS IS A COMPLETE HACK, WHY DOES THIS NOT A TYPE?
+		GB_ASSERT(type->kind == Type_Basic);
+		switch (type->Basic.kind) {
+		case Basic_UntypedBool:
+			type = t_bool;
+			break;
+		case Basic_UntypedInteger:
+			type = t_i64;
+			break;
+		case Basic_UntypedFloat:
+			type = t_f64;
+			break;
+		case Basic_UntypedComplex:
+			type = t_complex128;
+			break;
+		case Basic_UntypedQuaternion:
+			type = t_quaternion256;
+			break;
+		case Basic_UntypedString:
+			type = t_string;
+			break;
+		case Basic_UntypedRune:
+			type = t_rune;
+			break;
+		case Basic_UntypedNil:
+		case Basic_UntypedUninit:
+			return cg_value(cast(TB_Node *)nullptr, type);
+		}
+	}
+	TB_DataType dt = cg_data_type(type);
+
+	switch (value.kind) {
+	case ExactValue_Invalid:
+		return cg_const_nil(p, type);
+
+	case ExactValue_Typeid:
+		return cg_typeid(p, value.value_typeid);
+
+	case ExactValue_Procedure:
+		{
+			Ast *expr = unparen_expr(value.value_procedure);
+			if (expr->kind == Ast_ProcLit) {
+				cgProcedure *anon = cg_procedure_generate_anonymous(p->module, expr, p);
+				TB_Node *ptr = tb_inst_get_symbol_address(p->func, anon->symbol);
+				GB_ASSERT(are_types_identical(type, anon->type));
+				return cg_value(ptr, type);
+			}
+
+			Entity *e = entity_of_node(expr);
+			if (e != nullptr) {
+				TB_Symbol *found = cg_find_symbol_from_entity(p->module, e);
+				GB_ASSERT_MSG(found != nullptr, "could not find '%.*s'", LIT(e->token.string));
+				TB_Node *ptr = tb_inst_get_symbol_address(p->func, found);
+				GB_ASSERT(type != nullptr);
+				GB_ASSERT(are_types_identical(type, e->type));
+				return cg_value(ptr, type);
+			}
+
+			GB_PANIC("TODO(bill): cg_const_value ExactValue_Procedure %s", expr_to_string(expr));
+		}
+		break;
+	}
+
+	switch (value.kind) {
+	case ExactValue_Bool:
+		GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+		return cg_value(tb_inst_uint(p->func, dt, value.value_bool), type);
+
+	case ExactValue_Integer:
+		GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+		// GB_ASSERT(dt.raw != TB_TYPE_I128.raw);
+		if (is_type_unsigned(type)) {
+			u64 i = exact_value_to_u64(value);
+			return cg_value(tb_inst_uint(p->func, dt, i), type);
+		} else {
+			i64 i = exact_value_to_i64(value);
+			return cg_value(tb_inst_sint(p->func, dt, i), type);
+		}
+		break;
+
+	case ExactValue_Float:
+		GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+		GB_ASSERT(dt.raw != TB_TYPE_F16.raw);
+		GB_ASSERT(!is_type_different_to_arch_endianness(type));
+		{
+			f64 f = exact_value_to_f64(value);
+			if (type_size_of(type) == 8) {
+				return cg_value(tb_inst_float64(p->func, f), type);
+			} else {
+				return cg_value(tb_inst_float32(p->func, cast(f32)f), type);
+			}
+		}
+		break;
+
+	case ExactValue_String:
+		{
+			GB_ASSERT(is_type_string(type));
+			cgModule *m = p->module;
+
+			String str = value.value_string;
+
+			char name[32] = {};
+			gb_snprintf(name, 31, "csb$%u", 1+m->const_nil_guid.fetch_add(1));
+			TB_Global *cstr_global = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+
+			i64 size = str.len+1;
+			tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), cstr_global, size, 1, 1);
+			u8 *data = cast(u8 *)tb_global_add_region(m->mod, cstr_global, 0, size);
+			gb_memcopy(data, str.text, str.len);
+			data[str.len] = 0;
+
+			if (is_type_cstring(type)) {
+				cgValue s = cg_value(cstr_global, type);
+				return cg_flatten_value(p, s);
+			}
+
+			gb_snprintf(name, 31, "str$%u", 1+m->const_nil_guid.fetch_add(1));
+			TB_Global *str_global = tb_global_create(m->mod, -1, name, cg_debug_type(m, type), TB_LINKAGE_PRIVATE);
+			tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), str_global, type_size_of(type), type_align_of(type), 2);
+
+			tb_global_add_symbol_reloc(m->mod, str_global, 0, cast(TB_Symbol *)cstr_global);
+			void *len_ptr = tb_global_add_region(m->mod, str_global, build_context.int_size, build_context.int_size);
+			cg_write_int_at_ptr(len_ptr, str.len, t_int);
+
+			TB_Node *s = tb_inst_get_symbol_address(p->func, cast(TB_Symbol *)str_global);
+			return cg_lvalue_addr(s, type);
+
+		}
+
+	case ExactValue_Pointer:
+		return cg_value(tb_inst_uint(p->func, dt, exact_value_to_u64(value)), type);
+
+	case ExactValue_Compound:
+		{
+			TB_Symbol *symbol = cast(TB_Symbol *)cg_global_const_comp_literal(p->module, type, value, nullptr, 0);
+			TB_Node *node = tb_inst_get_symbol_address(p->func, symbol);
+			return cg_lvalue_addr(node, type);
+		}
+		break;
+	}
+
+
+	GB_ASSERT(node != nullptr);
+	return cg_value(node, type);
+}
+
+gb_internal cgValue cg_const_int(cgProcedure *p, Type *type, i64 i) {
+	return cg_const_value(p, type, exact_value_i64(i));
+}
+gb_internal cgValue cg_const_bool(cgProcedure *p, Type *type, bool v) {
+	return cg_value(tb_inst_bool(p->func, v), type);
+}
+
+gb_internal cgValue cg_const_string(cgProcedure *p, Type *type, String const &str) {
+	return cg_const_value(p, type, exact_value_string(str));
+}
+
+gb_internal cgValue cg_const_union_tag(cgProcedure *p, Type *u, Type *v) {
+	return cg_const_value(p, union_tag_type(u), exact_value_i64(union_variant_index(u, v)));
+}
+

+ 482 - 0
src/tilde_debug.cpp

@@ -0,0 +1,482 @@
+gb_internal TB_DebugType *cg_debug_type_internal(cgModule *m, Type *type);
+gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type) {
+	type = reduce_tuple_to_single_type(type);
+
+	mutex_lock(&m->debug_type_mutex);
+	defer (mutex_unlock(&m->debug_type_mutex));
+	TB_DebugType **found = map_get(&m->debug_type_map, type);
+	if (found) {
+		return *found;
+	}
+
+	TB_DebugType *res = cg_debug_type_internal(m, type);
+	map_set(&m->debug_type_map, type, res);
+	return res;
+}
+
+gb_internal TB_DebugType *cg_debug_type_for_proc(cgModule *m, Type *type) {
+	GB_ASSERT(is_type_proc(type));
+	TB_DebugType **func_found = nullptr;
+	TB_DebugType *func_ptr = cg_debug_type(m, type);
+	GB_ASSERT(func_ptr != nullptr);
+
+	mutex_lock(&m->debug_type_mutex);
+	func_found = map_get(&m->proc_debug_type_map, type);
+	mutex_unlock(&m->debug_type_mutex);
+	GB_ASSERT(func_found != nullptr);
+	return *func_found;
+}
+
+
+gb_internal TB_DebugType *cg_debug_type_internal_record(cgModule *m, Type *type, String const &record_name) {
+	Type *bt = base_type(type);
+	switch (bt->kind) {
+	case Type_Struct:
+		{
+			type_set_offsets(bt);
+
+			TB_DebugType *record = nullptr;
+			if (bt->Struct.is_raw_union) {
+				record = tb_debug_create_union(m->mod, record_name.len, cast(char const *)record_name.text);
+			} else {
+				record = tb_debug_create_struct(m->mod, record_name.len, cast(char const *)record_name.text);
+			}
+			if (record_name.len != 0) {
+				map_set(&m->debug_type_map, type, record);
+			}
+
+			TB_DebugType **fields = tb_debug_record_begin(record, bt->Struct.fields.count);
+			for_array(i, bt->Struct.fields) {
+				Entity *e = bt->Struct.fields[i];
+				Type *type = e->type;
+				if (is_type_proc(type)) {
+					type = t_rawptr;
+				}
+				TB_DebugType *field_type = cg_debug_type(m, type);
+				String        name       = e->token.string;
+				TB_CharUnits  offset     = cast(TB_CharUnits)bt->Struct.offsets[i];
+				if (name.len == 0) {
+					name = str_lit("_");
+				}
+
+				fields[i] = tb_debug_create_field(m->mod, field_type, name.len, cast(char const *)name.text, offset);
+			}
+			tb_debug_record_end(
+				record,
+				cast(TB_CharUnits)type_size_of(type),
+				cast(TB_CharUnits)type_align_of(type)
+			);
+			return record;
+		}
+		break;
+
+	case Type_Tuple:
+		{
+			GB_ASSERT(record_name.len == 0);
+			type_set_offsets(bt);
+
+			TB_DebugType *record = tb_debug_create_struct(m->mod, 0, "");
+			isize record_count = 0;
+			for (Entity *e : bt->Tuple.variables) {
+				if (e->kind == Entity_Variable) {
+					record_count += 1;
+				}
+			}
+			TB_DebugType **fields = tb_debug_record_begin(record, record_count);
+			for_array(i, bt->Tuple.variables) {
+				Entity *e = bt->Tuple.variables[i];
+				if (e->kind != Entity_Variable) {
+					continue;
+				}
+				Type *type = e->type;
+				if (is_type_proc(type)) {
+					type = t_rawptr;
+				}
+				TB_DebugType *field_type = cg_debug_type(m, type);
+				String        name       = e->token.string;
+				TB_CharUnits  offset     = cast(TB_CharUnits)bt->Tuple.offsets[i];
+				if (name.len == 0) {
+					name = str_lit("_");
+				}
+
+				fields[i] = tb_debug_create_field(m->mod, field_type, name.len, cast(char const *)name.text, offset);
+			}
+			tb_debug_record_end(
+				record,
+				cast(TB_CharUnits)type_size_of(type),
+				cast(TB_CharUnits)type_align_of(type)
+			);
+			return record;
+		}
+		break;
+	case Type_Union:
+		{
+			TB_DebugType *record = tb_debug_create_struct(m->mod, record_name.len, cast(char const *)record_name.text);
+			if (record_name.len != 0) {
+				map_set(&m->debug_type_map, type, record);
+			}
+
+			i64 variant_count = bt->Union.variants.count;
+			if (is_type_union_maybe_pointer(bt)) {
+				// NO TAG
+				GB_ASSERT(variant_count == 1);
+				TB_DebugType **fields = tb_debug_record_begin(record, variant_count);
+				TB_DebugType *variant_type = cg_debug_type(m, bt->Union.variants[0]);
+				fields[0] = tb_debug_create_field(m->mod, variant_type, -1, "v0", 0);
+				tb_debug_record_end(
+					record,
+					cast(TB_CharUnits)type_size_of(type),
+					cast(TB_CharUnits)type_align_of(type)
+				);
+			} else {
+				TB_DebugType **fields = tb_debug_record_begin(record, variant_count+1);
+				for_array(i, bt->Union.variants) {
+					Type *v = bt->Union.variants[i];
+					TB_DebugType *variant_type = cg_debug_type(m, v);
+					char name[32] = {};
+					u32 v_index = cast(u32)i;
+					if (bt->Union.kind != UnionType_no_nil) {
+						v_index += 1;
+					}
+					gb_snprintf(name, 31, "v%u", v_index);
+					fields[i] = tb_debug_create_field(m->mod, variant_type, -1, name, 0);
+				}
+
+				TB_DebugType *tag_type = cg_debug_type(m, union_tag_type(bt));
+				fields[variant_count] = tb_debug_create_field(m->mod, tag_type, -1, "tag", cast(TB_CharUnits)bt->Union.variant_block_size);
+
+			}
+			tb_debug_record_end(
+				record,
+				cast(TB_CharUnits)type_size_of(type),
+				cast(TB_CharUnits)type_align_of(type)
+			);
+			return record;
+		}
+		break;
+	}
+	return nullptr;
+}
+
+
+gb_internal TB_DebugType *cg_debug_type_internal(cgModule *m, Type *type) {
+	if (type == nullptr) {
+		return tb_debug_get_void(m->mod);
+	}
+	Type *original_type = type;
+	if (type->kind == Type_Named) {
+		String name = type->Named.name;
+		TB_DebugType *res = cg_debug_type_internal_record(m, type, name);
+		if (res) {
+			return res;
+		}
+		type = base_type(type->Named.base);
+	}
+
+	TB_CharUnits int_size = cast(TB_CharUnits)build_context.int_size;
+	TB_CharUnits ptr_size = cast(TB_CharUnits)build_context.ptr_size;
+	TB_CharUnits size  = cast(TB_CharUnits)type_size_of(type);
+	TB_CharUnits align = cast(TB_CharUnits)type_align_of(type);
+	int bits = cast(int)(8*size);
+	bool is_signed = is_type_integer(core_type(type)) && !is_type_unsigned(core_type(type));
+
+	switch (type->kind) {
+	case Type_Basic:
+		switch (type->Basic.kind) {
+		case Basic_bool:          return tb_debug_get_bool(m->mod);
+		case Basic_b8:            return tb_debug_get_bool(m->mod);
+		case Basic_b16:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_b32:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_b64:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i8:            return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u8:            return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i16:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u16:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i32:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u32:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i64:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u64:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i128:          return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u128:          return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_rune:          return tb_debug_get_integer(m->mod, is_signed, bits);
+
+		case Basic_f16:           return tb_debug_get_integer(m->mod, false, bits);
+		case Basic_f32:           return tb_debug_get_float(m->mod,   TB_FLT_32);
+		case Basic_f64:           return tb_debug_get_float(m->mod,   TB_FLT_64);
+
+		case Basic_complex32:
+		case Basic_complex64:
+		case Basic_complex128:
+			{
+				String name = basic_types[type->Basic.kind].Basic.name;
+				TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+				Type *et = base_complex_elem_type(type);
+				TB_CharUnits elem_size = cast(TB_CharUnits)type_size_of(et);
+				TB_DebugType *elem = cg_debug_type(m, et);
+
+				TB_DebugType **fields = tb_debug_record_begin(record, 2);
+				fields[0] = tb_debug_create_field(m->mod, elem, -1, "real", 0*elem_size);
+				fields[1] = tb_debug_create_field(m->mod, elem, -1, "imag", 1*elem_size);
+
+				tb_debug_record_end(record, size, align);
+				return record;
+			}
+		case Basic_quaternion64:
+		case Basic_quaternion128:
+		case Basic_quaternion256:
+			{
+				String name = basic_types[type->Basic.kind].Basic.name;
+				TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+				Type *et = base_complex_elem_type(type);
+				TB_CharUnits elem_size = cast(TB_CharUnits)type_size_of(et);
+				TB_DebugType *elem = cg_debug_type(m, et);
+
+				// @QuaternionLayout
+				TB_DebugType **fields = tb_debug_record_begin(record, 4);
+				fields[0] = tb_debug_create_field(m->mod, elem, -1, "imag", 0*elem_size);
+				fields[1] = tb_debug_create_field(m->mod, elem, -1, "jmag", 1*elem_size);
+				fields[2] = tb_debug_create_field(m->mod, elem, -1, "kmag", 2*elem_size);
+				fields[3] = tb_debug_create_field(m->mod, elem, -1, "real", 3*elem_size);
+
+				tb_debug_record_end(record, size, align);
+				return record;
+			}
+
+		case Basic_int:           return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_uint:          return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_uintptr:       return tb_debug_get_integer(m->mod, is_signed, bits);
+
+		case Basic_rawptr:
+			return tb_debug_create_ptr(m->mod, tb_debug_get_void(m->mod));
+		case Basic_string:
+			{
+				String name = basic_types[type->Basic.kind].Basic.name;
+				TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+				// @QuaternionLayout
+				TB_DebugType **fields = tb_debug_record_begin(record, 2);
+				fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, t_u8_ptr), -1, "data", 0*int_size);
+				fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int),    -1, "len",  1*int_size);
+
+				tb_debug_record_end(record, size, align);
+				return record;
+			}
+		case Basic_cstring:
+			return tb_debug_create_ptr(m->mod, tb_debug_get_integer(m->mod, false, 8));
+
+		case Basic_any:
+			{
+				String name = basic_types[type->Basic.kind].Basic.name;
+				TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+				// @QuaternionLayout
+				TB_DebugType **fields = tb_debug_record_begin(record, 2);
+				fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, t_rawptr), -1, "data", 0*ptr_size);
+				fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_typeid), -1, "id",   1*ptr_size);
+
+				tb_debug_record_end(record, size, align);
+				return record;
+			}
+		case Basic_typeid: return tb_debug_get_integer(m->mod, false, bits);
+
+		case Basic_i16le:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u16le:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i32le:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u32le:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i64le:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u64le:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i128le:        return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u128le:        return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i16be:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u16be:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i32be:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u32be:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i64be:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u64be:         return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_i128be:        return tb_debug_get_integer(m->mod, is_signed, bits);
+		case Basic_u128be:        return tb_debug_get_integer(m->mod, is_signed, bits);
+
+		case Basic_f16le:         return tb_debug_get_integer(m->mod, false, bits);
+		case Basic_f32le:         return tb_debug_get_float(m->mod,   TB_FLT_32);
+		case Basic_f64le:         return tb_debug_get_float(m->mod,   TB_FLT_64);
+		case Basic_f16be:         return tb_debug_get_integer(m->mod, false, bits);
+		case Basic_f32be:         return tb_debug_get_float(m->mod,   TB_FLT_32);
+		case Basic_f64be:         return tb_debug_get_float(m->mod,   TB_FLT_64);
+		}
+		break;
+	case Type_Generic:
+		GB_PANIC("SHOULD NEVER HIT");
+		break;
+	case Type_Pointer:
+		return tb_debug_create_ptr(m->mod, cg_debug_type(m, type->Pointer.elem));
+	case Type_MultiPointer:
+		return tb_debug_create_ptr(m->mod, cg_debug_type(m, type->MultiPointer.elem));
+	case Type_Array:
+		return tb_debug_create_array(m->mod, cg_debug_type(m, type->Array.elem), type->Array.count);
+	case Type_EnumeratedArray:
+		return tb_debug_create_array(m->mod, cg_debug_type(m, type->EnumeratedArray.elem), type->EnumeratedArray.count);
+	case Type_Slice:
+		{
+			String name = {};
+			TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+			TB_DebugType **fields = tb_debug_record_begin(record, 2);
+			fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, alloc_type_pointer(type->Slice.elem)), -1, "data", 0*int_size);
+			fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int),    -1, "len",  1*int_size);
+
+			tb_debug_record_end(record, size, align);
+			return record;
+		}
+	case Type_DynamicArray:
+		{
+			String name = {};
+			TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+			TB_DebugType **fields = tb_debug_record_begin(record, 4);
+			fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, alloc_type_pointer(type->Slice.elem)), -1, "data", 0*int_size);
+			fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int),       -1, "len",        1*int_size);
+			fields[2] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int),       -1, "cap",        2*int_size);
+			fields[3] = tb_debug_create_field(m->mod, cg_debug_type(m, t_allocator), -1, "allocator",  3*int_size);
+
+			tb_debug_record_end(record, size, align);
+			return record;
+		}
+	case Type_Map:
+		return cg_debug_type(m, t_raw_map);
+
+	case Type_Struct:
+	case Type_Tuple:
+	case Type_Union:
+		return cg_debug_type_internal_record(m, type, {});
+
+	case Type_Enum:
+		return tb_debug_get_integer(m->mod, is_signed, bits);
+
+	case Type_Proc:
+		{
+			TypeProc *pt = &type->Proc;
+			isize param_count  = 0;
+			isize return_count = 0;
+
+			bool is_odin_cc = is_calling_convention_odin(pt->calling_convention);
+
+			if (pt->params) for (Entity *e : pt->params->Tuple.variables) {
+				if (e->kind == Entity_Variable) {
+					param_count += 1;
+				}
+			}
+
+			if (pt->result_count > 0) {
+				if (is_odin_cc) {
+					// Split returns
+					param_count += pt->result_count-1;
+					return_count = 1;
+				} else {
+					return_count = 1;
+				}
+			}
+
+			if (pt->calling_convention == ProcCC_Odin) {
+				// `context` ptr
+				param_count += 1;
+			}
+
+			TB_CallingConv tb_cc = TB_CDECL;
+			if (pt->calling_convention == ProcCC_StdCall) {
+				tb_cc = TB_STDCALL;
+			}
+			TB_DebugType *func = tb_debug_create_func(m->mod, tb_cc, param_count, return_count, pt->c_vararg);
+
+			map_set(&m->proc_debug_type_map, original_type, func);
+			map_set(&m->proc_debug_type_map, type, func);
+
+			TB_DebugType *func_ptr = tb_debug_create_ptr(m->mod, func);
+			map_set(&m->debug_type_map, original_type, func_ptr);
+			map_set(&m->debug_type_map, type, func_ptr);
+
+			TB_DebugType **params = tb_debug_func_params(func);
+			TB_DebugType **returns = tb_debug_func_returns(func);
+
+			isize param_index = 0;
+			isize return_index = 0;
+			if (pt->params) for (Entity *e : pt->params->Tuple.variables) {
+				if (e->kind == Entity_Variable) {
+					Type *type = e->type;
+					if (is_type_proc(type)) {
+						type = t_rawptr;
+					}
+					String name = e->token.string;
+					if (name.len == 0) {
+						name = str_lit("_");
+					}
+					params[param_index++] = tb_debug_create_field(m->mod, cg_debug_type(m, type), name.len, cast(char const *)name.text, 0);
+				}
+			}
+
+			if (pt->result_count) {
+				GB_ASSERT(pt->results);
+				if (is_odin_cc) {
+					// Split Returns
+					for (isize i = 0; i < pt->results->Tuple.variables.count-1; i++) {
+						Entity *e = pt->results->Tuple.variables[i];
+						GB_ASSERT(e->kind == Entity_Variable);
+						Type *type = e->type;
+						if (is_type_proc(e->type)) {
+							type = t_rawptr;
+						}
+						type = alloc_type_pointer(type);
+
+						String name = e->token.string;
+						if (name.len == 0) {
+							name = str_lit("_");
+						}
+						params[param_index++] = tb_debug_create_field(m->mod, cg_debug_type(m, type), name.len, cast(char const *)name.text, 0);
+					}
+
+					Type *last_type = pt->results->Tuple.variables[pt->results->Tuple.variables.count-1]->type;
+					if (is_type_proc(last_type)) {
+						last_type = t_rawptr;
+					}
+					returns[return_index++] = cg_debug_type(m, last_type);
+				} else {
+					returns[return_index++] = cg_debug_type(m, pt->results);
+				}
+			}
+
+			if (pt->calling_convention == ProcCC_Odin) {
+				Type *type = t_context_ptr;
+				String name = str_lit("__.context_ptr");
+				params[param_index++] = tb_debug_create_field(m->mod, cg_debug_type(m, type), name.len, cast(char const *)name.text, 0);
+			}
+
+			GB_ASSERT_MSG(param_index  == param_count,  "%td vs %td for %s", param_index,  param_count, type_to_string(type));
+			GB_ASSERT_MSG(return_index == return_count, "%td vs %td for %s", return_index, return_count, type_to_string(type));
+
+			return func_ptr;
+		}
+		break;
+	case Type_BitSet:
+		return cg_debug_type(m, bit_set_to_int(type));
+	case Type_SimdVector:
+		return tb_debug_create_array(m->mod, cg_debug_type(m, type->SimdVector.elem), type->SimdVector.count);
+	case Type_RelativePointer:
+		return cg_debug_type(m, type->RelativePointer.base_integer);
+	case Type_RelativeMultiPointer:
+		return cg_debug_type(m, type->RelativeMultiPointer.base_integer);
+	case Type_Matrix:
+		{
+			i64 count = matrix_type_total_internal_elems(type);
+			return tb_debug_create_array(m->mod, cg_debug_type(m, type->Matrix.elem), count);
+		}
+	case Type_SoaPointer:
+		{
+			String name = {};
+			TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
+			TB_DebugType **fields = tb_debug_record_begin(record, 2);
+			fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, alloc_type_pointer(type->SoaPointer.elem)), -1, "ptr", 0*int_size);
+			fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int), -1, "offset",  1*int_size);
+
+			tb_debug_record_end(record, size, align);
+			return record;
+		}
+	}
+
+	// TODO(bill): cg_debug_type
+	return tb_debug_get_void(m->mod);
+}

+ 3871 - 0
src/tilde_expr.cpp

@@ -0,0 +1,3871 @@
+gb_internal cgValue cg_flatten_value(cgProcedure *p, cgValue value) {
+	GB_ASSERT(value.kind != cgValue_Multi);
+	if (value.kind == cgValue_Symbol) {
+		GB_ASSERT(is_type_internally_pointer_like(value.type));
+		return cg_value(tb_inst_get_symbol_address(p->func, value.symbol), value.type);
+	} else if (value.kind == cgValue_Addr) {
+		// TODO(bill): Is this a good idea?
+		// this converts an lvalue to an rvalue if trivially possible
+		TB_DataType dt = cg_data_type(value.type);
+		if (!TB_IS_VOID_TYPE(dt)) {
+			TB_CharUnits align = cast(TB_CharUnits)type_align_of(value.type);
+			return cg_value(tb_inst_load(p->func, dt, value.node, align, false), value.type);
+		}
+	}
+	return value;
+}
+
+gb_internal cgValue cg_emit_select(cgProcedure *p, cgValue const &cond, cgValue const &x, cgValue const &y) {
+	GB_ASSERT(x.kind == y.kind);
+	GB_ASSERT(cond.kind == cgValue_Value);
+	cgValue res = x;
+	res.node = tb_inst_select(p->func, cond.node, x.node, y.node);
+	return res;
+}
+
+
+gb_internal bool cg_is_expr_untyped_const(Ast *expr) {
+	auto const &tv = type_and_value_of_expr(expr);
+	if (is_type_untyped(tv.type)) {
+		return tv.value.kind != ExactValue_Invalid;
+	}
+	return false;
+}
+gb_internal cgValue cg_expr_untyped_const_to_typed(cgProcedure *p, Ast *expr, Type *t) {
+	GB_ASSERT(is_type_typed(t));
+	auto const &tv = type_and_value_of_expr(expr);
+	return cg_const_value(p, t, tv.value);
+}
+
+gb_internal cgContextData *cg_push_context_onto_stack(cgProcedure *p, cgAddr ctx) {
+	ctx.kind = cgAddr_Context;
+	cgContextData *cd = array_add_and_get(&p->context_stack);
+	cd->ctx = ctx;
+	cd->scope_index = p->scope_index;
+	return cd;
+}
+
+gb_internal cgAddr cg_find_or_generate_context_ptr(cgProcedure *p) {
+	if (p->context_stack.count > 0) {
+		return p->context_stack[p->context_stack.count-1].ctx;
+	}
+
+	Type *pt = base_type(p->type);
+	GB_ASSERT(pt->kind == Type_Proc);
+	GB_ASSERT(pt->Proc.calling_convention != ProcCC_Odin);
+
+	cgAddr c = cg_add_local(p, t_context, nullptr, true);
+	tb_node_append_attrib(c.addr.node, tb_function_attrib_variable(p->func, -1, "context", cg_debug_type(p->module, t_context)));
+	c.kind = cgAddr_Context;
+	// lb_emit_init_context(p, c);
+	cg_push_context_onto_stack(p, c);
+	// lb_add_debug_context_variable(p, c);
+
+	return c;
+}
+
+gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e) {
+	e = strip_entity_wrapping(e);
+	GB_ASSERT(e != nullptr);
+
+	GB_ASSERT(e->token.string != "_");
+
+	if (e->kind == Entity_Procedure) {
+		return cg_find_procedure_value_from_entity(m, e);
+	}
+
+	cgValue *found = nullptr;
+	rw_mutex_shared_lock(&m->values_mutex);
+	found = map_get(&m->values, e);
+	rw_mutex_shared_unlock(&m->values_mutex);
+	if (found) {
+		return *found;
+	}
+
+	GB_PANIC("\n\tError in: %s, missing value '%.*s'\n", token_pos_to_string(e->token.pos), LIT(e->token.string));
+	return {};
+}
+
+gb_internal cgValue cg_get_using_variable(cgProcedure *p, Entity *e) {
+	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using);
+	String name = e->token.string;
+	Entity *parent = e->using_parent;
+	Selection sel = lookup_field(parent->type, name, false);
+	GB_ASSERT(sel.entity != nullptr);
+	cgValue *pv = map_get(&p->module->values, parent);
+
+	cgValue v = {};
+
+	if (pv == nullptr && parent->flags & EntityFlag_SoaPtrField) {
+		// NOTE(bill): using SOA value (probably from for-in statement)
+		GB_PANIC("TODO(bill): cg_get_soa_variable_addr");
+		// cgAddr parent_addr = cg_get_soa_variable_addr(p, parent);
+		// v = cg_addr_get_ptr(p, parent_addr);
+	} else if (pv != nullptr) {
+		v = *pv;
+	} else {
+		GB_ASSERT_MSG(e->using_expr != nullptr, "%.*s %.*s", LIT(e->token.string), LIT(name));
+		v = cg_build_addr_ptr(p, e->using_expr);
+	}
+	GB_ASSERT(v.node != nullptr);
+	GB_ASSERT_MSG(parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type));
+	cgValue ptr = cg_emit_deep_field_gep(p, v, sel);
+	// if (parent->scope) {
+	// 	if ((parent->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
+	// 		cg_add_debug_local_variable(p, ptr.value, e->type, e->token);
+	// 	}
+	// } else {
+	// 	cg_add_debug_local_variable(p, ptr.value, e->type, e->token);
+	// }
+	return ptr;
+}
+gb_internal cgAddr cg_build_addr_from_entity(cgProcedure *p, Entity *e, Ast *expr) {
+	GB_ASSERT(e != nullptr);
+	if (e->kind == Entity_Constant) {
+		Type *t = default_type(type_of_expr(expr));
+		cgValue v = cg_const_value(p, t, e->Constant.value);
+		GB_PANIC("TODO(bill): cg_add_global_generated");
+		// return cg_add_global_generated(p->module, t, v);
+		return {};
+	}
+
+	cgAddr *local_found = map_get(&p->variable_map, e);
+	if (local_found) {
+		return *local_found;
+	}
+
+	cgValue v = {};
+
+	cgModule *m = p->module;
+
+	rw_mutex_lock(&m->values_mutex);
+	cgValue *found = map_get(&m->values, e);
+	rw_mutex_unlock(&m->values_mutex);
+	if (found) {
+		v = *found;
+	} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Using) {
+		// NOTE(bill): Calculate the using variable every time
+		v = cg_get_using_variable(p, e);
+	} else if (e->flags & EntityFlag_SoaPtrField) {
+		GB_PANIC("TODO(bill): cg_get_soa_variable_addr");
+		// return cg_get_soa_variable_addr(p, e);
+	}
+
+
+	if (v.node == nullptr) {
+		cgValue v = cg_find_value_from_entity(m, e);
+		v = cg_flatten_value(p, v);
+		return cg_addr(v);
+	}
+
+	return cg_addr(v);
+}
+
+gb_internal cgValue cg_emit_union_tag_ptr(cgProcedure *p, cgValue const &parent_ptr) {
+	Type *t = parent_ptr.type;
+	Type *ut = base_type(type_deref(t));
+	GB_ASSERT_MSG(is_type_pointer(t), "%s", type_to_string(t));
+	GB_ASSERT_MSG(ut->kind == Type_Union, "%s", type_to_string(t));
+
+	GB_ASSERT(!is_type_union_maybe_pointer_original_alignment(ut));
+	GB_ASSERT(!is_type_union_maybe_pointer(ut));
+	GB_ASSERT(type_size_of(ut) > 0);
+
+	Type *tag_type = union_tag_type(ut);
+	i64 tag_offset = ut->Union.variant_block_size;
+
+	GB_ASSERT(parent_ptr.kind == cgValue_Value);
+	TB_Node *ptr = parent_ptr.node;
+	TB_Node *tag_ptr = tb_inst_member_access(p->func, ptr, tag_offset);
+	return cg_value(tag_ptr, alloc_type_pointer(tag_type));
+}
+
+
+
+gb_internal cgValue cg_correct_endianness(cgProcedure *p, cgValue value) {
+	Type *src = core_type(value.type);
+	GB_ASSERT(is_type_integer(src) || is_type_float(src));
+	if (is_type_different_to_arch_endianness(src)) {
+		GB_PANIC("TODO(bill): cg_correct_endianness");
+		// Type *platform_src_type = integer_endian_type_to_platform_type(src);
+		// value = cg_emit_byte_swap(p, value, platform_src_type);
+	}
+	return value;
+}
+
+gb_internal cgValue cg_emit_transmute(cgProcedure *p, cgValue value, Type *type) {
+	GB_ASSERT(type_size_of(value.type) == type_size_of(type));
+
+	value = cg_flatten_value(p, value);
+
+	if (are_types_identical(value.type, type)) {
+		return value;
+	}
+	if (are_types_identical(core_type(value.type), core_type(type))) {
+		value.type = type;
+		return value;
+	}
+
+	i64 src_align = type_align_of(value.type);
+	i64 dst_align = type_align_of(type);
+
+	if (dst_align > src_align) {
+		cgAddr local = cg_add_local(p, type, nullptr, false);
+		cgValue dst = local.addr;
+		dst.type = alloc_type_pointer(value.type);
+		cg_emit_store(p, dst, value);
+		return cg_addr_load(p, local);
+	}
+
+	TB_DataType dt = cg_data_type(type);
+	switch (value.kind) {
+	case cgValue_Value:
+		GB_ASSERT_MSG(!TB_IS_VOID_TYPE(dt), "%d %s -> %s", dt.type, type_to_string(value.type), type_to_string(type));
+		value.type = type;
+		if (value.node->dt.raw != dt.raw) {
+			switch (value.node->dt.type) {
+			case TB_INT:
+				switch (value.node->dt.type) {
+				case TB_INT:
+					break;
+				case TB_FLOAT:
+					value.node = tb_inst_bitcast(p->func, value.node, dt);
+					break;
+				case TB_PTR:
+					value.node = tb_inst_int2ptr(p->func, value.node);
+					break;
+				}
+				break;
+			case TB_FLOAT:
+				switch (value.node->dt.type) {
+				case TB_INT:
+					value.node = tb_inst_bitcast(p->func, value.node, dt);
+					break;
+				case TB_FLOAT:
+					break;
+				case TB_PTR:
+					value.node = tb_inst_bitcast(p->func, value.node, TB_TYPE_INTPTR);
+					value.node = tb_inst_int2ptr(p->func, value.node);
+					break;
+				}
+				break;
+			case TB_PTR:
+				switch (value.node->dt.type) {
+				case TB_INT:
+					value.node = tb_inst_ptr2int(p->func, value.node, dt);
+					break;
+				case TB_FLOAT:
+					value.node = tb_inst_ptr2int(p->func, value.node, TB_TYPE_INTPTR);
+					value.node = tb_inst_bitcast(p->func, value.node, dt);
+					break;
+				case TB_PTR:
+					break;
+				}
+				break;
+			}
+		}
+		return value;
+	case cgValue_Addr:
+		value.type = type;
+		return value;
+	case cgValue_Symbol:
+		GB_PANIC("should be handled above");
+		break;
+	case cgValue_Multi:
+		GB_PANIC("cannot transmute multiple values at once");
+		break;
+	}
+	return value;
+
+}
+gb_internal cgValue cg_emit_byte_swap(cgProcedure *p, cgValue value, Type *end_type) {
+	GB_ASSERT(type_size_of(value.type) == type_size_of(end_type));
+
+	if (type_size_of(value.type) < 2) {
+		return value;
+	}
+
+	if (is_type_float(value.type)) {
+		i64 sz = type_size_of(value.type);
+		Type *integer_type = nullptr;
+		switch (sz) {
+		case 2: integer_type = t_u16; break;
+		case 4: integer_type = t_u32; break;
+		case 8: integer_type = t_u64; break;
+		}
+		GB_ASSERT(integer_type != nullptr);
+		value = cg_emit_transmute(p, value, integer_type);
+	}
+
+	GB_ASSERT(value.kind == cgValue_Value);
+
+	// TODO(bill): bswap
+	// value.node = tb_inst_bswap(p->func, value.node);
+	return cg_emit_transmute(p, value, end_type);
+}
+
+gb_internal cgValue cg_emit_comp_records(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right, Type *type) {
+	GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type));
+	cgValue left_ptr  = cg_address_from_load_or_generate_local(p, left);
+	cgValue right_ptr = cg_address_from_load_or_generate_local(p, right);
+	cgValue res = {};
+	if (type_size_of(type) == 0) {
+		switch (op_kind) {
+		case Token_CmpEq:
+			return cg_const_bool(p, t_bool, true);
+		case Token_NotEq:
+			return cg_const_bool(p, t_bool, false);
+		}
+		GB_PANIC("invalid operator");
+	}
+	TEMPORARY_ALLOCATOR_GUARD();
+	if (is_type_simple_compare(type)) {
+		// TODO(bill): Test to see if this is actually faster!!!!
+		auto args = slice_make<cgValue>(temporary_allocator(), 3);
+		args[0] = cg_emit_conv(p, left_ptr, t_rawptr);
+		args[1] = cg_emit_conv(p, right_ptr, t_rawptr);
+		args[2] = cg_const_int(p, t_int, type_size_of(type));
+		res = cg_emit_runtime_call(p, "memory_equal", args);
+	} else {
+		cgProcedure *equal_proc = cg_equal_proc_for_type(p->module, type);
+		cgValue value = cg_value(tb_inst_get_symbol_address(p->func, equal_proc->symbol), equal_proc->type);
+		auto args = slice_make<cgValue>(temporary_allocator(), 2);
+		args[0] = cg_emit_conv(p, left_ptr, t_rawptr);
+		args[1] = cg_emit_conv(p, right_ptr, t_rawptr);
+		res = cg_emit_call(p, value, args);
+	}
+	if (op_kind == Token_NotEq) {
+		res = cg_emit_unary_arith(p, Token_Not, res, res.type);
+	}
+	return res;
+}
+
+gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right) {
+	GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1));
+
+	Type *a = core_type(left.type);
+	Type *b = core_type(right.type);
+
+	cgValue nil_check = {};
+	if (is_type_array_like(left.type) || is_type_array_like(right.type)) {
+		// don't do `nil` check if it is array-like
+	} else if (is_type_untyped_nil(left.type)) {
+		nil_check = cg_emit_comp_against_nil(p, op_kind, right);
+	} else if (is_type_untyped_nil(right.type)) {
+		nil_check = cg_emit_comp_against_nil(p, op_kind, left);
+	}
+	if (nil_check.node != nullptr) {
+		return nil_check;
+	}
+
+	if (are_types_identical(a, b)) {
+		// NOTE(bill): No need for a conversion
+	} /*else if (cg_is_const(left) || cg_is_const_nil(left)) {
+		left = cg_emit_conv(p, left, right.type);
+	} else if (cg_is_const(right) || cg_is_const_nil(right)) {
+		right = cg_emit_conv(p, right, left.type);
+	}*/ else {
+		Type *lt = left.type;
+		Type *rt = right.type;
+
+		lt = left.type;
+		rt = right.type;
+		i64 ls = type_size_of(lt);
+		i64 rs = type_size_of(rt);
+
+		// NOTE(bill): Quick heuristic, larger types are usually the target type
+		if (ls < rs) {
+			left = cg_emit_conv(p, left, rt);
+		} else if (ls > rs) {
+			right = cg_emit_conv(p, right, lt);
+		} else {
+			if (is_type_union(rt)) {
+				left = cg_emit_conv(p, left, rt);
+			} else {
+				right = cg_emit_conv(p, right, lt);
+			}
+		}
+	}
+
+	a = core_type(left.type);
+	b = core_type(right.type);
+	left  = cg_flatten_value(p, left);
+	right = cg_flatten_value(p, right);
+
+
+	if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+		GB_PANIC("TODO(bill): cg_emit_comp matrix");
+		// Type *tl = base_type(a);
+		// lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+		// lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+		// // TODO(bill): Test to see if this is actually faster!!!!
+		// auto args = array_make<lbValue>(permanent_allocator(), 3);
+		// args[0] = lb_emit_conv(p, lhs, t_rawptr);
+		// args[1] = lb_emit_conv(p, rhs, t_rawptr);
+		// args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+		// lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+		// lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+		// return lb_emit_conv(p, res, t_bool);
+	}
+	if (is_type_array_like(a)) {
+		GB_PANIC("TODO(bill): cg_emit_comp is_type_array_like");
+		// Type *tl = base_type(a);
+		// lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+		// lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+		// TokenKind cmp_op = Token_And;
+		// lbValue res = lb_const_bool(p->module, t_bool, true);
+		// if (op_kind == Token_NotEq) {
+		// 	res = lb_const_bool(p->module, t_bool, false);
+		// 	cmp_op = Token_Or;
+		// } else if (op_kind == Token_CmpEq) {
+		// 	res = lb_const_bool(p->module, t_bool, true);
+		// 	cmp_op = Token_And;
+		// }
+
+		// bool inline_array_arith = lb_can_try_to_inline_array_arith(tl);
+		// i32 count = 0;
+		// switch (tl->kind) {
+		// case Type_Array:           count = cast(i32)tl->Array.count;           break;
+		// case Type_EnumeratedArray: count = cast(i32)tl->EnumeratedArray.count; break;
+		// }
+
+		// if (inline_array_arith) {
+		// 	// inline
+		// 	lbAddr val = lb_add_local_generated(p, t_bool, false);
+		// 	lb_addr_store(p, val, res);
+		// 	for (i32 i = 0; i < count; i++) {
+		// 		lbValue x = lb_emit_load(p, lb_emit_array_epi(p, lhs, i));
+		// 		lbValue y = lb_emit_load(p, lb_emit_array_epi(p, rhs, i));
+		// 		lbValue cmp = lb_emit_comp(p, op_kind, x, y);
+		// 		lbValue new_res = lb_emit_arith(p, cmp_op, lb_addr_load(p, val), cmp, t_bool);
+		// 		lb_addr_store(p, val, lb_emit_conv(p, new_res, t_bool));
+		// 	}
+
+		// 	return lb_addr_load(p, val);
+		// } else {
+		// 	if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+		// 		// TODO(bill): Test to see if this is actually faster!!!!
+		// 		auto args = array_make<lbValue>(permanent_allocator(), 3);
+		// 		args[0] = lb_emit_conv(p, lhs, t_rawptr);
+		// 		args[1] = lb_emit_conv(p, rhs, t_rawptr);
+		// 		args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+		// 		lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+		// 		lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+		// 		return lb_emit_conv(p, res, t_bool);
+		// 	} else {
+		// 		lbAddr val = lb_add_local_generated(p, t_bool, false);
+		// 		lb_addr_store(p, val, res);
+		// 		auto loop_data = lb_loop_start(p, count, t_i32);
+		// 		{
+		// 			lbValue i = loop_data.idx;
+		// 			lbValue x = lb_emit_load(p, lb_emit_array_ep(p, lhs, i));
+		// 			lbValue y = lb_emit_load(p, lb_emit_array_ep(p, rhs, i));
+		// 			lbValue cmp = lb_emit_comp(p, op_kind, x, y);
+		// 			lbValue new_res = lb_emit_arith(p, cmp_op, lb_addr_load(p, val), cmp, t_bool);
+		// 			lb_addr_store(p, val, lb_emit_conv(p, new_res, t_bool));
+		// 		}
+		// 		lb_loop_end(p, loop_data);
+
+		// 		return lb_addr_load(p, val);
+		// 	}
+		// }
+	}
+
+	if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) {
+		return cg_emit_comp_records(p, op_kind, left, right, a);
+	}
+
+	if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) {
+		return cg_emit_comp_records(p, op_kind, left, right, b);
+	}
+
+	if (is_type_string(a)) {
+		if (is_type_cstring(a)) {
+			left  = cg_emit_conv(p, left, t_string);
+			right = cg_emit_conv(p, right, t_string);
+		}
+
+		char const *runtime_procedure = nullptr;
+		switch (op_kind) {
+		case Token_CmpEq: runtime_procedure = "string_eq"; break;
+		case Token_NotEq: runtime_procedure = "string_ne"; break;
+		case Token_Lt:    runtime_procedure = "string_lt"; break;
+		case Token_Gt:    runtime_procedure = "string_gt"; break;
+		case Token_LtEq:  runtime_procedure = "string_le"; break;
+		case Token_GtEq:  runtime_procedure = "string_gt"; break;
+		}
+		GB_ASSERT(runtime_procedure != nullptr);
+
+		auto args = slice_make<cgValue>(permanent_allocator(), 2);
+		args[0] = left;
+		args[1] = right;
+		return cg_emit_runtime_call(p, runtime_procedure, args);
+	}
+
+	if (is_type_complex(a)) {
+		char const *runtime_procedure = "";
+		i64 sz = 8*type_size_of(a);
+		switch (sz) {
+		case 32:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "complex32_eq"; break;
+			case Token_NotEq: runtime_procedure = "complex32_ne"; break;
+			}
+			break;
+		case 64:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "complex64_eq"; break;
+			case Token_NotEq: runtime_procedure = "complex64_ne"; break;
+			}
+			break;
+		case 128:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "complex128_eq"; break;
+			case Token_NotEq: runtime_procedure = "complex128_ne"; break;
+			}
+			break;
+		}
+		GB_ASSERT(runtime_procedure != nullptr);
+
+		GB_PANIC("TODO(bill): cg_emit_runtime_call");
+		// auto args = array_make<lbValue>(permanent_allocator(), 2);
+		// args[0] = left;
+		// args[1] = right;
+		// return lb_emit_runtime_call(p, runtime_procedure, args);
+	}
+
+	if (is_type_quaternion(a)) {
+		char const *runtime_procedure = "";
+		i64 sz = 8*type_size_of(a);
+		switch (sz) {
+		case 64:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "quaternion64_eq"; break;
+			case Token_NotEq: runtime_procedure = "quaternion64_ne"; break;
+			}
+			break;
+		case 128:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "quaternion128_eq"; break;
+			case Token_NotEq: runtime_procedure = "quaternion128_ne"; break;
+			}
+			break;
+		case 256:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "quaternion256_eq"; break;
+			case Token_NotEq: runtime_procedure = "quaternion256_ne"; break;
+			}
+			break;
+		}
+		GB_ASSERT(runtime_procedure != nullptr);
+
+		GB_PANIC("TODO(bill): cg_emit_runtime_call");
+		// auto args = array_make<lbValue>(permanent_allocator(), 2);
+		// args[0] = left;
+		// args[1] = right;
+		// return lb_emit_runtime_call(p, runtime_procedure, args);
+	}
+
+	if (is_type_bit_set(a)) {
+		switch (op_kind) {
+		case Token_Lt:
+		case Token_LtEq:
+		case Token_Gt:
+		case Token_GtEq:
+			{
+				Type *it = bit_set_to_int(a);
+				cgValue lhs = cg_emit_transmute(p, left, it);
+				cgValue rhs = cg_emit_transmute(p, right, it);
+				cgValue res = cg_emit_arith(p, Token_And, lhs, rhs, it);
+				GB_ASSERT(lhs.kind == cgValue_Value);
+				GB_ASSERT(rhs.kind == cgValue_Value);
+				GB_ASSERT(res.kind == cgValue_Value);
+
+				if (op_kind == Token_Lt || op_kind == Token_LtEq) {
+					// (lhs & rhs) == lhs
+					res = cg_value(tb_inst_cmp_eq(p->func, res.node, lhs.node), t_bool);
+				} else if (op_kind == Token_Gt || op_kind == Token_GtEq) {
+					// (lhs & rhs) == rhs
+					res = cg_value(tb_inst_cmp_eq(p->func, res.node, rhs.node), t_bool);
+				}
+
+				// NOTE(bill): Strict subsets
+				if (op_kind == Token_Lt || op_kind == Token_Gt) {
+					// res &~ (lhs == rhs)
+					cgValue eq = cg_value(tb_inst_cmp_eq(p->func, lhs.node, rhs.node), t_bool);
+					res = cg_emit_arith(p, Token_AndNot, res, eq, t_bool);
+				}
+				return res;
+			}
+
+		case Token_CmpEq:
+			GB_ASSERT(left.kind  == cgValue_Value);
+			GB_ASSERT(right.kind == cgValue_Value);
+			return cg_value(tb_inst_cmp_eq(p->func, left.node, right.node), t_bool);
+		case Token_NotEq:
+			GB_ASSERT(left.kind  == cgValue_Value);
+			GB_ASSERT(right.kind == cgValue_Value);
+			return cg_value(tb_inst_cmp_ne(p->func, left.node, right.node), t_bool);
+		}
+	}
+
+	if (op_kind != Token_CmpEq && op_kind != Token_NotEq) {
+		Type *t = left.type;
+		if (is_type_integer(t) && is_type_different_to_arch_endianness(t)) {
+			Type *platform_type = integer_endian_type_to_platform_type(t);
+			cgValue x = cg_emit_byte_swap(p, left, platform_type);
+			cgValue y = cg_emit_byte_swap(p, right, platform_type);
+			left = x;
+			right = y;
+		} else if (is_type_float(t) && is_type_different_to_arch_endianness(t)) {
+			Type *platform_type = integer_endian_type_to_platform_type(t);
+			cgValue x = cg_emit_conv(p, left, platform_type);
+			cgValue y = cg_emit_conv(p, right, platform_type);
+			left = x;
+			right = y;
+		}
+	}
+
+	a = core_type(left.type);
+	b = core_type(right.type);
+
+
+	if (is_type_integer(a) ||
+	    is_type_boolean(a) ||
+	    is_type_pointer(a) ||
+	    is_type_multi_pointer(a) ||
+	    is_type_proc(a) ||
+	    is_type_enum(a) ||
+	    is_type_typeid(a)) {
+	    	TB_Node *lhs = left.node;
+		TB_Node *rhs = right.node;
+		TB_Node *res = nullptr;
+
+		bool is_signed = is_type_integer(left.type) && !is_type_unsigned(left.type);
+		switch (op_kind) {
+		case Token_CmpEq: res = tb_inst_cmp_eq(p->func, lhs, rhs); break;
+		case Token_NotEq: res = tb_inst_cmp_ne(p->func, lhs, rhs); break;
+		case Token_Gt:    res = tb_inst_cmp_igt(p->func, lhs, rhs, is_signed); break;
+		case Token_GtEq:  res = tb_inst_cmp_ige(p->func, lhs, rhs, is_signed); break;
+		case Token_Lt:    res = tb_inst_cmp_ilt(p->func, lhs, rhs, is_signed); break;
+		case Token_LtEq:  res = tb_inst_cmp_ile(p->func, lhs, rhs, is_signed); break;
+		}
+
+		GB_ASSERT(res != nullptr);
+		return cg_value(res, t_bool);
+	} else if (is_type_float(a)) {
+	    	TB_Node *lhs = left.node;
+		TB_Node *rhs = right.node;
+		TB_Node *res = nullptr;
+		switch (op_kind) {
+		case Token_CmpEq: res = tb_inst_cmp_eq(p->func, lhs, rhs);  break;
+		case Token_NotEq: res = tb_inst_cmp_ne(p->func, lhs, rhs);  break;
+		case Token_Gt:    res = tb_inst_cmp_fgt(p->func, lhs, rhs); break;
+		case Token_GtEq:  res = tb_inst_cmp_fge(p->func, lhs, rhs); break;
+		case Token_Lt:    res = tb_inst_cmp_flt(p->func, lhs, rhs); break;
+		case Token_LtEq:  res = tb_inst_cmp_fle(p->func, lhs, rhs); break;
+		}
+		GB_ASSERT(res != nullptr);
+		return cg_value(res, t_bool);
+	} else if (is_type_simd_vector(a)) {
+		GB_PANIC("TODO(bill): #simd vector");
+		// LLVMValueRef mask = nullptr;
+		// Type *elem = base_array_type(a);
+		// if (is_type_float(elem)) {
+		// 	LLVMRealPredicate pred = {};
+		// 	switch (op_kind) {
+		// 	case Token_CmpEq: pred = LLVMRealOEQ; break;
+		// 	case Token_NotEq: pred = LLVMRealONE; break;
+		// 	}
+		// 	mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
+		// } else {
+		// 	LLVMIntPredicate pred = {};
+		// 	switch (op_kind) {
+		// 	case Token_CmpEq: pred = LLVMIntEQ; break;
+		// 	case Token_NotEq: pred = LLVMIntNE; break;
+		// 	}
+		// 	mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+		// }
+		// GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
+
+		// /* NOTE(bill, 2022-05-28):
+		// 	Thanks to Per Vognsen, sign extending <N x i1> to
+		// 	a vector of the same width as the input vector, bit casting to an integer,
+		// 	and then comparing against zero is the better option
+		// 	See: https://lists.llvm.org/pipermail/llvm-dev/2012-September/053046.html
+
+		// 	// Example assuming 128-bit vector
+
+		// 	%1 = <4 x float> ...
+		// 	%2 = <4 x float> ...
+		// 	%3 = fcmp oeq <4 x float> %1, %2
+		// 	%4 = sext <4 x i1> %3 to <4 x i32>
+		// 	%5 = bitcast <4 x i32> %4 to i128
+		// 	%6 = icmp ne i128 %5, 0
+		// 	br i1 %6, label %true1, label %false2
+
+		// 	This will result in 1 cmpps + 1 ptest + 1 br
+		// 	(even without SSE4.1, contrary to what the mail list states, because of pmovmskb)
+
+		// */
+
+		// unsigned count = cast(unsigned)get_array_type_count(a);
+		// unsigned elem_sz = cast(unsigned)(type_size_of(elem)*8);
+		// LLVMTypeRef mask_type = LLVMVectorType(LLVMIntTypeInContext(p->module->ctx, elem_sz), count);
+		// mask = LLVMBuildSExtOrBitCast(p->builder, mask, mask_type, "");
+
+		// LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a)));
+		// LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, "");
+		// res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), "");
+		// return res;
+	}
+
+	GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
+	return {};
+}
+
+gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x) {
+	GB_ASSERT(op_kind == Token_CmpEq || op_kind == Token_NotEq);
+	x = cg_flatten_value(p, x);
+	cgValue res = {};
+	Type *t = x.type;
+
+	TB_DataType dt = cg_data_type(t);
+
+	Type *bt = base_type(t);
+	TypeKind type_kind = bt->kind;
+
+	switch (type_kind) {
+	case Type_Basic:
+		switch (bt->Basic.kind) {
+		case Basic_rawptr:
+		case Basic_cstring:
+			GB_ASSERT(x.kind == cgValue_Value);
+			if (op_kind == Token_CmpEq) {
+				return cg_value(tb_inst_cmp_eq(p->func, x.node, tb_inst_uint(p->func, dt, 0)), t_bool);
+			} else if (op_kind == Token_NotEq) {
+				return cg_value(tb_inst_cmp_ne(p->func, x.node, tb_inst_uint(p->func, dt, 0)), t_bool);
+			}
+			break;
+		case Basic_any:
+			{
+				GB_ASSERT(x.kind == cgValue_Addr);
+				// // TODO(bill): is this correct behaviour for nil comparison for any?
+				cgValue data = cg_emit_struct_ev(p, x, 0);
+				cgValue id   = cg_emit_struct_ev(p, x, 1);
+
+				if (op_kind == Token_CmpEq) {
+					TB_Node *a = tb_inst_cmp_eq(p->func, data.node, tb_inst_uint(p->func, data.node->dt, 0));
+					TB_Node *b = tb_inst_cmp_eq(p->func, id.node,   tb_inst_uint(p->func, id.node->dt,   0));
+					TB_Node *c = tb_inst_or(p->func, a, b);
+					return cg_value(c, t_bool);
+				} else if (op_kind == Token_NotEq) {
+					TB_Node *a = tb_inst_cmp_ne(p->func, data.node, tb_inst_uint(p->func, data.node->dt, 0));
+					TB_Node *b = tb_inst_cmp_ne(p->func, id.node,   tb_inst_uint(p->func, id.node->dt,   0));
+					TB_Node *c = tb_inst_and(p->func, a, b);
+					return cg_value(c, t_bool);
+				}
+			}
+			break;
+		case Basic_typeid:
+			cgValue invalid_typeid = cg_const_value(p, t_typeid, exact_value_i64(0));
+			return cg_emit_comp(p, op_kind, x, invalid_typeid);
+		}
+		break;
+
+	case Type_Enum:
+	case Type_Pointer:
+	case Type_MultiPointer:
+	case Type_Proc:
+	case Type_BitSet:
+		GB_ASSERT(x.kind == cgValue_Value);
+		if (op_kind == Token_CmpEq) {
+			return cg_value(tb_inst_cmp_eq(p->func, x.node, tb_inst_uint(p->func, dt, 0)), t_bool);
+		} else if (op_kind == Token_NotEq) {
+			return cg_value(tb_inst_cmp_ne(p->func, x.node, tb_inst_uint(p->func, dt, 0)), t_bool);
+		}
+		break;
+
+	case Type_Slice:
+	case Type_DynamicArray:
+	case Type_Map:
+		{
+			// NOTE(bill): all of their data "pointer-like" fields are at the 0-index
+			cgValue data = cg_emit_struct_ev(p, x, 0);
+			if (op_kind == Token_CmpEq) {
+				TB_Node *a = tb_inst_cmp_eq(p->func, data.node, tb_inst_uint(p->func, data.node->dt, 0));
+				return cg_value(a, t_bool);
+			} else if (op_kind == Token_NotEq) {
+				TB_Node *a = tb_inst_cmp_ne(p->func, data.node, tb_inst_uint(p->func, data.node->dt, 0));
+				return cg_value(a, t_bool);
+			}
+		}
+		break;
+
+	case Type_Union:
+		{
+			if (type_size_of(t) == 0) {
+				return cg_const_bool(p, t_bool, op_kind == Token_CmpEq);
+			} else if (is_type_union_maybe_pointer(t)) {
+				cgValue tag = cg_emit_transmute(p, x, t_rawptr);
+				return cg_emit_comp_against_nil(p, op_kind, tag);
+			} else {
+				GB_ASSERT("TODO(bill): cg_emit_union_tag_value");
+				// cgValue tag = cg_emit_union_tag_value(p, x);
+				// return cg_emit_comp(p, op_kind, tag, cg_zero(p->module, tag.type));
+			}
+		}
+		break;
+	case Type_Struct:
+		GB_PANIC("TODO(bill): cg_emit_struct_ev");
+		// if (is_type_soa_struct(t)) {
+		// 	Type *bt = base_type(t);
+		// 	if (bt->Struct.soa_kind == StructSoa_Slice) {
+		// 		LLVMValueRef the_value = {};
+		// 		if (bt->Struct.fields.count == 0) {
+		// 			cgValue len = cg_soa_struct_len(p, x);
+		// 			the_value = len.value;
+		// 		} else {
+		// 			cgValue first_field = cg_emit_struct_ev(p, x, 0);
+		// 			the_value = first_field.value;
+		// 		}
+		// 		if (op_kind == Token_CmpEq) {
+		// 			res.value = LLVMBuildIsNull(p->builder, the_value, "");
+		// 			return res;
+		// 		} else if (op_kind == Token_NotEq) {
+		// 			res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+		// 			return res;
+		// 		}
+		// 	} else if (bt->Struct.soa_kind == StructSoa_Dynamic) {
+		// 		LLVMValueRef the_value = {};
+		// 		if (bt->Struct.fields.count == 0) {
+		// 			cgValue cap = cg_soa_struct_cap(p, x);
+		// 			the_value = cap.value;
+		// 		} else {
+		// 			cgValue first_field = cg_emit_struct_ev(p, x, 0);
+		// 			the_value = first_field.value;
+		// 		}
+		// 		if (op_kind == Token_CmpEq) {
+		// 			res.value = LLVMBuildIsNull(p->builder, the_value, "");
+		// 			return res;
+		// 		} else if (op_kind == Token_NotEq) {
+		// 			res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+		// 			return res;
+		// 		}
+		// 	}
+		// } else if (is_type_struct(t) && type_has_nil(t)) {
+		// 	auto args = array_make<cgValue>(permanent_allocator(), 2);
+		// 	cgValue lhs = cg_address_from_load_or_generate_local(p, x);
+		// 	args[0] = cg_emit_conv(p, lhs, t_rawptr);
+		// 	args[1] = cg_const_int(p->module, t_int, type_size_of(t));
+		// 	cgValue val = cg_emit_runtime_call(p, "memory_compare_zero", args);
+		// 	cgValue res = cg_emit_comp(p, op_kind, val, cg_const_int(p->module, t_int, 0));
+		// 	return res;
+		// }
+		break;
+	}
+	GB_PANIC("Unknown handled type: %s -> %s", type_to_string(t), type_to_string(bt));
+	return {};
+}
+
+gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
+	t = reduce_tuple_to_single_type(t);
+
+	value = cg_flatten_value(p, value);
+
+	Type *src_type = value.type;
+	if (are_types_identical(t, src_type)) {
+		return value;
+	}
+
+	if (is_type_untyped_uninit(src_type)) {
+		// return cg_const_undef(m, t);
+		return cg_const_nil(p, t);
+	}
+	if (is_type_untyped_nil(src_type)) {
+		return cg_const_nil(p, t);
+	}
+
+	Type *src = core_type(src_type);
+	Type *dst = core_type(t);
+	GB_ASSERT(src != nullptr);
+	GB_ASSERT(dst != nullptr);
+
+	if (are_types_identical(src, dst)) {
+		return cg_emit_transmute(p, value, t);
+	}
+
+	TB_DataType st = cg_data_type(src);
+	if (value.kind == cgValue_Value && !TB_IS_VOID_TYPE(value.node->dt)) {
+		st = value.node->dt;
+	}
+	TB_DataType dt = cg_data_type(t);
+
+	if (is_type_integer(src) && is_type_integer(dst)) {
+		GB_ASSERT(src->kind == Type_Basic &&
+		          dst->kind == Type_Basic);
+		GB_ASSERT(value.kind == cgValue_Value);
+
+		i64 sz = type_size_of(default_type(src));
+		i64 dz = type_size_of(default_type(dst));
+
+		if (sz == dz) {
+			if (dz > 1 && !types_have_same_internal_endian(src, dst)) {
+				return cg_emit_byte_swap(p, value, t);
+			}
+			value.type = t;
+			return value;
+		}
+
+		if (sz > 1 && is_type_different_to_arch_endianness(src)) {
+			Type *platform_src_type = integer_endian_type_to_platform_type(src);
+			value = cg_emit_byte_swap(p, value, platform_src_type);
+		}
+
+		TB_Node* (*op)(TB_Function* f, TB_Node* src, TB_DataType dt) = tb_inst_trunc;
+
+		if (dz < sz) {
+			op = tb_inst_trunc;
+		} else if (dz == sz) {
+			op = tb_inst_bitcast;
+		} else if (dz > sz) {
+			op = is_type_unsigned(src) ? tb_inst_zxt : tb_inst_sxt; // zero extent
+		}
+
+		if (dz > 1 && is_type_different_to_arch_endianness(dst)) {
+			Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
+
+			cgValue res = cg_value(op(p->func, value.node, cg_data_type(platform_dst_type)), platform_dst_type);
+			return cg_emit_byte_swap(p, res, t);
+		} else {
+			return cg_value(op(p->func, value.node, dt), t);
+		}
+	}
+
+	// boolean -> boolean/integer
+	if (is_type_boolean(src) && (is_type_boolean(dst) || is_type_integer(dst))) {
+		TB_Node *v = tb_inst_cmp_ne(p->func, value.node, tb_inst_uint(p->func, st, 0));
+		return cg_value(tb_inst_zxt(p->func, v, dt), t);
+	}
+
+	// integer -> boolean
+	if (is_type_integer(src) && is_type_boolean(dst)) {
+		TB_Node *v = tb_inst_cmp_ne(p->func, value.node, tb_inst_uint(p->func, st, 0));
+		return cg_value(tb_inst_zxt(p->func, v, dt), t);
+	}
+
+	if (is_type_cstring(src) && is_type_u8_ptr(dst)) {
+		return cg_emit_transmute(p, value, dst);
+	}
+	if (is_type_u8_ptr(src) && is_type_cstring(dst)) {
+		return cg_emit_transmute(p, value, dst);
+	}
+	if (is_type_cstring(src) && is_type_u8_multi_ptr(dst)) {
+		return cg_emit_transmute(p, value, dst);
+	}
+	if (is_type_u8_multi_ptr(src) && is_type_cstring(dst)) {
+		return cg_emit_transmute(p, value, dst);
+	}
+	if (is_type_cstring(src) && is_type_rawptr(dst)) {
+		return cg_emit_transmute(p, value, dst);
+	}
+	if (is_type_rawptr(src) && is_type_cstring(dst)) {
+		return cg_emit_transmute(p, value, dst);
+	}
+
+
+	if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) {
+		TEMPORARY_ALLOCATOR_GUARD();
+		cgValue c = cg_emit_conv(p, value, t_cstring);
+		auto args = slice_make<cgValue>(temporary_allocator(), 1);
+		args[0] = c;
+		cgValue s = cg_emit_runtime_call(p, "cstring_to_string", args);
+		return cg_emit_conv(p, s, dst);
+	}
+
+	// float -> float
+	if (is_type_float(src) && is_type_float(dst)) {
+		i64 sz = type_size_of(src);
+		i64 dz = type_size_of(dst);
+
+		if (sz == 2 || dz == 2) {
+			GB_PANIC("TODO(bill): f16 conversions");
+		}
+
+
+		if (dz == sz) {
+			if (types_have_same_internal_endian(src, dst)) {
+				return cg_value(value.node, t);
+			} else {
+				return cg_emit_byte_swap(p, value, t);
+			}
+		}
+
+		if (is_type_different_to_arch_endianness(src) || is_type_different_to_arch_endianness(dst)) {
+			Type *platform_src_type = integer_endian_type_to_platform_type(src);
+			Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
+			cgValue res = {};
+			res = cg_emit_conv(p, value, platform_src_type);
+			res = cg_emit_conv(p, res, platform_dst_type);
+			if (is_type_different_to_arch_endianness(dst)) {
+				res = cg_emit_byte_swap(p, res, t);
+			}
+			return cg_emit_conv(p, res, t);
+		}
+
+
+		if (dz >= sz) {
+			return cg_value(tb_inst_fpxt(p->func, value.node, dt), t);
+		}
+		return cg_value(tb_inst_trunc(p->func, value.node, dt), t);
+	}
+
+	if (is_type_complex(src) && is_type_complex(dst)) {
+		GB_PANIC("TODO(bill): complex -> complex");
+	}
+
+	if (is_type_quaternion(src) && is_type_quaternion(dst)) {
+		// @QuaternionLayout
+		GB_PANIC("TODO(bill): quaternion -> quaternion");
+	}
+	if (is_type_integer(src) && is_type_complex(dst)) {
+		GB_PANIC("TODO(bill): int -> complex");
+	}
+	if (is_type_float(src) && is_type_complex(dst)) {
+		GB_PANIC("TODO(bill): float -> complex");
+	}
+	if (is_type_integer(src) && is_type_quaternion(dst)) {
+		GB_PANIC("TODO(bill): int -> quaternion");
+	}
+	if (is_type_float(src) && is_type_quaternion(dst)) {
+		GB_PANIC("TODO(bill): float -> quaternion");
+	}
+	if (is_type_complex(src) && is_type_quaternion(dst)) {
+		GB_PANIC("TODO(bill): complex -> quaternion");
+	}
+
+
+	// float <-> integer
+	if (is_type_float(src) && is_type_integer(dst)) {
+		if (is_type_different_to_arch_endianness(src) || is_type_different_to_arch_endianness(dst)) {
+			Type *platform_src_type = integer_endian_type_to_platform_type(src);
+			Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
+			cgValue res = {};
+			res = cg_emit_conv(p, value, platform_src_type);
+			res = cg_emit_conv(p, res, platform_dst_type);
+			return cg_emit_conv(p, res, t);
+		}
+
+		// if (is_type_integer_128bit(dst)) {
+		// 	TEMPORARY_ALLOCATOR_GUARD();
+
+		// 	auto args = array_make<lbValue>(temporary_allocator(), 1);
+		// 	args[0] = value;
+		// 	char const *call = "fixunsdfdi";
+		// 	if (is_type_unsigned(dst)) {
+		// 		call = "fixunsdfti";
+		// 	}
+		// 	lbValue res_i128 = lb_emit_runtime_call(p, call, args);
+		// 	return lb_emit_conv(p, res_i128, t);
+		// }
+
+		bool is_signed = !is_type_unsigned(dst);
+		return cg_value(tb_inst_float2int(p->func, value.node, dt, is_signed), t);
+	}
+	if (is_type_integer(src) && is_type_float(dst)) {
+		if (is_type_different_to_arch_endianness(src) || is_type_different_to_arch_endianness(dst)) {
+			Type *platform_src_type = integer_endian_type_to_platform_type(src);
+			Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
+			cgValue res = {};
+			res = cg_emit_conv(p, value, platform_src_type);
+			res = cg_emit_conv(p, res, platform_dst_type);
+			if (is_type_different_to_arch_endianness(dst)) {
+				res = cg_emit_byte_swap(p, res, t);
+			}
+			return cg_emit_conv(p, res, t);
+		}
+
+		// if (is_type_integer_128bit(src)) {
+		// 	TEMPORARY_ALLOCATOR_GUARD();
+
+		// 	auto args = array_make<lbValue>(temporary_allocator(), 1);
+		// 	args[0] = value;
+		// 	char const *call = "floattidf";
+		// 	if (is_type_unsigned(src)) {
+		// 		call = "floattidf_unsigned";
+		// 	}
+		// 	lbValue res_f64 = lb_emit_runtime_call(p, call, args);
+		// 	return lb_emit_conv(p, res_f64, t);
+		// }
+
+		bool is_signed = !is_type_unsigned(dst);
+		return cg_value(tb_inst_int2float(p->func, value.node, dt, is_signed), t);
+	}
+
+	if (is_type_simd_vector(dst)) {
+		GB_PANIC("TODO(bill): ? -> #simd vector");
+	}
+
+
+	// Pointer <-> uintptr
+	if (is_type_pointer(src) && is_type_uintptr(dst)) {
+		return cg_value(tb_inst_ptr2int(p->func, value.node, dt), t);
+	}
+	if (is_type_uintptr(src) && is_type_pointer(dst)) {
+		return cg_value(tb_inst_int2ptr(p->func, value.node), t);
+	}
+	if (is_type_multi_pointer(src) && is_type_uintptr(dst)) {
+		return cg_value(tb_inst_ptr2int(p->func, value.node, dt), t);
+	}
+	if (is_type_uintptr(src) && is_type_multi_pointer(dst)) {
+		return cg_value(tb_inst_int2ptr(p->func, value.node), t);
+	}
+
+	if (is_type_union(dst)) {
+		GB_PANIC("TODO(bill): ? -> union");
+	}
+
+	// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
+	// subtype polymorphism casting
+	if (check_is_assignable_to_using_subtype(src_type, t)) {
+		GB_PANIC("TODO(bill): ? -> subtyping");
+	}
+
+	// Pointer <-> Pointer
+	if (is_type_pointer(src) && is_type_pointer(dst)) {
+		return cg_value(value.node, t);
+	}
+	if (is_type_multi_pointer(src) && is_type_pointer(dst)) {
+		return cg_value(value.node, t);
+	}
+	if (is_type_pointer(src) && is_type_multi_pointer(dst)) {
+		return cg_value(value.node, t);
+	}
+	if (is_type_multi_pointer(src) && is_type_multi_pointer(dst)) {
+		return cg_value(value.node, t);
+	}
+
+	// proc <-> proc
+	if (is_type_proc(src) && is_type_proc(dst)) {
+		return cg_value(value.node, t);
+	}
+
+	// pointer -> proc
+	if (is_type_pointer(src) && is_type_proc(dst)) {
+		return cg_value(value.node, t);
+	}
+	// proc -> pointer
+	if (is_type_proc(src) && is_type_pointer(dst)) {
+		return cg_value(value.node, t);
+	}
+
+	// []byte/[]u8 <-> string
+	if (is_type_u8_slice(src) && is_type_string(dst)) {
+		return cg_emit_transmute(p, value, t);
+	}
+	if (is_type_string(src) && is_type_u8_slice(dst)) {
+		return cg_emit_transmute(p, value, t);
+	}
+
+	if (is_type_matrix(dst) && !is_type_matrix(src)) {
+		GB_PANIC("TODO(bill): !matrix -> matrix");
+	}
+
+	if (is_type_matrix(dst) && is_type_matrix(src)) {
+		GB_PANIC("TODO(bill): matrix -> matrix");
+	}
+
+	if (is_type_any(dst)) {
+		if (is_type_untyped_nil(src) ||
+		    is_type_untyped_uninit(src)) {
+			return cg_const_nil(p, t);
+		}
+
+		cgAddr result = cg_add_local(p, t, nullptr, false);
+
+		Type *st = default_type(src_type);
+
+		cgValue data = cg_address_from_load_or_generate_local(p, value);
+		GB_ASSERT(is_type_pointer(data.type));
+		GB_ASSERT(is_type_typed(st));
+
+		data = cg_emit_conv(p, data, t_rawptr);
+
+		cgValue id = cg_typeid(p, st);
+		cgValue data_ptr = cg_emit_struct_ep(p, result.addr, 0);
+		cgValue id_ptr   = cg_emit_struct_ep(p, result.addr, 1);
+
+		cg_emit_store(p, data_ptr, data);
+		cg_emit_store(p, id_ptr,   id);
+
+		return cg_addr_load(p, result);
+	}
+
+	i64 src_sz = type_size_of(src);
+	i64 dst_sz = type_size_of(dst);
+
+	if (src_sz == dst_sz) {
+		// bit_set <-> integer
+		if (is_type_integer(src) && is_type_bit_set(dst)) {
+			cgValue v = cg_emit_conv(p, value, bit_set_to_int(dst));
+			return cg_emit_transmute(p, v, t);
+		}
+		if (is_type_bit_set(src) && is_type_integer(dst)) {
+			cgValue bs = cg_emit_transmute(p, value, bit_set_to_int(src));
+			return cg_emit_conv(p, bs, dst);
+		}
+
+		// typeid <-> integer
+		if (is_type_integer(src) && is_type_typeid(dst)) {
+			return cg_emit_transmute(p, value, dst);
+		}
+		if (is_type_typeid(src) && is_type_integer(dst)) {
+			return cg_emit_transmute(p, value, dst);
+		}
+	}
+
+
+	if (is_type_untyped(src)) {
+		if (is_type_string(src) && is_type_string(dst)) {
+			cgAddr result = cg_add_local(p, t, nullptr, false);
+			cg_addr_store(p, result, value);
+			return cg_addr_load(p, result);
+		}
+	}
+
+
+	gb_printf_err("%.*s\n", LIT(p->name));
+	gb_printf_err("cg_emit_conv: src -> dst\n");
+	gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
+	gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
+	gb_printf_err("Not Identical %p != %p\n", src_type, t);
+	gb_printf_err("Not Identical %p != %p\n", src, dst);
+
+
+	GB_PANIC("Invalid type conversion: '%s' to '%s' for procedure '%.*s'",
+	         type_to_string(src_type), type_to_string(t),
+	         LIT(p->name));
+
+	return {};
+}
+
+gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type) {
+	if (is_type_array_like(lhs.type) || is_type_array_like(rhs.type)) {
+		GB_PANIC("TODO(bill): cg_emit_arith_array");
+	} else if (is_type_matrix(lhs.type) || is_type_matrix(rhs.type)) {
+		GB_PANIC("TODO(bill): cg_emit_arith_matrix");
+	} else if (is_type_complex(type)) {
+		GB_PANIC("TODO(bill): cg_emit_arith complex");
+	} else if (is_type_quaternion(type)) {
+		GB_PANIC("TODO(bill): cg_emit_arith quaternion");
+	}
+
+	lhs = cg_flatten_value(p, cg_emit_conv(p, lhs, type));
+	rhs = cg_flatten_value(p, cg_emit_conv(p, rhs, type));
+	GB_ASSERT(lhs.kind == cgValue_Value);
+	GB_ASSERT(rhs.kind == cgValue_Value);
+
+	if (is_type_integer(type) && is_type_different_to_arch_endianness(type)) {
+		switch (op) {
+		case Token_AndNot:
+		case Token_And:
+		case Token_Or:
+		case Token_Xor:
+			goto handle_op;
+		}
+
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		cgValue x = cg_emit_byte_swap(p, lhs, integer_endian_type_to_platform_type(lhs.type));
+		cgValue y = cg_emit_byte_swap(p, rhs, integer_endian_type_to_platform_type(rhs.type));
+
+		cgValue res = cg_emit_arith(p, op, x, y, platform_type);
+
+		return cg_emit_byte_swap(p, res, type);
+	}
+
+	if (is_type_float(type) && is_type_different_to_arch_endianness(type)) {
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		cgValue x = cg_emit_conv(p, lhs, integer_endian_type_to_platform_type(lhs.type));
+		cgValue y = cg_emit_conv(p, rhs, integer_endian_type_to_platform_type(rhs.type));
+
+		cgValue res = cg_emit_arith(p, op, x, y, platform_type);
+
+		return cg_emit_byte_swap(p, res, type);
+	}
+
+handle_op:;
+
+	// NOTE(bill): Bit Set Aliases for + and -
+	if (is_type_bit_set(type)) {
+		switch (op) {
+		case Token_Add: op = Token_Or;     break;
+		case Token_Sub: op = Token_AndNot; break;
+		}
+	}
+
+	TB_ArithmeticBehavior arith_behavior = cast(TB_ArithmeticBehavior)0;
+
+	Type *integral_type = type;
+	if (is_type_simd_vector(integral_type)) {
+		GB_PANIC("TODO(bill): cg_emit_arith #simd vector");
+		// integral_type = core_array_type(integral_type);
+	}
+
+	switch (op) {
+	case Token_Add:
+		if (is_type_float(integral_type)) {
+			return cg_value(tb_inst_fadd(p->func, lhs.node, rhs.node), type);
+		}
+		return cg_value(tb_inst_add(p->func, lhs.node, rhs.node, arith_behavior), type);
+	case Token_Sub:
+		if (is_type_float(integral_type)) {
+			return cg_value(tb_inst_fsub(p->func, lhs.node, rhs.node), type);
+		}
+		return cg_value(tb_inst_sub(p->func, lhs.node, rhs.node, arith_behavior), type);
+	case Token_Mul:
+		if (is_type_float(integral_type)) {
+			return cg_value(tb_inst_fmul(p->func, lhs.node, rhs.node), type);
+		}
+		return cg_value(tb_inst_mul(p->func, lhs.node, rhs.node, arith_behavior), type);
+	case Token_Quo:
+		if (is_type_float(integral_type)) {
+			return cg_value(tb_inst_fdiv(p->func, lhs.node, rhs.node), type);
+		}
+		return cg_value(tb_inst_div(p->func, lhs.node, rhs.node, !is_type_unsigned(integral_type)), type);
+	case Token_Mod:
+		if (is_type_float(integral_type)) {
+			GB_PANIC("TODO(bill): float %% float");
+		}
+		return cg_value(tb_inst_mod(p->func, lhs.node, rhs.node, !is_type_unsigned(integral_type)), type);
+	case Token_ModMod:
+		if (is_type_unsigned(integral_type)) {
+			return cg_value(tb_inst_mod(p->func, lhs.node, rhs.node, false), type);
+		} else {
+			TB_Node *a = tb_inst_mod(p->func, lhs.node, rhs.node, true);
+			TB_Node *b = tb_inst_add(p->func, a, rhs.node, arith_behavior);
+			TB_Node *c = tb_inst_mod(p->func, b, rhs.node, true);
+			return cg_value(c, type);
+		}
+
+	case Token_And:
+		return cg_value(tb_inst_and(p->func, lhs.node, rhs.node), type);
+	case Token_Or:
+		return cg_value(tb_inst_or(p->func, lhs.node, rhs.node), type);
+	case Token_Xor:
+		return cg_value(tb_inst_xor(p->func, lhs.node, rhs.node), type);
+	case Token_Shl:
+		{
+			rhs = cg_emit_conv(p, rhs, lhs.type);
+			TB_DataType dt = cg_data_type(lhs.type);
+			TB_Node *lhsval = lhs.node;
+			TB_Node *bits = rhs.node;
+
+			TB_Node *bit_size = tb_inst_uint(p->func, dt, 8*type_size_of(lhs.type));
+			TB_Node *zero = tb_inst_uint(p->func, dt, 0);
+
+			TB_Node *width_test = tb_inst_cmp_ilt(p->func, bits, bit_size, false);
+
+			TB_Node *res = tb_inst_shl(p->func, lhsval, bits, arith_behavior);
+			res = tb_inst_select(p->func, width_test, res, zero);
+			return cg_value(res, type);
+		}
+	case Token_Shr:
+		{
+			rhs = cg_emit_conv(p, rhs, lhs.type);
+			TB_DataType dt = cg_data_type(lhs.type);
+			TB_Node *lhsval = lhs.node;
+			TB_Node *bits = rhs.node;
+
+			TB_Node *bit_size = tb_inst_uint(p->func, dt, 8*type_size_of(lhs.type));
+			TB_Node *zero = tb_inst_uint(p->func, dt, 0);
+
+			TB_Node *width_test = tb_inst_cmp_ilt(p->func, bits, bit_size, false);
+
+			TB_Node *res = nullptr;
+
+			if (is_type_unsigned(integral_type)) {
+				res = tb_inst_shr(p->func, lhsval, bits);
+			} else {
+				res = tb_inst_sar(p->func, lhsval, bits);
+			}
+
+
+			res = tb_inst_select(p->func, width_test, res, zero);
+			return cg_value(res, type);
+		}
+	case Token_AndNot:
+		return cg_value(tb_inst_and(p->func, lhs.node, tb_inst_not(p->func, rhs.node)), type);
+	}
+
+	GB_PANIC("unhandled operator of cg_emit_arith");
+
+	return {};
+}
+
+
+gb_internal void cg_fill_slice(cgProcedure *p, cgAddr const &slice, cgValue data, cgValue len) {
+	cgValue slice_ptr = cg_addr_get_ptr(p, slice);
+	cgValue data_ptr = cg_emit_struct_ep(p, slice_ptr, 0);
+	cgValue len_ptr  = cg_emit_struct_ep(p, slice_ptr, 1);
+
+	data = cg_emit_conv(p, data, type_deref(data_ptr.type));
+	len = cg_emit_conv(p, len, t_int);
+	cg_emit_store(p, data_ptr, data);
+	cg_emit_store(p, len_ptr,  len);
+}
+
+gb_internal cgAddr cg_build_addr_slice_expr(cgProcedure *p, Ast *expr) {
+	ast_node(se, SliceExpr, expr);
+
+	cgValue low  = cg_const_int(p, t_int, 0);
+	cgValue high = {};
+
+	if (se->low  != nullptr) {
+		low = cg_correct_endianness(p, cg_build_expr(p, se->low));
+	}
+	if (se->high != nullptr) {
+		high = cg_correct_endianness(p, cg_build_expr(p, se->high));
+	}
+
+	bool no_indices = se->low == nullptr && se->high == nullptr;
+	gb_unused(no_indices);
+
+	cgAddr addr = cg_build_addr(p, se->expr);
+	cgValue base = cg_addr_load(p, addr);
+	Type *type = base_type(base.type);
+
+	if (is_type_pointer(type)) {
+		type = base_type(type_deref(type));
+		addr = cg_addr(base);
+		base = cg_addr_load(p, addr);
+	}
+
+	switch (type->kind) {
+	case Type_Basic:
+	case Type_Slice: {
+		if (type->kind == Type_Basic) {
+			GB_ASSERT(type->Basic.kind == Basic_string);
+		}
+
+		Type *slice_type = type;
+		if (high.node == nullptr) {
+			cgValue len = cg_builtin_len(p, base);
+			high = len;
+		}
+
+		if (!no_indices) {
+			// cg_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		}
+
+		cgValue elem    = cg_emit_ptr_offset(p, cg_builtin_raw_data(p, base), low);
+		cgValue new_len = cg_emit_arith(p, Token_Sub, high, low, t_int);
+
+		cgAddr slice = cg_add_local(p, slice_type, nullptr, true);
+		cg_fill_slice(p, slice, elem, new_len);
+		return slice;
+	}
+
+	case Type_RelativeMultiPointer:
+		GB_PANIC("TODO(bill): Type_RelativeMultiPointer should be handled above already on the cg_addr_load");
+		break;
+
+	case Type_DynamicArray: {
+		// Type *elem_type = type->DynamicArray.elem;
+		// Type *slice_type = alloc_type_slice(elem_type);
+
+		// lbValue len = lb_dynamic_array_len(p, base);
+		// if (high.value == nullptr) high = len;
+
+		// if (!no_indices) {
+		// 	lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// }
+
+		// lbValue elem    = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low);
+		// lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+		// lbAddr slice = lb_add_local_generated(p, slice_type, false);
+		// lb_fill_slice(p, slice, elem, new_len);
+		// return slice;
+		GB_PANIC("cg_build_addr_slice_expr Type_DynamicArray");
+		break;
+	}
+
+	case Type_MultiPointer: {
+		Type *res_type = type_of_expr(expr);
+		if (se->high == nullptr) {
+			cgAddr res = cg_add_local(p, res_type, nullptr, false);
+			GB_ASSERT(base.kind == cgValue_Value);
+			GB_ASSERT(low.kind == cgValue_Value);
+
+			i64 stride = type_size_of(type->MultiPointer.elem);
+			cgValue offset = cg_value(tb_inst_array_access(p->func, base.node, low.node, stride), base.type);
+			cg_addr_store(p, res, offset);
+			return res;
+		} else {
+			cgAddr res = cg_add_local(p, res_type, nullptr, true);
+			low  = cg_emit_conv(p, low,  t_int);
+			high = cg_emit_conv(p, high, t_int);
+
+			// cg_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
+
+			i64 stride = type_size_of(type->MultiPointer.elem);
+			TB_Node *offset = tb_inst_array_access(p->func, base.node, low.node, stride);
+			TB_Node *len = tb_inst_sub(p->func, high.node, low.node, cast(TB_ArithmeticBehavior)0);
+
+			TB_Node *data_ptr = tb_inst_member_access(p->func, res.addr.node, type_offset_of(res_type, 0));
+			TB_Node *len_ptr  = tb_inst_member_access(p->func, res.addr.node, type_offset_of(res_type, 1));
+
+			tb_inst_store(p->func, TB_TYPE_PTR, data_ptr, offset, cast(TB_CharUnits)build_context.ptr_size, false);
+			tb_inst_store(p->func, TB_TYPE_INT, len_ptr,  len,    cast(TB_CharUnits)build_context.int_size, false);
+			return res;
+		}
+	}
+
+	case Type_Array: {
+		Type *slice_type = type_of_expr(expr);
+		GB_ASSERT(is_type_slice(slice_type));
+		cgValue len = cg_const_int(p, t_int, type->Array.count);
+		if (high.node == nullptr) high = len;
+
+		// bool low_const  = type_and_value_of_expr(se->low).mode  == Addressing_Constant;
+		// bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant;
+		// if (!low_const || !high_const) {
+		// 	if (!no_indices) {
+		// 		lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// 	}
+		// }
+		cgValue elem    = cg_emit_ptr_offset(p, cg_builtin_raw_data(p, cg_addr_get_ptr(p, addr)), low);
+		cgValue new_len = cg_emit_arith(p, Token_Sub, high, low, t_int);
+
+		cgAddr slice = cg_add_local(p, slice_type, nullptr, true);
+		cg_fill_slice(p, slice, elem, new_len);
+		return slice;
+	}
+
+
+	case Type_Struct:
+		// if (is_type_soa_struct(type)) {
+		// 	lbValue len = lb_soa_struct_len(p, lb_addr_get_ptr(p, addr));
+		// 	if (high.value == nullptr) high = len;
+
+		// 	if (!no_indices) {
+		// 		lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// 	}
+		// 	#if 1
+
+		// 	lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true);
+		// 	if (type->Struct.soa_kind == StructSoa_Fixed) {
+		// 		i32 field_count = cast(i32)type->Struct.fields.count;
+		// 		for (i32 i = 0; i < field_count; i++) {
+		// 			lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+		// 			lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i);
+		// 			field_src = lb_emit_array_ep(p, field_src, low);
+		// 			lb_emit_store(p, field_dst, field_src);
+		// 		}
+
+		// 		lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+		// 		lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+		// 		lb_emit_store(p, len_dst, new_len);
+		// 	} else if (type->Struct.soa_kind == StructSoa_Slice) {
+		// 		if (no_indices) {
+		// 			lb_addr_store(p, dst, base);
+		// 		} else {
+		// 			i32 field_count = cast(i32)type->Struct.fields.count - 1;
+		// 			for (i32 i = 0; i < field_count; i++) {
+		// 				lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+		// 				lbValue field_src = lb_emit_struct_ev(p, base, i);
+		// 				field_src = lb_emit_ptr_offset(p, field_src, low);
+		// 				lb_emit_store(p, field_dst, field_src);
+		// 			}
+
+
+		// 			lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+		// 			lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+		// 			lb_emit_store(p, len_dst, new_len);
+		// 		}
+		// 	} else if (type->Struct.soa_kind == StructSoa_Dynamic) {
+		// 		i32 field_count = cast(i32)type->Struct.fields.count - 3;
+		// 		for (i32 i = 0; i < field_count; i++) {
+		// 			lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+		// 			lbValue field_src = lb_emit_struct_ev(p, base, i);
+		// 			field_src = lb_emit_ptr_offset(p, field_src, low);
+		// 			lb_emit_store(p, field_dst, field_src);
+		// 		}
+
+
+		// 		lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+		// 		lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+		// 		lb_emit_store(p, len_dst, new_len);
+		// 	}
+
+		// 	return dst;
+		// 	#endif
+		// }
+		GB_PANIC("cg_build_addr_slice_expr Type_Struct");
+		break;
+
+	}
+
+	GB_PANIC("Unknown slicable type");
+	return {};
+}
+
+gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x, Type *type) {
+	switch (op) {
+	case Token_Add:
+		return x;
+	case Token_Not: // Boolean not
+	case Token_Xor: // Bitwise not
+	case Token_Sub: // Number negation
+		break;
+	case Token_Pointer:
+		GB_PANIC("This should be handled elsewhere");
+		break;
+	}
+
+	x = cg_flatten_value(p, x);
+
+	if (is_type_array_like(x.type)) {
+		GB_PANIC("TODO(bill): cg_emit_unary_arith is_type_array_like");
+		// // IMPORTANT TODO(bill): This is very wasteful with regards to stack memory
+		// Type *tl = base_type(x.type);
+		// cgValue val = cg_address_from_load_or_generate_local(p, x);
+		// GB_ASSERT(is_type_array_like(type));
+		// Type *elem_type = base_array_type(type);
+
+		// // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
+		// cgAddr res_addr = cg_add_local(p, type, nullptr, false);
+		// cgValue res = cg_addr_get_ptr(p, res_addr);
+
+		// bool inline_array_arith = cg_can_try_to_inline_array_arith(type);
+
+		// i32 count = cast(i32)get_array_type_count(tl);
+
+		// LLVMTypeRef vector_type = nullptr;
+		// if (op != Token_Not && cg_try_vector_cast(p->module, val, &vector_type)) {
+		// 	LLVMValueRef vp = LLVMBuildPointerCast(p->builder, val.value, LLVMPointerType(vector_type, 0), "");
+		// 	LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, "");
+
+		// 	LLVMValueRef opv = nullptr;
+		// 	switch (op) {
+		// 	case Token_Xor:
+		// 		opv = LLVMBuildNot(p->builder, v, "");
+		// 		break;
+		// 	case Token_Sub:
+		// 		if (is_type_float(elem_type)) {
+		// 			opv = LLVMBuildFNeg(p->builder, v, "");
+		// 		} else {
+		// 			opv = LLVMBuildNeg(p->builder, v, "");
+		// 		}
+		// 		break;
+		// 	}
+
+		// 	if (opv != nullptr) {
+		// 		LLVMSetAlignment(res.value, cast(unsigned)cg_alignof(vector_type));
+		// 		LLVMValueRef res_ptr = LLVMBuildPointerCast(p->builder, res.value, LLVMPointerType(vector_type, 0), "");
+		// 		LLVMBuildStore(p->builder, opv, res_ptr);
+		// 		return cg_emit_conv(p, cg_emit_load(p, res), type);
+		// 	}
+		// }
+
+		// if (inline_array_arith) {
+		// 	// inline
+		// 	for (i32 i = 0; i < count; i++) {
+		// 		cgValue e = cg_emit_load(p, cg_emit_array_epi(p, val, i));
+		// 		cgValue z = cg_emit_unary_arith(p, op, e, elem_type);
+		// 		cg_emit_store(p, cg_emit_array_epi(p, res, i), z);
+		// 	}
+		// } else {
+		// 	auto loop_data = cg_loop_start(p, count, t_i32);
+
+		// 	cgValue e = cg_emit_load(p, cg_emit_array_ep(p, val, loop_data.idx));
+		// 	cgValue z = cg_emit_unary_arith(p, op, e, elem_type);
+		// 	cg_emit_store(p, cg_emit_array_ep(p, res, loop_data.idx), z);
+
+		// 	cg_loop_end(p, loop_data);
+		// }
+		// return cg_emit_load(p, res);
+	}
+
+	if (op == Token_Xor) {
+		GB_ASSERT(x.kind == cgValue_Value);
+		cgValue cmp = cg_value(tb_inst_not(p->func, x.node), x.type);
+		return cg_emit_conv(p, cmp, type);
+	}
+
+	if (op == Token_Not) {
+		TB_Node *zero = cg_const_nil(p, x.type).node;
+		cgValue cmp = cg_value(tb_inst_cmp_ne(p->func, x.node, zero), x.type);
+		return cg_emit_conv(p, cmp, type);
+	}
+
+	if (op == Token_Sub && is_type_integer(type) && is_type_different_to_arch_endianness(type)) {
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		cgValue v = cg_emit_byte_swap(p, x, platform_type);
+
+		cgValue res = cg_value(tb_inst_neg(p->func, v.node), platform_type);
+		return cg_emit_byte_swap(p, res, type);
+	}
+
+	if (op == Token_Sub && is_type_float(type) && is_type_different_to_arch_endianness(type)) {
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		cgValue v = cg_emit_byte_swap(p, x, platform_type);
+
+		cgValue res = cg_value(tb_inst_neg(p->func, v.node), platform_type);
+		return cg_emit_byte_swap(p, res, type);
+	}
+
+	cgValue res = {};
+
+	if (op == Token_Sub) { // Number negation
+		if (is_type_integer(x.type)) {
+			res = cg_value(tb_inst_neg(p->func, x.node), x.type);
+		} else if (is_type_float(x.type)) {
+			res = cg_value(tb_inst_neg(p->func, x.node), x.type);
+		} else if (is_type_complex(x.type)) {
+			GB_PANIC("TODO(bill): neg complex");
+			// LLVMValueRef v0 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 0, ""), "");
+			// LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), "");
+
+			// cgAddr addr = cg_add_local_generated(p, x.type, false);
+			// LLVMTypeRef type = llvm_addr_type(p->module, addr.addr);
+			// LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, ""));
+			// LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, ""));
+			// return cg_addr_load(p, addr);
+
+		} else if (is_type_quaternion(x.type)) {
+			GB_PANIC("TODO(bill): neg quaternion");
+			// LLVMValueRef v0 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 0, ""), "");
+			// LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), "");
+			// LLVMValueRef v2 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 2, ""), "");
+			// LLVMValueRef v3 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 3, ""), "");
+
+			// cgAddr addr = cg_add_local_generated(p, x.type, false);
+			// LLVMTypeRef type = llvm_addr_type(p->module, addr.addr);
+			// LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, ""));
+			// LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, ""));
+			// LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 2, ""));
+			// LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 3, ""));
+			// return cg_addr_load(p, addr);
+		} else if (is_type_simd_vector(x.type)) {
+			GB_PANIC("TODO(bill): neg simd");
+			// Type *elem = base_array_type(x.type);
+			// if (is_type_float(elem)) {
+			// 	res.value = LLVMBuildFNeg(p->builder, x.value, "");
+			// } else {
+			// 	res.value = LLVMBuildNeg(p->builder, x.value, "");
+			// }
+		} else if (is_type_matrix(x.type)) {
+			GB_PANIC("TODO(bill): neg matrix");
+			// cgValue zero = {};
+			// zero.value = LLVMConstNull(cg_type(p->module, type));
+			// zero.type = type;
+			// return cg_emit_arith_matrix(p, Token_Sub, zero, x, type, true);
+		} else {
+			GB_PANIC("Unhandled type %s", type_to_string(x.type));
+		}
+		res.type = x.type;
+		return res;
+	}
+
+	return res;
+}
+
+gb_internal void cg_emit_if(cgProcedure *p, cgValue const &cond, TB_Node *true_region, TB_Node *false_region) {
+	GB_ASSERT(cond.kind == cgValue_Value);
+	tb_inst_if(p->func, cond.node, true_region, false_region);
+}
+
+
+struct cgLoopData {
+	cgAddr  index_addr;
+	cgValue index;
+	TB_Node *body;
+	TB_Node *done;
+	TB_Node *loop;
+};
+
+gb_internal cgLoopData cg_loop_start(cgProcedure *p, isize count, Type *index_type) {
+	cgLoopData data = {};
+
+	cgValue max = cg_const_int(p, index_type, count);
+
+	data.index_addr = cg_add_local(p, index_type, nullptr, true);
+
+	data.body = cg_control_region(p, "loop_body");
+	data.done = cg_control_region(p, "loop_done");
+	data.loop = cg_control_region(p, "loop_loop");
+
+	cg_emit_goto(p, data.loop);
+	tb_inst_set_control(p->func, data.loop);
+
+	data.index = cg_addr_load(p, data.index_addr);
+
+	cgValue cond = cg_emit_comp(p, Token_Lt, data.index, max);
+	cg_emit_if(p, cond, data.body, data.done);
+	tb_inst_set_control(p->func, data.body);
+
+	return data;
+}
+
+gb_internal void cg_loop_end(cgProcedure *p, cgLoopData const &data) {
+	if (data.index_addr.addr.node != nullptr) {
+		cg_emit_increment(p, data.index_addr.addr);
+		cg_emit_goto(p, data.loop);
+		tb_inst_set_control(p->func, data.done);
+	}
+}
+
+
+
+gb_internal void cg_build_try_lhs_rhs(cgProcedure *p, Ast *arg, Type *final_type, cgValue *lhs_, cgValue *rhs_) {
+	cgValue lhs = {};
+	cgValue rhs = {};
+
+	cgValue value = cg_build_expr(p, arg);
+	if (value.kind == cgValue_Multi) {
+		auto const &values = value.multi->values;
+		if (values.count == 2) {
+			lhs = values[0];
+			rhs = values[1];
+		} else {
+			rhs = values[values.count-1];
+			if (values.count > 1) {
+				lhs = cg_value_multi(slice(values, 0, values.count-1), final_type);
+			}
+		}
+	} else {
+		rhs = value;
+	}
+
+	GB_ASSERT(rhs.node != nullptr);
+
+	if (lhs_) *lhs_ = lhs;
+	if (rhs_) *rhs_ = rhs;
+}
+
+gb_internal cgValue cg_emit_try_has_value(cgProcedure *p, cgValue rhs) {
+	cgValue has_value = {};
+	if (is_type_boolean(rhs.type)) {
+		has_value = rhs;
+	} else {
+		GB_ASSERT_MSG(type_has_nil(rhs.type), "%s", type_to_string(rhs.type));
+		has_value = cg_emit_comp_against_nil(p, Token_CmpEq, rhs);
+	}
+	GB_ASSERT(has_value.node != nullptr);
+	return has_value;
+}
+
+gb_internal cgValue cg_build_or_return(cgProcedure *p, Ast *arg, Type *final_type) {
+	cgValue lhs = {};
+	cgValue rhs = {};
+	cg_build_try_lhs_rhs(p, arg, final_type, &lhs, &rhs);
+
+	TB_Node *return_region   = cg_control_region(p, "or_return_return");
+	TB_Node *continue_region = cg_control_region(p, "or_return_continue");
+
+	cgValue cond = cg_emit_try_has_value(p, rhs);
+	cg_emit_if(p, cond, continue_region, return_region);
+	tb_inst_set_control(p->func, return_region);
+	{
+		Type *proc_type = base_type(p->type);
+		Type *results = proc_type->Proc.results;
+		GB_ASSERT(results != nullptr && results->kind == Type_Tuple);
+		TypeTuple *tuple = &results->Tuple;
+
+		GB_ASSERT(tuple->variables.count != 0);
+
+		Entity *end_entity = tuple->variables[tuple->variables.count-1];
+		rhs = cg_emit_conv(p, rhs, end_entity->type);
+		if (p->type->Proc.has_named_results) {
+			GB_ASSERT(end_entity->token.string.len != 0);
+
+			// NOTE(bill): store the named values before returning
+			cgAddr found = map_must_get(&p->variable_map, end_entity);
+			cg_addr_store(p, found, rhs);
+
+			cg_build_return_stmt(p, {});
+		} else {
+			GB_ASSERT(tuple->variables.count == 1);
+			Slice<cgValue> results = {};
+			results.data = &rhs;
+			results.count = 1;;
+			cg_build_return_stmt_internal(p, results);
+		}
+	}
+	tb_inst_set_control(p->func, continue_region);
+	if (final_type != nullptr && !is_type_tuple(final_type)) {
+		return cg_emit_conv(p, lhs, final_type);
+	}
+	return {};
+}
+
+gb_internal cgValue cg_build_or_else(cgProcedure *p, Ast *arg, Ast *else_expr, Type *final_type) {
+	if (arg->state_flags & StateFlag_DirectiveWasFalse) {
+		return cg_build_expr(p, else_expr);
+	}
+
+	cgValue lhs = {};
+	cgValue rhs = {};
+	cg_build_try_lhs_rhs(p, arg, final_type, &lhs, &rhs);
+
+	GB_ASSERT(else_expr != nullptr);
+
+	if (is_diverging_expr(else_expr)) {
+		TB_Node *then  = cg_control_region(p, "or_else_then");
+		TB_Node *else_ = cg_control_region(p, "or_else_else");
+
+		cg_emit_if(p, cg_emit_try_has_value(p, rhs), then, else_);
+		// NOTE(bill): else block needs to be straight afterwards to make sure that the actual value is used
+		// from the then block
+		tb_inst_set_control(p->func, else_);
+
+		cg_build_expr(p, else_expr);
+
+		tb_inst_set_control(p->func, then);
+		return cg_emit_conv(p, lhs, final_type);
+	} else {
+		TB_Node *incoming_values[2] = {};
+		TB_Node *incoming_regions[2] = {};
+
+		TB_Node *then  = cg_control_region(p, "or_else_then");
+		TB_Node *done  = cg_control_region(p, "or_else_done"); // NOTE(bill): Append later
+		TB_Node *else_ = cg_control_region(p, "or_else_else");
+
+		cg_emit_if(p, cg_emit_try_has_value(p, rhs), then, else_);
+		tb_inst_set_control(p->func, then);
+
+		cgValue x = cg_emit_conv(p, lhs, final_type);
+		incoming_values[0] = x.node;
+		incoming_regions[0] = tb_inst_get_control(p->func);
+
+		tb_inst_goto(p->func, done);
+		tb_inst_set_control(p->func, else_);
+
+		cgValue y = cg_emit_conv(p, cg_build_expr(p, else_expr), final_type);
+		incoming_values[1] = y.node;
+		incoming_regions[1] = tb_inst_get_control(p->func);
+
+		tb_inst_goto(p->func, done);
+		tb_inst_set_control(p->func, done);
+
+		GB_ASSERT(x.kind == y.kind);
+		GB_ASSERT(incoming_values[0]->dt.raw == incoming_values[1]->dt.raw);
+		cgValue res = {};
+		res.kind = x.kind;
+		res.type = final_type;
+
+		res.node = tb_inst_incomplete_phi(p->func, incoming_values[0]->dt, done, 2);
+		tb_inst_add_phi_operand(p->func, res.node, incoming_regions[0], incoming_values[0]);
+		tb_inst_add_phi_operand(p->func, res.node, incoming_regions[1], incoming_values[1]);
+		return res;
+	}
+}
+
+
+gb_internal isize cg_control_region_pred_count(TB_Node *region) {
+	GB_ASSERT(region->type == TB_REGION);
+	GB_ASSERT(region->input_count > 0);
+	return region->input_count;
+}
+
+gb_internal cgValue cg_build_logical_binary_expr(cgProcedure *p, TokenKind op, Ast *left, Ast *right, Type *final_type) {
+	TB_Node *rhs  = cg_control_region(p, "logical_cmp_rhs");
+	TB_Node *done = cg_control_region(p, "logical_cmp_done");
+
+	cgValue short_circuit = {};
+	if (op == Token_CmpAnd) {
+		cg_build_cond(p, left, rhs, done);
+		short_circuit = cg_const_bool(p, t_bool, false);
+	} else if (op == Token_CmpOr) {
+		cg_build_cond(p, left, done, rhs);
+		short_circuit = cg_const_bool(p, t_bool, true);
+	}
+
+	if (rhs->input_count == 0) {
+		tb_inst_set_control(p->func, done);
+		return cg_emit_conv(p, short_circuit, final_type);
+	}
+
+	if (done->input_count == 0) {
+		tb_inst_set_control(p->func, rhs);
+		return cg_build_expr(p, right);
+	}
+
+	tb_inst_set_control(p->func, rhs);
+	cgValue edge = cg_build_expr(p, right);
+	TB_Node *edge_region = tb_inst_get_control(p->func);
+
+	tb_inst_goto(p->func, done);
+	tb_inst_set_control(p->func, done);
+
+	TB_DataType dt = edge.node->dt;
+	TB_Node *phi = tb_inst_incomplete_phi(p->func, dt, done, done->input_count);
+	for (size_t i = 0; i < done->input_count; i++) {
+		TB_Node *val = short_circuit.node;
+		TB_Node *region = done->inputs[i];
+		if (region == edge_region) {
+			val = edge.node;
+		}
+		tb_inst_add_phi_operand(p->func, phi, region, val);
+	}
+	return cg_emit_conv(p, cg_value(phi, t_bool), final_type);
+}
+
+
+
+gb_internal cgValue cg_build_binary_expr(cgProcedure *p, Ast *expr) {
+	ast_node(be, BinaryExpr, expr);
+
+	TypeAndValue tv = type_and_value_of_expr(expr);
+
+	if (is_type_matrix(be->left->tav.type) || is_type_matrix(be->right->tav.type)) {
+		cgValue left = cg_build_expr(p, be->left);
+		cgValue right = cg_build_expr(p, be->right);
+		GB_PANIC("TODO(bill): cg_emit_arith_matrix");
+		// return cg_emit_arith_matrix(p, be->op.kind, left, right, default_type(tv.type), false);
+	}
+
+
+	switch (be->op.kind) {
+	case Token_Add:
+	case Token_Sub:
+	case Token_Mul:
+	case Token_Quo:
+	case Token_Mod:
+	case Token_ModMod:
+	case Token_And:
+	case Token_Or:
+	case Token_Xor:
+	case Token_AndNot: {
+		Type *type = default_type(tv.type);
+		cgValue left = cg_build_expr(p, be->left);
+		cgValue right = cg_build_expr(p, be->right);
+		return cg_emit_arith(p, be->op.kind, left, right, type);
+	}
+
+	case Token_Shl:
+	case Token_Shr: {
+		cgValue left, right;
+		Type *type = default_type(tv.type);
+		left = cg_build_expr(p, be->left);
+
+		if (cg_is_expr_untyped_const(be->right)) {
+			// NOTE(bill): RHS shift operands can still be untyped
+			// Just bypass the standard cg_build_expr
+			right = cg_expr_untyped_const_to_typed(p, be->right, type);
+		} else {
+			right = cg_build_expr(p, be->right);
+		}
+		return cg_emit_arith(p, be->op.kind, left, right, type);
+	}
+
+	case Token_CmpEq:
+	case Token_NotEq:
+		if (is_type_untyped_nil(be->right->tav.type)) {
+			// `x == nil` or `x != nil`
+			cgValue left = cg_build_expr(p, be->left);
+			cgValue cmp = cg_emit_comp_against_nil(p, be->op.kind, left);
+			Type *type = default_type(tv.type);
+			return cg_emit_conv(p, cmp, type);
+		} else if (is_type_untyped_nil(be->left->tav.type)) {
+			// `nil == x` or `nil != x`
+			cgValue right = cg_build_expr(p, be->right);
+			cgValue cmp = cg_emit_comp_against_nil(p, be->op.kind, right);
+			Type *type = default_type(tv.type);
+			return cg_emit_conv(p, cmp, type);
+		}/* else if (cg_is_empty_string_constant(be->right)) {
+			// `x == ""` or `x != ""`
+			cgValue s = cg_build_expr(p, be->left);
+			s = cg_emit_conv(p, s, t_string);
+			cgValue len = cg_string_len(p, s);
+			cgValue cmp = cg_emit_comp(p, be->op.kind, len, cg_const_int(p->module, t_int, 0));
+			Type *type = default_type(tv.type);
+			return cg_emit_conv(p, cmp, type);
+		} else if (cg_is_empty_string_constant(be->left)) {
+			// `"" == x` or `"" != x`
+			cgValue s = cg_build_expr(p, be->right);
+			s = cg_emit_conv(p, s, t_string);
+			cgValue len = cg_string_len(p, s);
+			cgValue cmp = cg_emit_comp(p, be->op.kind, len, cg_const_int(p->module, t_int, 0));
+			Type *type = default_type(tv.type);
+			return cg_emit_conv(p, cmp, type);
+		}*/
+		/*fallthrough*/
+	case Token_Lt:
+	case Token_LtEq:
+	case Token_Gt:
+	case Token_GtEq:
+		{
+			cgValue left = {};
+			cgValue right = {};
+
+			if (be->left->tav.mode == Addressing_Type) {
+				left = cg_typeid(p, be->left->tav.type);
+			}
+			if (be->right->tav.mode == Addressing_Type) {
+				right = cg_typeid(p, be->right->tav.type);
+			}
+			if (left.node == nullptr)  left  = cg_build_expr(p, be->left);
+			if (right.node == nullptr) right = cg_build_expr(p, be->right);
+			cgValue cmp = cg_emit_comp(p, be->op.kind, left, right);
+			Type *type = default_type(tv.type);
+			return cg_emit_conv(p, cmp, type);
+		}
+
+	case Token_CmpAnd:
+	case Token_CmpOr:
+		return cg_build_logical_binary_expr(p, be->op.kind, be->left, be->right, tv.type);
+
+	case Token_in:
+	case Token_not_in:
+		{
+			cgValue left = cg_build_expr(p, be->left);
+			cgValue right = cg_build_expr(p, be->right);
+			Type *rt = base_type(right.type);
+			if (is_type_pointer(rt)) {
+				right = cg_emit_load(p, right);
+				rt = base_type(type_deref(rt));
+			}
+
+			switch (rt->kind) {
+			case Type_Map:
+				{
+					GB_PANIC("TODO(bill): in/not_in for maps");
+					// cgValue map_ptr = cg_address_from_load_or_generate_local(p, right);
+					// cgValue key = left;
+					// cgValue ptr = cg_internal_dynamic_map_get_ptr(p, map_ptr, key);
+					// if (be->op.kind == Token_in) {
+					// 	return cg_emit_conv(p, cg_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
+					// } else {
+					// 	return cg_emit_conv(p, cg_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool);
+					// }
+				}
+				break;
+			case Type_BitSet:
+				{
+					Type *key_type = rt->BitSet.elem;
+					GB_ASSERT(are_types_identical(left.type, key_type));
+
+					Type *it = bit_set_to_int(rt);
+					left = cg_emit_conv(p, left, it);
+					if (is_type_different_to_arch_endianness(it)) {
+						left = cg_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
+					}
+
+					cgValue lower = cg_const_value(p, left.type, exact_value_i64(rt->BitSet.lower));
+					cgValue key = cg_emit_arith(p, Token_Sub, left, lower, left.type);
+					cgValue bit = cg_emit_arith(p, Token_Shl, cg_const_int(p, left.type, 1), key, left.type);
+					bit = cg_emit_conv(p, bit, it);
+
+					cgValue old_value = cg_emit_transmute(p, right, it);
+					cgValue new_value = cg_emit_arith(p, Token_And, old_value, bit, it);
+
+					GB_PANIC("TODO(bill): cg_emit_comp");
+					// TokenKind op = (be->op.kind == Token_in) ? Token_NotEq : Token_CmpEq;
+					// return cg_emit_conv(p, cg_emit_comp(p, op, new_value, cg_const_int(p, new_value.type, 0)), t_bool);
+				}
+				break;
+			default:
+				GB_PANIC("Invalid 'in' type");
+			}
+			break;
+		}
+		break;
+	default:
+		GB_PANIC("Invalid binary expression");
+		break;
+	}
+	return {};
+}
+
+gb_internal cgValue cg_build_cond(cgProcedure *p, Ast *cond, TB_Node *true_block, TB_Node *false_block) {
+	cond = unparen_expr(cond);
+
+	GB_ASSERT(cond != nullptr);
+	GB_ASSERT(true_block  != nullptr);
+	GB_ASSERT(false_block != nullptr);
+
+	// Use to signal not to do compile time short circuit for consts
+	cgValue no_comptime_short_circuit = {};
+
+	switch (cond->kind) {
+	case_ast_node(ue, UnaryExpr, cond);
+		if (ue->op.kind == Token_Not) {
+			cgValue cond_val = cg_build_cond(p, ue->expr, false_block, true_block);
+			return cond_val;
+			// if (cond_val.value && LLVMIsConstant(cond_val.value)) {
+			// 	return cg_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
+			// }
+			// return no_comptime_short_circuit;
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, cond);
+		if (be->op.kind == Token_CmpAnd) {
+			TB_Node *block = cg_control_region(p, "cmp_and");
+			cg_build_cond(p, be->left, block, false_block);
+			tb_inst_set_control(p->func, block);
+			cg_build_cond(p, be->right, true_block, false_block);
+			return no_comptime_short_circuit;
+		} else if (be->op.kind == Token_CmpOr) {
+			TB_Node *block = cg_control_region(p, "cmp_or");
+			cg_build_cond(p, be->left, true_block, block);
+			tb_inst_set_control(p->func, block);
+			cg_build_cond(p, be->right, true_block, false_block);
+			return no_comptime_short_circuit;
+		}
+	case_end;
+	}
+
+	cgValue v = {};
+	if (cg_is_expr_untyped_const(cond)) {
+		v = cg_expr_untyped_const_to_typed(p, cond, t_bool);
+	} else {
+		v = cg_build_expr(p, cond);
+	}
+	cg_emit_if(p, v, true_block, false_block);
+	return v;
+}
+
+gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr);
+gb_internal cgValue cg_build_expr(cgProcedure *p, Ast *expr) {
+	cg_set_debug_pos_from_node(p, expr);
+
+	u16 prev_state_flags = p->state_flags;
+	defer (p->state_flags = prev_state_flags);
+
+	if (expr->state_flags != 0) {
+		u16 in = expr->state_flags;
+		u16 out = p->state_flags;
+
+		if (in & StateFlag_bounds_check) {
+			out |= StateFlag_bounds_check;
+			out &= ~StateFlag_no_bounds_check;
+		} else if (in & StateFlag_no_bounds_check) {
+			out |= StateFlag_no_bounds_check;
+			out &= ~StateFlag_bounds_check;
+		}
+
+		if (in & StateFlag_type_assert) {
+			out |= StateFlag_type_assert;
+			out &= ~StateFlag_no_type_assert;
+		} else if (in & StateFlag_no_type_assert) {
+			out |= StateFlag_no_type_assert;
+			out &= ~StateFlag_type_assert;
+		}
+
+		p->state_flags = out;
+	}
+
+
+	// IMPORTANT NOTE(bill):
+	// Selector Call Expressions (foo->bar(...))
+	// must only evaluate `foo` once as it gets transformed into
+	// `foo.bar(foo, ...)`
+	// And if `foo` is a procedure call or something more complex, storing the value
+	// once is a very good idea
+	// If a stored value is found, it must be removed from the cache
+	if (expr->state_flags & StateFlag_SelectorCallExpr) {
+		// cgValue *pp = map_get(&p->selector_values, expr);
+		// if (pp != nullptr) {
+		// 	cgValue res = *pp;
+		// 	map_remove(&p->selector_values, expr);
+		// 	return res;
+		// }
+		// cgAddr *pa = map_get(&p->selector_addr, expr);
+		// if (pa != nullptr) {
+		// 	cgAddr res = *pa;
+		// 	map_remove(&p->selector_addr, expr);
+		// 	return cg_addr_load(p, res);
+		// }
+	}
+
+	cgValue res = cg_build_expr_internal(p, expr);
+	if (res.kind == cgValue_Symbol) {
+		GB_ASSERT(is_type_internally_pointer_like(res.type));
+		res = cg_value(tb_inst_get_symbol_address(p->func, res.symbol), res.type);
+	}
+
+	if (expr->state_flags & StateFlag_SelectorCallExpr) {
+		// map_set(&p->selector_values, expr, res);
+	}
+	return res;
+}
+
+
+gb_internal cgValue cg_find_ident(cgProcedure *p, Entity *e, Ast *expr) {
+	cgAddr *found_addr = map_get(&p->variable_map, e);
+	if (found_addr) {
+		return cg_addr_load(p, *found_addr);
+	}
+
+	cgValue *found = nullptr;
+	rw_mutex_shared_lock(&p->module->values_mutex);
+	found = map_get(&p->module->values, e);
+	rw_mutex_shared_unlock(&p->module->values_mutex);
+
+	if (found) {
+
+		auto v = *found;
+		// NOTE(bill): This is because pointers are already pointers in LLVM
+		if (is_type_proc(v.type)) {
+			return v;
+		}
+		return cg_emit_load(p, v);
+	} else if (e != nullptr && e->kind == Entity_Variable) {
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	}
+
+	if (e->kind == Entity_Procedure) {
+		return cg_find_procedure_value_from_entity(p->module, e);
+	}
+
+	String pkg = {};
+	if (e->pkg) {
+		pkg = e->pkg->name;
+	}
+	gb_printf_err("Error in: %s\n", token_pos_to_string(ast_token(expr).pos));
+	GB_PANIC("nullptr value for expression from identifier: %.*s.%.*s (%p) : %s @ %p", LIT(pkg), LIT(e->token.string), e, type_to_string(e->type), expr);
+	return {};
+}
+
+cgAddr cg_build_addr_compound_lit(cgProcedure *p, Ast *expr) {
+	struct cgCompoundLitElemTempData {
+		Ast *   expr;
+		cgValue value;
+		i64     elem_index;
+		i64     elem_length;
+		cgValue gep;
+	};
+
+
+	auto const &populate = [](cgProcedure *p, Slice<Ast *> const &elems, Array<cgCompoundLitElemTempData> *temp_data, Type *compound_type) {
+		Type *bt = base_type(compound_type);
+		Type *et = nullptr;
+		switch (bt->kind) {
+		case Type_Array:           et = bt->Array.elem;           break;
+		case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
+		case Type_Slice:           et = bt->Slice.elem;           break;
+		case Type_BitSet:          et = bt->BitSet.elem;          break;
+		case Type_DynamicArray:    et = bt->DynamicArray.elem;    break;
+		case Type_SimdVector:      et = bt->SimdVector.elem;      break;
+		case Type_Matrix:          et = bt->Matrix.elem;          break;
+		}
+		GB_ASSERT(et != nullptr);
+
+
+		// NOTE(bill): Separate value, gep, store into their own chunks
+		for_array(i, elems) {
+			Ast *elem = elems[i];
+			if (elem->kind == Ast_FieldValue) {
+				ast_node(fv, FieldValue, elem);
+				if (is_ast_range(fv->field)) {
+					ast_node(ie, BinaryExpr, fv->field);
+					TypeAndValue lo_tav = ie->left->tav;
+					TypeAndValue hi_tav = ie->right->tav;
+					GB_ASSERT(lo_tav.mode == Addressing_Constant);
+					GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+					TokenKind op = ie->op.kind;
+					i64 lo = exact_value_to_i64(lo_tav.value);
+					i64 hi = exact_value_to_i64(hi_tav.value);
+					if (op != Token_RangeHalf) {
+						hi += 1;
+					}
+
+					cgValue value = cg_emit_conv(p, cg_build_expr(p, fv->value), et);
+
+					GB_ASSERT((hi-lo) > 0);
+
+					if (bt->kind == Type_Matrix) {
+						GB_PANIC("TODO(bill): Type_Matrix");
+						// for (i64 k = lo; k < hi; k++) {
+						// 	cgCompoundLitElemTempData data = {};
+						// 	data.value = value;
+
+						// 	data.elem_index = matrix_row_major_index_to_offset(bt, k);
+						// 	array_add(temp_data, data);
+						// }
+					} else {
+						enum {MAX_ELEMENT_AMOUNT = 32};
+						if ((hi-lo) <= MAX_ELEMENT_AMOUNT) {
+							for (i64 k = lo; k < hi; k++) {
+								cgCompoundLitElemTempData data = {};
+								data.value = value;
+								data.elem_index = k;
+								array_add(temp_data, data);
+							}
+						} else {
+							cgCompoundLitElemTempData data = {};
+							data.value = value;
+							data.elem_index = lo;
+							data.elem_length = hi-lo;
+							array_add(temp_data, data);
+						}
+					}
+				} else {
+					auto tav = fv->field->tav;
+					GB_ASSERT(tav.mode == Addressing_Constant);
+					i64 index = exact_value_to_i64(tav.value);
+
+					cgValue value = cg_emit_conv(p, cg_build_expr(p, fv->value), et);
+					GB_ASSERT(!is_type_tuple(value.type));
+
+					cgCompoundLitElemTempData data = {};
+					data.value = value;
+					data.expr = fv->value;
+					if (bt->kind == Type_Matrix) {
+						GB_PANIC("TODO(bill): Type_Matrix");
+						// data.elem_index = matrix_row_major_index_to_offset(bt, index);
+					} else {
+						data.elem_index = index;
+					}
+					array_add(temp_data, data);
+				}
+
+			} else {
+				// if (bt->kind != Type_DynamicArray && lb_is_elem_const(elem, et)) {
+					// continue;
+				// }
+
+				cgValue field_expr = cg_build_expr(p, elem);
+				GB_ASSERT(!is_type_tuple(field_expr.type));
+
+				cgValue ev = cg_emit_conv(p, field_expr, et);
+
+				cgCompoundLitElemTempData data = {};
+				data.value = ev;
+				if (bt->kind == Type_Matrix) {
+						GB_PANIC("TODO(bill): Type_Matrix");
+					// data.elem_index = matrix_row_major_index_to_offset(bt, i);
+				} else {
+					data.elem_index = i;
+				}
+				array_add(temp_data, data);
+			}
+		}
+	};
+
+	auto const &assign_array = [](cgProcedure *p, Array<cgCompoundLitElemTempData> const &temp_data) {
+		for (auto const &td : temp_data) if (td.value.node != nullptr) {
+			if (td.elem_length > 0) {
+				GB_PANIC("TODO(bill): range");
+				// auto loop_data = cg_loop_start(p, cast(isize)td.elem_length, t_i32);
+				// {
+				// 	cgValue dst = td.gep;
+				// 	dst = cg_emit_ptr_offset(p, dst, loop_data.idx);
+				// 	cg_emit_store(p, dst, td.value);
+				// }
+				// cg_loop_end(p, loop_data);
+			} else {
+				cg_emit_store(p, td.gep, td.value);
+			}
+		}
+	};
+
+
+
+	ast_node(cl, CompoundLit, expr);
+
+	Type *type = type_of_expr(expr);
+	Type *bt = base_type(type);
+
+	cgAddr v = {};
+	if (p->is_startup) {
+		v = cg_add_global(p, type, nullptr);
+	} else {
+		v = cg_add_local(p, type, nullptr, true);
+	}
+
+	if (cl->elems.count == 0) {
+		// No need to create it
+		return v;
+	}
+
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	Type *et = nullptr;
+	switch (bt->kind) {
+	case Type_Array:           et = bt->Array.elem;           break;
+	case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
+	case Type_Slice:           et = bt->Slice.elem;           break;
+	case Type_BitSet:          et = bt->BitSet.elem;          break;
+	case Type_SimdVector:      et = bt->SimdVector.elem;      break;
+	case Type_Matrix:          et = bt->Matrix.elem;          break;
+	}
+
+	String proc_name = {};
+	if (p->entity) {
+		proc_name = p->entity->token.string;
+	}
+	TokenPos pos = ast_token(expr).pos;
+
+
+	switch (bt->kind) {
+	default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
+
+	case Type_Struct: {
+		TypeStruct *st = &bt->Struct;
+		cgValue comp_lit_ptr = cg_addr_get_ptr(p, v);
+
+		for_array(field_index, cl->elems) {
+			Ast *elem = cl->elems[field_index];
+
+			cgValue field_expr = {};
+			Entity *field = nullptr;
+			isize index = field_index;
+
+			if (elem->kind == Ast_FieldValue) {
+				ast_node(fv, FieldValue, elem);
+				String name = fv->field->Ident.token.string;
+				Selection sel = lookup_field(bt, name, false);
+				GB_ASSERT(!sel.indirect);
+
+				elem = fv->value;
+				if (sel.index.count > 1) {
+					cgValue dst = cg_emit_deep_field_gep(p, comp_lit_ptr, sel);
+					field_expr = cg_build_expr(p, elem);
+					field_expr = cg_emit_conv(p, field_expr, sel.entity->type);
+					cg_emit_store(p, dst, field_expr);
+					continue;
+				}
+
+				index = sel.index[0];
+			} else {
+				Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index);
+				GB_ASSERT(sel.index.count == 1);
+				GB_ASSERT(!sel.indirect);
+				index = sel.index[0];
+			}
+
+			field = st->fields[index];
+			Type *ft = field->type;
+
+			field_expr = cg_build_expr(p, elem);
+
+			cgValue gep = {};
+			if (st->is_raw_union) {
+				gep = cg_emit_conv(p, comp_lit_ptr, alloc_type_pointer(ft));
+			} else {
+				gep = cg_emit_struct_ep(p, comp_lit_ptr, cast(i32)index);
+			}
+
+			Type *fet = field_expr.type;
+			GB_ASSERT(fet->kind != Type_Tuple);
+
+			// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
+			if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
+				GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet));
+
+				GB_PANIC("TODO(bill): cg_emit_store_union_variant");
+				// cg_emit_store_union_variant(p, gep, field_expr, fet);
+			} else {
+				cgValue fv = cg_emit_conv(p, field_expr, ft);
+				cg_emit_store(p, gep, fv);
+			}
+		}
+		return v;
+	}
+
+	case Type_Map: {
+		GB_ASSERT(!build_context.no_dynamic_literals);
+		GB_PANIC("TODO(bill): map literals");
+
+		// cgValue err = cg_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
+		// gb_unused(err);
+
+		// for (Ast *elem : cl->elems) {
+		// 	ast_node(fv, FieldValue, elem);
+
+		// 	cgValue key   = cg_build_expr(p, fv->field);
+		// 	cgValue value = cg_build_expr(p, fv->value);
+		// 	cg_internal_dynamic_map_set(p, v.addr, type, key, value, elem);
+		// }
+		break;
+	}
+
+	case Type_Array: {
+		auto temp_data = array_make<cgCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+		populate(p, cl->elems, &temp_data, type);
+
+		cgValue dst_ptr = cg_addr_get_ptr(p, v);
+		for_array(i, temp_data) {
+			i32 index = cast(i32)(temp_data[i].elem_index);
+			temp_data[i].gep = cg_emit_array_epi(p, dst_ptr, index);
+		}
+
+		assign_array(p, temp_data);
+		break;
+	}
+	case Type_EnumeratedArray: {
+		auto temp_data = array_make<cgCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+		populate(p, cl->elems, &temp_data, type);
+
+		cgValue dst_ptr = cg_addr_get_ptr(p, v);
+		i64 index_offset = exact_value_to_i64(*bt->EnumeratedArray.min_value);
+		for_array(i, temp_data) {
+			i32 index = cast(i32)(temp_data[i].elem_index - index_offset);
+			temp_data[i].gep = cg_emit_array_epi(p, dst_ptr, index);
+		}
+
+		assign_array(p, temp_data);
+		break;
+	}
+	case Type_Slice: {
+		isize count = gb_max(cl->elems.count, cl->max_count);
+
+		TB_CharUnits backing_size = cast(TB_CharUnits)(type_size_of(bt->Slice.elem) * count);
+		TB_CharUnits align = cast(TB_CharUnits)type_align_of(bt->Slice.elem);
+
+		TB_Node *backing = nullptr;
+		if (p->is_startup) {
+			TB_Global *global = tb_global_create(p->module->mod, 0, "", nullptr, TB_LINKAGE_PRIVATE);
+			tb_global_set_storage(p->module->mod, tb_module_get_data(p->module->mod), global, backing_size, align, 0);
+			backing = tb_inst_get_symbol_address(p->func, cast(TB_Symbol *)global);
+		} else {
+			backing = tb_inst_local(p->func, backing_size, align);
+		}
+
+		cgValue data = cg_value(backing, alloc_type_multi_pointer(bt->Slice.elem));
+
+		auto temp_data = array_make<cgCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+		populate(p, cl->elems, &temp_data, type);
+
+
+		for_array(i, temp_data) {
+			temp_data[i].gep = cg_emit_ptr_offset(p, data, cg_const_int(p, t_int, temp_data[i].elem_index));
+		}
+
+		assign_array(p, temp_data);
+		cg_fill_slice(p, v, data, cg_const_int(p, t_int, count));
+		return v;
+	}
+
+	case Type_DynamicArray: {
+		GB_ASSERT(!build_context.no_dynamic_literals);
+
+		Type *et = bt->DynamicArray.elem;
+		cgValue size  = cg_const_int(p, t_int, type_size_of(et));
+		cgValue align = cg_const_int(p, t_int, type_align_of(et));
+
+		i64 item_count = gb_max(cl->max_count, cl->elems.count);
+		{
+
+			auto args = slice_make<cgValue>(temporary_allocator(), 5);
+			args[0] = cg_emit_conv(p, cg_addr_get_ptr(p, v), t_rawptr);
+			args[1] = size;
+			args[2] = align;
+			args[3] = cg_const_int(p, t_int, item_count);
+			args[4] = cg_emit_source_code_location_as_global(p, proc_name, pos);
+			cg_emit_runtime_call(p, "__dynamic_array_reserve", args);
+		}
+
+		Type *array_type = alloc_type_array(et, item_count);
+		cgAddr items_addr = cg_add_local(p, array_type, nullptr, true);
+		cgValue items = cg_addr_get_ptr(p, items_addr);
+
+		auto temp_data = array_make<cgCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+		populate(p, cl->elems, &temp_data, type);
+
+		for_array(i, temp_data) {
+			temp_data[i].gep = cg_emit_array_epi(p, items, temp_data[i].elem_index);
+		}
+		assign_array(p, temp_data);
+
+		{
+			auto args = slice_make<cgValue>(temporary_allocator(), 6);
+			args[0] = cg_emit_conv(p, v.addr, t_rawptr);
+			args[1] = size;
+			args[2] = align;
+			args[3] = cg_emit_conv(p, items, t_rawptr);
+			args[4] = cg_const_int(p, t_int, item_count);
+			args[5] = cg_emit_source_code_location_as_global(p, proc_name, pos);
+			cg_emit_runtime_call(p, "__dynamic_array_append", args);
+		}
+		break;
+	}
+
+	case Type_Basic: {
+		GB_ASSERT(is_type_any(bt));
+		String field_names[2] = {
+			str_lit("data"),
+			str_lit("id"),
+		};
+		Type *field_types[2] = {
+			t_rawptr,
+			t_typeid,
+		};
+
+		for_array(field_index, cl->elems) {
+			Ast *elem = cl->elems[field_index];
+
+			cgValue field_expr = {};
+			isize index = field_index;
+
+			if (elem->kind == Ast_FieldValue) {
+				ast_node(fv, FieldValue, elem);
+				Selection sel = lookup_field(bt, fv->field->Ident.token.string, false);
+				index = sel.index[0];
+				elem = fv->value;
+			} else {
+				TypeAndValue tav = type_and_value_of_expr(elem);
+				Selection sel = lookup_field(bt, field_names[field_index], false);
+				index = sel.index[0];
+			}
+
+			field_expr = cg_build_expr(p, elem);
+
+			GB_ASSERT(field_expr.type->kind != Type_Tuple);
+
+			Type *ft = field_types[index];
+			cgValue fv = cg_emit_conv(p, field_expr, ft);
+			cgValue gep = cg_emit_struct_ep(p, cg_addr_get_ptr(p, v), index);
+			cg_emit_store(p, gep, fv);
+		}
+		break;
+	}
+
+	case Type_BitSet: {
+		i64 sz = type_size_of(type);
+		if (sz == 0) {
+			return v;
+		}
+		cgValue lower = cg_const_value(p, t_int, exact_value_i64(bt->BitSet.lower));
+		Type *it = bit_set_to_int(bt);
+		cgValue one = cg_const_value(p, it, exact_value_i64(1));
+		for (Ast *elem : cl->elems) {
+			GB_ASSERT(elem->kind != Ast_FieldValue);
+
+			cgValue expr = cg_build_expr(p, elem);
+			GB_ASSERT(expr.type->kind != Type_Tuple);
+
+			cgValue e = cg_emit_conv(p, expr, it);
+			e = cg_emit_arith(p, Token_Sub, e, lower, it);
+			e = cg_emit_arith(p, Token_Shl, one, e, it);
+
+			cgValue old_value = cg_emit_transmute(p, cg_addr_load(p, v), it);
+			cgValue new_value = cg_emit_arith(p, Token_Or, old_value, e, it);
+			new_value = cg_emit_transmute(p, new_value, type);
+			cg_addr_store(p, v, new_value);
+		}
+		return v;
+	}
+
+	case Type_Matrix: {
+		auto temp_data = array_make<cgCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+		populate(p, cl->elems, &temp_data, type);
+
+		cgValue dst_ptr = cg_addr_get_ptr(p, v);
+		for_array(i, temp_data) {
+			temp_data[i].gep = cg_emit_array_epi(p, dst_ptr, temp_data[i].elem_index);
+		}
+
+		assign_array(p, temp_data);
+		break;
+	}
+
+	case Type_SimdVector: {
+		// auto temp_data = array_make<cgCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
+
+		// populate(p, cl->elems, &temp_data, type);
+
+		// // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
+		// // might be a better option
+		// for (auto const &td : temp_data) if (td.value.node != nullptr) {
+		// 	if (td.elem_length > 0) {
+		// 		for (i64 k = 0; k < td.elem_length; k++) {
+		// 			LLVMValueRef index = cg_const_int(p->module, t_u32, td.elem_index + k).value;
+		// 			vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, td.value.value, index, "");
+		// 		}
+		// 	} else {
+		// 		LLVMValueRef index = cg_const_int(p->module, t_u32, td.elem_index).value;
+		// 		vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, td.value.value, index, "");
+
+		// 	}
+		// }
+		break;
+	}
+	}
+
+	return v;
+}
+
+gb_internal cgValue cg_build_unary_and(cgProcedure *p, Ast *expr) {
+	ast_node(ue, UnaryExpr, expr);
+	auto tv = type_and_value_of_expr(expr);
+
+
+	Ast *ue_expr = unparen_expr(ue->expr);
+	if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_type_tuple(tv.type)) {
+		GB_PANIC("TODO(bill): &m[k]");
+		// Type *tuple = tv.type;
+
+		// Type *map_type = type_of_expr(ue_expr->IndexExpr.expr);
+		// Type *ot = base_type(map_type);
+		// Type *t = base_type(type_deref(ot));
+		// bool deref = t != ot;
+		// GB_ASSERT(t->kind == Type_Map);
+		// ast_node(ie, IndexExpr, ue_expr);
+
+		// cgValue map_val = cg_build_addr_ptr(p, ie->expr);
+		// if (deref) {
+		// 	map_val = cg_emit_load(p, map_val);
+		// }
+
+		// cgValue key = lb_build_expr(p, ie->index);
+		// key = lb_emit_conv(p, key, t->Map.key);
+
+		// lbAddr addr = lb_addr_map(map_val, key, t, alloc_type_pointer(t->Map.value));
+		// lbValue ptr = lb_addr_get_ptr(p, addr);
+
+		// lbValue ok = lb_emit_comp_against_nil(p, Token_NotEq, ptr);
+		// ok = lb_emit_conv(p, ok, tuple->Tuple.variables[1]->type);
+
+		// lbAddr res = lb_add_local_generated(p, tuple, false);
+		// lbValue gep0 = lb_emit_struct_ep(p, res.addr, 0);
+		// lbValue gep1 = lb_emit_struct_ep(p, res.addr, 1);
+		// lb_emit_store(p, gep0, ptr);
+		// lb_emit_store(p, gep1, ok);
+		// return lb_addr_load(p, res);
+
+	} else if (is_type_soa_pointer(tv.type)) {
+		GB_PANIC("TODO(bill): &soa[i]");
+		// ast_node(ie, IndexExpr, ue_expr);
+		// lbValue addr = lb_build_addr_ptr(p, ie->expr);
+		// lbValue index = lb_build_expr(p, ie->index);
+
+		// if (!build_context.no_bounds_check) {
+		// 	// TODO(bill): soa bounds checking
+		// }
+
+		// return lb_make_soa_pointer(p, tv.type, addr, index);
+	} else if (ue_expr->kind == Ast_CompoundLit) {
+		cgAddr addr = cg_build_addr_compound_lit(p, expr);
+		return addr.addr;
+	} else if (ue_expr->kind == Ast_TypeAssertion) {
+		GB_PANIC("TODO(bill): &v.(T)");
+		// if (is_type_tuple(tv.type)) {
+		// 	Type *tuple = tv.type;
+		// 	Type *ptr_type = tuple->Tuple.variables[0]->type;
+		// 	Type *ok_type = tuple->Tuple.variables[1]->type;
+
+		// 	ast_node(ta, TypeAssertion, ue_expr);
+		// 	TokenPos pos = ast_token(expr).pos;
+		// 	Type *type = type_of_expr(ue_expr);
+		// 	GB_ASSERT(!is_type_tuple(type));
+
+		// 	lbValue e = lb_build_expr(p, ta->expr);
+		// 	Type *t = type_deref(e.type);
+		// 	if (is_type_union(t)) {
+		// 		lbValue v = e;
+		// 		if (!is_type_pointer(v.type)) {
+		// 			v = lb_address_from_load_or_generate_local(p, v);
+		// 		}
+		// 		Type *src_type = type_deref(v.type);
+		// 		Type *dst_type = type;
+
+		// 		lbValue src_tag = {};
+		// 		lbValue dst_tag = {};
+		// 		if (is_type_union_maybe_pointer(src_type)) {
+		// 			src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+		// 			dst_tag = lb_const_bool(p->module, t_bool, true);
+		// 		} else {
+		// 			src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+		// 			dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+		// 		}
+
+		// 		lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+
+		// 		lbValue data_ptr = lb_emit_conv(p, v, ptr_type);
+		// 		lbAddr res = lb_add_local_generated(p, tuple, true);
+		// 		lbValue gep0 = lb_emit_struct_ep(p, res.addr, 0);
+		// 		lbValue gep1 = lb_emit_struct_ep(p, res.addr, 1);
+		// 		lb_emit_store(p, gep0, lb_emit_select(p, ok, data_ptr, lb_const_nil(p->module, ptr_type)));
+		// 		lb_emit_store(p, gep1, lb_emit_conv(p, ok, ok_type));
+		// 		return lb_addr_load(p, res);
+		// 	} else if (is_type_any(t)) {
+		// 		lbValue v = e;
+		// 		if (is_type_pointer(v.type)) {
+		// 			v = lb_emit_load(p, v);
+		// 		}
+
+		// 		lbValue data_ptr = lb_emit_conv(p, lb_emit_struct_ev(p, v, 0), ptr_type);
+		// 		lbValue any_id = lb_emit_struct_ev(p, v, 1);
+		// 		lbValue id = lb_typeid(p->module, type);
+
+		// 		lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+
+		// 		lbAddr res = lb_add_local_generated(p, tuple, false);
+		// 		lbValue gep0 = lb_emit_struct_ep(p, res.addr, 0);
+		// 		lbValue gep1 = lb_emit_struct_ep(p, res.addr, 1);
+		// 		lb_emit_store(p, gep0, lb_emit_select(p, ok, data_ptr, lb_const_nil(p->module, ptr_type)));
+		// 		lb_emit_store(p, gep1, lb_emit_conv(p, ok, ok_type));
+		// 		return lb_addr_load(p, res);
+		// 	} else {
+		// 		GB_PANIC("TODO(bill): type assertion %s", type_to_string(type));
+		// 	}
+
+		// } else {
+		// 	GB_ASSERT(is_type_pointer(tv.type));
+
+		// 	ast_node(ta, TypeAssertion, ue_expr);
+		// 	TokenPos pos = ast_token(expr).pos;
+		// 	Type *type = type_of_expr(ue_expr);
+		// 	GB_ASSERT(!is_type_tuple(type));
+
+		// 	lbValue e = lb_build_expr(p, ta->expr);
+		// 	Type *t = type_deref(e.type);
+		// 	if (is_type_union(t)) {
+		// 		lbValue v = e;
+		// 		if (!is_type_pointer(v.type)) {
+		// 			v = lb_address_from_load_or_generate_local(p, v);
+		// 		}
+		// 		Type *src_type = type_deref(v.type);
+		// 		Type *dst_type = type;
+
+
+		// 		if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+		// 			lbValue src_tag = {};
+		// 			lbValue dst_tag = {};
+		// 			if (is_type_union_maybe_pointer(src_type)) {
+		// 				src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+		// 				dst_tag = lb_const_bool(p->module, t_bool, true);
+		// 			} else {
+		// 				src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+		// 				dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+		// 			}
+
+
+		// 			isize arg_count = 6;
+		// 			if (build_context.no_rtti) {
+		// 				arg_count = 4;
+		// 			}
+
+		// 			lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+		// 			auto args = array_make<lbValue>(permanent_allocator(), arg_count);
+		// 			args[0] = ok;
+
+		// 			args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+		// 			args[2] = lb_const_int(p->module, t_i32, pos.line);
+		// 			args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+		// 			if (!build_context.no_rtti) {
+		// 				args[4] = lb_typeid(p->module, src_type);
+		// 				args[5] = lb_typeid(p->module, dst_type);
+		// 			}
+		// 			lb_emit_runtime_call(p, "type_assertion_check", args);
+		// 		}
+
+		// 		lbValue data_ptr = v;
+		// 		return lb_emit_conv(p, data_ptr, tv.type);
+		// 	} else if (is_type_any(t)) {
+		// 		lbValue v = e;
+		// 		if (is_type_pointer(v.type)) {
+		// 			v = lb_emit_load(p, v);
+		// 		}
+		// 		lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
+		// 		if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+		// 			GB_ASSERT(!build_context.no_rtti);
+
+		// 			lbValue any_id = lb_emit_struct_ev(p, v, 1);
+
+		// 			lbValue id = lb_typeid(p->module, type);
+		// 			lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+		// 			auto args = array_make<lbValue>(permanent_allocator(), 6);
+		// 			args[0] = ok;
+
+		// 			args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+		// 			args[2] = lb_const_int(p->module, t_i32, pos.line);
+		// 			args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+		// 			args[4] = any_id;
+		// 			args[5] = id;
+		// 			lb_emit_runtime_call(p, "type_assertion_check", args);
+		// 		}
+
+		// 		return lb_emit_conv(p, data_ptr, tv.type);
+		// 	} else {
+		// 		GB_PANIC("TODO(bill): type assertion %s", type_to_string(type));
+		// 	}
+		// }
+	}
+
+	return cg_build_addr_ptr(p, ue->expr);
+}
+
+gb_internal cgValue cg_emit_cast_union(cgProcedure *p, cgValue value, Type *type, TokenPos pos) {
+	Type *src_type = value.type;
+	bool is_ptr = is_type_pointer(src_type);
+
+	bool is_tuple = true;
+	Type *tuple = type;
+	if (type->kind != Type_Tuple) {
+		is_tuple = false;
+		tuple = make_optional_ok_type(type);
+	}
+
+
+	if (is_ptr) {
+		value = cg_emit_load(p, value);
+	}
+	Type *src = base_type(type_deref(src_type));
+	GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type));
+	Type *dst = tuple->Tuple.variables[0]->type;
+
+	cgValue value_  = cg_address_from_load_or_generate_local(p, value);
+
+	if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+		// just do a bit cast of the data at the front
+		cgValue ptr = cg_emit_conv(p, value_, alloc_type_pointer(type));
+		return cg_emit_load(p, ptr);
+	}
+
+
+	cgValue tag = {};
+	cgValue dst_tag = {};
+	cgValue cond = {};
+	cgValue data = {};
+
+	cgValue gep0 = cg_add_local(p, tuple->Tuple.variables[0]->type, nullptr, true).addr;
+	cgValue gep1 = cg_add_local(p, tuple->Tuple.variables[1]->type, nullptr, true).addr;
+
+	if (is_type_union_maybe_pointer(src)) {
+		data = cg_emit_load(p, cg_emit_conv(p, value_, gep0.type));
+	} else {
+		tag     = cg_emit_load(p, cg_emit_union_tag_ptr(p, value_));
+		dst_tag = cg_const_union_tag(p, src, dst);
+	}
+
+	TB_Node *ok_block  = cg_control_region(p, "union_cast_ok");
+	TB_Node *end_block = cg_control_region(p, "union_cast_end");
+
+	if (data.node != nullptr) {
+		GB_ASSERT(is_type_union_maybe_pointer(src));
+		cond = cg_emit_comp_against_nil(p, Token_NotEq, data);
+	} else {
+		cond = cg_emit_comp(p, Token_CmpEq, tag, dst_tag);
+	}
+
+	cg_emit_if(p, cond, ok_block, end_block);
+	tb_inst_set_control(p->func, ok_block);
+
+	if (data.node == nullptr) {
+		data = cg_emit_load(p, cg_emit_conv(p, value_, gep0.type));
+	}
+	cg_emit_store(p, gep0, data);
+	cg_emit_store(p, gep1, cg_const_bool(p, t_bool, true));
+
+	cg_emit_goto(p, end_block);
+	tb_inst_set_control(p->func, end_block);
+
+	if (!is_tuple) {
+		GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+		// NOTE(bill): Panic on invalid conversion
+		Type *dst_type = tuple->Tuple.variables[0]->type;
+
+		isize arg_count = 7;
+		if (build_context.no_rtti) {
+			arg_count = 4;
+		}
+
+		cgValue ok = cg_emit_load(p, gep1);
+		auto args = slice_make<cgValue>(permanent_allocator(), arg_count);
+		args[0] = ok;
+
+		args[1] = cg_const_string(p, t_string, get_file_path_string(pos.file_id));
+		args[2] = cg_const_int(p, t_i32, pos.line);
+		args[3] = cg_const_int(p, t_i32, pos.column);
+
+		if (!build_context.no_rtti) {
+			args[4] = cg_typeid(p, src_type);
+			args[5] = cg_typeid(p, dst_type);
+			args[6] = cg_emit_conv(p, value_, t_rawptr);
+		}
+		cg_emit_runtime_call(p, "type_assertion_check2", args);
+
+		return cg_emit_load(p, gep0);
+	}
+
+	return cg_value_multi2(cg_emit_load(p, gep0), cg_emit_load(p, gep1), tuple);
+}
+
+gb_internal cgValue cg_emit_cast_any(cgProcedure *p, cgValue value, Type *type, TokenPos pos) {
+	Type *src_type = value.type;
+
+	if (is_type_pointer(src_type)) {
+		value = cg_emit_load(p, value);
+	}
+
+	bool is_tuple = true;
+	Type *tuple = type;
+	if (type->kind != Type_Tuple) {
+		is_tuple = false;
+		tuple = make_optional_ok_type(type);
+	}
+	Type *dst_type = tuple->Tuple.variables[0]->type;
+
+	if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+		// just do a bit cast of the data at the front
+		cgValue ptr = cg_emit_struct_ev(p, value, 0);
+		ptr = cg_emit_conv(p, ptr, alloc_type_pointer(type));
+		return cg_emit_load(p, ptr);
+	}
+
+	cgValue dst_typeid = cg_typeid(p, dst_type);
+	cgValue any_typeid = cg_emit_struct_ev(p, value, 1);
+
+
+	TB_Node *ok_block = cg_control_region(p, "any_cast_ok");
+	TB_Node *end_block = cg_control_region(p, "any_cast_end");
+	cgValue cond = cg_emit_comp(p, Token_CmpEq, any_typeid, dst_typeid);
+	cg_emit_if(p, cond, ok_block, end_block);
+	tb_inst_set_control(p->func, ok_block);
+
+	cgValue gep0 = cg_add_local(p, tuple->Tuple.variables[0]->type, nullptr, true).addr;
+	cgValue gep1 = cg_add_local(p, tuple->Tuple.variables[1]->type, nullptr, true).addr;
+
+	cgValue any_data = cg_emit_struct_ev(p, value, 0);
+	cgValue ptr = cg_emit_conv(p, any_data, alloc_type_pointer(dst_type));
+	cg_emit_store(p, gep0, cg_emit_load(p, ptr));
+	cg_emit_store(p, gep1, cg_const_bool(p, t_bool, true));
+
+	cg_emit_goto(p, end_block);
+	tb_inst_set_control(p->func, end_block);
+
+	if (!is_tuple) {
+		// NOTE(bill): Panic on invalid conversion
+		cgValue ok = cg_emit_load(p, gep1);
+
+		isize arg_count = 7;
+		if (build_context.no_rtti) {
+			arg_count = 4;
+		}
+		auto args = slice_make<cgValue>(permanent_allocator(), arg_count);
+		args[0] = ok;
+
+		args[1] = cg_const_string(p, t_string, get_file_path_string(pos.file_id));
+		args[2] = cg_const_int(p, t_i32, pos.line);
+		args[3] = cg_const_int(p, t_i32, pos.column);
+
+		if (!build_context.no_rtti) {
+			args[4] = any_typeid;
+			args[5] = dst_typeid;
+			args[6] = cg_emit_struct_ev(p, value, 0);
+		}
+		cg_emit_runtime_call(p, "type_assertion_check2", args);
+
+		return cg_emit_load(p, gep0);
+	}
+
+	return cg_value_multi2(cg_emit_load(p, gep0), cg_emit_load(p, gep1), tuple);
+}
+
+
+gb_internal cgValue cg_build_type_assertion(cgProcedure *p, Ast *expr, Type *type) {
+	ast_node(ta, TypeAssertion, expr);
+
+	TokenPos pos = ast_token(expr).pos;
+	cgValue e = cg_build_expr(p, ta->expr);
+	Type *t = type_deref(e.type);
+
+	if (is_type_union(t)) {
+		return cg_emit_cast_union(p, e, type, pos);
+	} else if (is_type_any(t)) {
+		return cg_emit_cast_any(p, e, type, pos);
+	}
+	GB_PANIC("TODO(bill): type assertion %s", type_to_string(e.type));
+	return {};
+}
+
+
+gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr) {
+	expr = unparen_expr(expr);
+
+	TokenPos expr_pos = ast_token(expr).pos;
+	TypeAndValue tv = type_and_value_of_expr(expr);
+	Type *type = type_of_expr(expr);
+	GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type));
+
+	if (tv.value.kind != ExactValue_Invalid &&
+	    expr->kind != Ast_CompoundLit) {
+		// NOTE(bill): The commented out code below is just for debug purposes only
+		// if (is_type_untyped(type)) {
+		// 	gb_printf_err("%s %s : %s @ %p\n", token_pos_to_string(expr_pos), expr_to_string(expr), type_to_string(expr->tav.type), expr);
+		// 	GB_PANIC("%s\n", type_to_string(tv.type));
+		// }
+		// NOTE(bill): Short on constant values
+		return cg_const_value(p, type, tv.value);
+	} else if (tv.mode == Addressing_Type) {
+		// NOTE(bill, 2023-01-16): is this correct? I hope so at least
+		return cg_typeid(p, tv.type);
+	}
+
+	switch (expr->kind) {
+	case_ast_node(bl, BasicLit, expr);
+		TokenPos pos = bl->token.pos;
+		GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]));
+	case_end;
+
+	case_ast_node(bd, BasicDirective, expr);
+		TokenPos pos = bd->token.pos;
+		GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string));
+	case_end;
+
+	case_ast_node(i, Ident, expr);
+		Entity *e = entity_from_expr(expr);
+		e = strip_entity_wrapping(e);
+
+		GB_ASSERT_MSG(e != nullptr, "%s in %.*s %p", expr_to_string(expr), LIT(p->name), expr);
+
+		if (e->kind == Entity_Builtin) {
+			Token token = ast_token(expr);
+			GB_PANIC("TODO(bill): lb_build_expr Entity_Builtin '%.*s'\n"
+			         "\t at %s", LIT(builtin_procs[e->Builtin.id].name),
+			         token_pos_to_string(token.pos));
+			return {};
+		} else if (e->kind == Entity_Nil) {
+			// TODO(bill): is this correct?
+			return cg_value(cast(TB_Node *)nullptr, e->type);
+		}
+		GB_ASSERT(e->kind != Entity_ProcGroup);
+
+		cgAddr *addr = map_get(&p->variable_map, e);
+		if (addr) {
+			return cg_addr_load(p, *addr);
+		}
+		return cg_find_ident(p, e, expr);
+	case_end;
+
+	case_ast_node(i, Implicit, expr);
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(u, Uninit, expr);
+		if (is_type_untyped(type)) {
+			return cg_value(cast(TB_Node *)nullptr, t_untyped_uninit);
+		}
+		return cg_value(tb_inst_poison(p->func), type);
+	case_end;
+
+	case_ast_node(de, DerefExpr, expr);
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
+
+
+	case_ast_node(se, SelectorExpr, expr);
+		TypeAndValue tav = type_and_value_of_expr(expr);
+		GB_ASSERT(tav.mode != Addressing_Invalid);
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ise, ImplicitSelectorExpr, expr);
+		TypeAndValue tav = type_and_value_of_expr(expr);
+		GB_ASSERT(tav.mode == Addressing_Constant);
+
+		return cg_const_value(p, type, tv.value);
+	case_end;
+
+
+	case_ast_node(se, SelectorCallExpr, expr);
+		GB_ASSERT(se->modified_call);
+		return cg_build_call_expr(p, se->call);
+	case_end;
+
+	case_ast_node(i, CallExpr, expr);
+		return cg_build_call_expr(p, expr);
+	case_end;
+
+	case_ast_node(cl, CompoundLit, expr);
+		cgAddr addr = cg_build_addr_compound_lit(p, expr);
+		return cg_addr_load(p, addr);
+	case_end;
+
+
+	case_ast_node(te, TernaryIfExpr, expr);
+		cgValue incoming_values[2] = {};
+		TB_Node *incoming_regions[2] = {};
+
+		TB_Node *then  = cg_control_region(p, "if_then");
+		TB_Node *done  = cg_control_region(p, "if_done");
+		TB_Node *else_ = cg_control_region(p, "if_else");
+
+		cg_build_cond(p, te->cond, then, else_);
+		tb_inst_set_control(p->func, then);
+
+		Type *type = default_type(type_of_expr(expr));
+
+		incoming_values [0] = cg_emit_conv(p, cg_build_expr(p, te->x), type);
+		incoming_regions[0] = tb_inst_get_control(p->func);
+
+		cg_emit_goto(p, done);
+		tb_inst_set_control(p->func, else_);
+
+		incoming_values [1] = cg_emit_conv(p, cg_build_expr(p, te->y), type);
+		incoming_regions[1] = tb_inst_get_control(p->func);
+
+		cg_emit_goto(p, done);
+		tb_inst_set_control(p->func, done);
+
+		GB_ASSERT(incoming_values[0].kind == cgValue_Value ||
+		          incoming_values[0].kind == cgValue_Addr);
+		GB_ASSERT(incoming_values[0].kind == incoming_values[1].kind);
+
+		cgValue res = {};
+		res.kind = incoming_values[0].kind;
+		res.type = type;
+		TB_DataType dt = cg_data_type(type);
+		if (res.kind == cgValue_Addr) {
+			dt = TB_TYPE_PTR;
+		}
+		res.node = tb_inst_incomplete_phi(p->func, dt, done, 2);
+		tb_inst_add_phi_operand(p->func, res.node, incoming_regions[0], incoming_values[0].node);
+		tb_inst_add_phi_operand(p->func, res.node, incoming_regions[1], incoming_values[1].node);
+		return res;
+	case_end;
+
+	case_ast_node(te, TernaryWhenExpr, expr);
+		TypeAndValue tav = type_and_value_of_expr(te->cond);
+		GB_ASSERT(tav.mode == Addressing_Constant);
+		GB_ASSERT(tav.value.kind == ExactValue_Bool);
+		if (tav.value.value_bool) {
+			return cg_build_expr(p, te->x);
+		} else {
+			return cg_build_expr(p, te->y);
+		}
+	case_end;
+
+	case_ast_node(tc, TypeCast, expr);
+		cgValue e = cg_build_expr(p, tc->expr);
+		switch (tc->token.kind) {
+		case Token_cast:
+			return cg_emit_conv(p, e, type);
+		case Token_transmute:
+			return cg_emit_transmute(p, e, type);
+		}
+		GB_PANIC("Invalid AST TypeCast");
+	case_end;
+
+	case_ast_node(ac, AutoCast, expr);
+		cgValue value = cg_build_expr(p, ac->expr);
+		return cg_emit_conv(p, value, type);
+	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		if (is_type_slice(type_of_expr(se->expr))) {
+			// NOTE(bill): Quick optimization
+			if (se->high == nullptr &&
+			    (se->low == nullptr || cg_is_expr_constant_zero(se->low))) {
+				return cg_build_expr(p, se->expr);
+			}
+		}
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ie, IndexExpr, expr);
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ie, MatrixIndexExpr, expr);
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, expr);
+		if (ue->op.kind == Token_And) {
+			return cg_build_unary_and(p, expr);
+		}
+		cgValue v = cg_build_expr(p, ue->expr);
+		return cg_emit_unary_arith(p, ue->op.kind, v, type);
+	case_end;
+	case_ast_node(be, BinaryExpr, expr);
+		return cg_build_binary_expr(p, expr);
+	case_end;
+
+	case_ast_node(oe, OrReturnExpr, expr);
+		return cg_build_or_return(p, oe->expr, tv.type);
+	case_end;
+
+	case_ast_node(oe, OrElseExpr, expr);
+		return cg_build_or_else(p, oe->x, oe->y, tv.type);
+	case_end;
+
+	case_ast_node(ta, TypeAssertion, expr);
+		return cg_build_type_assertion(p, expr, tv.type);
+	case_end;
+
+	case_ast_node(pl, ProcLit, expr);
+		cgProcedure *anon = cg_procedure_generate_anonymous(p->module, expr, p);
+		GB_ASSERT(anon != nullptr);
+		GB_ASSERT(anon->symbol != nullptr);
+		return cg_value(tb_inst_get_symbol_address(p->func, anon->symbol), type);
+	case_end;
+
+	}
+	TokenPos token_pos = ast_token(expr).pos;
+	GB_PANIC("Unexpected expression\n"
+	         "\tAst: %.*s @ "
+	         "%s\n",
+	         LIT(ast_strings[expr->kind]),
+	         token_pos_to_string(token_pos));
+
+	return {};
+
+}
+
+gb_internal cgValue cg_build_addr_ptr(cgProcedure *p, Ast *expr) {
+	cgAddr addr = cg_build_addr(p, expr);
+	return cg_addr_get_ptr(p, addr);
+}
+
+gb_internal cgAddr cg_build_addr_internal(cgProcedure *p, Ast *expr);
+gb_internal cgAddr cg_build_addr(cgProcedure *p, Ast *expr) {
+	expr = unparen_expr(expr);
+
+	// IMPORTANT NOTE(bill):
+	// Selector Call Expressions (foo->bar(...))
+	// must only evaluate `foo` once as it gets transformed into
+	// `foo.bar(foo, ...)`
+	// And if `foo` is a procedure call or something more complex, storing the value
+	// once is a very good idea
+	// If a stored value is found, it must be removed from the cache
+	if (expr->state_flags & StateFlag_SelectorCallExpr) {
+		// lbAddr *pp = map_get(&p->selector_addr, expr);
+		// if (pp != nullptr) {
+		// 	lbAddr res = *pp;
+		// 	map_remove(&p->selector_addr, expr);
+		// 	return res;
+		// }
+	}
+	cgAddr addr = cg_build_addr_internal(p, expr);
+	if (expr->state_flags & StateFlag_SelectorCallExpr) {
+		// map_set(&p->selector_addr, expr, addr);
+	}
+	return addr;
+}
+
+gb_internal cgAddr cg_build_addr_index_expr(cgProcedure *p, Ast *expr) {
+	ast_node(ie, IndexExpr, expr);
+
+	Type *t = base_type(type_of_expr(ie->expr));
+
+	bool deref = is_type_pointer(t);
+	t = base_type(type_deref(t));
+	if (is_type_soa_struct(t)) {
+		GB_PANIC("TODO(bill): #soa");
+		// // SOA STRUCTURES!!!!
+		// lbValue val = cg_build_addr_ptr(p, ie->expr);
+		// if (deref) {
+		// 	val = cg_emit_load(p, val);
+		// }
+
+		// cgValue index = cg_build_expr(p, ie->index);
+		// return cg_addr_soa_variable(val, index, ie->index);
+	}
+
+	if (ie->expr->tav.mode == Addressing_SoaVariable) {
+		GB_PANIC("TODO(bill): #soa");
+		// // SOA Structures for slices/dynamic arrays
+		// GB_ASSERT(is_type_pointer(type_of_expr(ie->expr)));
+
+		// lbValue field = lb_build_expr(p, ie->expr);
+		// lbValue index = lb_build_expr(p, ie->index);
+
+
+		// if (!build_context.no_bounds_check) {
+		// 	// TODO HACK(bill): Clean up this hack to get the length for bounds checking
+		// 	// GB_ASSERT(LLVMIsALoadInst(field.value));
+
+		// 	// lbValue a = {};
+		// 	// a.value = LLVMGetOperand(field.value, 0);
+		// 	// a.type = alloc_type_pointer(field.type);
+
+		// 	// irInstr *b = &a->Instr;
+		// 	// GB_ASSERT(b->kind == irInstr_StructElementPtr);
+		// 	// lbValue base_struct = b->StructElementPtr.address;
+
+		// 	// GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct))));
+		// 	// lbValue len = ir_soa_struct_len(p, base_struct);
+		// 	// lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+		// }
+		// lbValue val = lb_emit_ptr_offset(p, field, index);
+		// return lb_addr(val);
+	}
+
+	GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
+
+	if (is_type_map(t)) {
+		GB_PANIC("TODO(bill): map indexing");
+		// lbAddr map_addr = lb_build_addr(p, ie->expr);
+		// lbValue key = lb_build_expr(p, ie->index);
+		// key = lb_emit_conv(p, key, t->Map.key);
+
+		// Type *result_type = type_of_expr(expr);
+		// lbValue map_ptr = lb_addr_get_ptr(p, map_addr);
+		// if (is_type_pointer(type_deref(map_ptr.type))) {
+		// 	map_ptr = lb_emit_load(p, map_ptr);
+		// }
+		// return lb_addr_map(map_ptr, key, t, result_type);
+	}
+
+	switch (t->kind) {
+	case Type_Array: {
+		cgValue array = {};
+		array = cg_build_addr_ptr(p, ie->expr);
+		if (deref) {
+			array = cg_emit_load(p, array);
+		}
+		cgValue index = cg_build_expr(p, ie->index);
+		index = cg_emit_conv(p, index, t_int);
+		cgValue elem = cg_emit_array_ep(p, array, index);
+
+		auto index_tv = type_and_value_of_expr(ie->index);
+		if (index_tv.mode != Addressing_Constant) {
+			// cgValue len = cg_const_int(p->module, t_int, t->Array.count);
+			// cg_emit_bounds_check(p, ast_token(ie->index), index, len);
+		}
+		return cg_addr(elem);
+	}
+
+	case Type_EnumeratedArray: {
+		cgValue array = {};
+		array = cg_build_addr_ptr(p, ie->expr);
+		if (deref) {
+			array = cg_emit_load(p, array);
+		}
+
+		Type *index_type = t->EnumeratedArray.index;
+
+		auto index_tv = type_and_value_of_expr(ie->index);
+
+		cgValue index = {};
+		if (compare_exact_values(Token_NotEq, *t->EnumeratedArray.min_value, exact_value_i64(0))) {
+			if (index_tv.mode == Addressing_Constant) {
+				ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value);
+				index = cg_const_value(p, index_type, idx);
+			} else {
+				index = cg_emit_arith(p, Token_Sub,
+				                      cg_build_expr(p, ie->index),
+				                      cg_const_value(p, index_type, *t->EnumeratedArray.min_value),
+				                      index_type);
+				index = cg_emit_conv(p, index, t_int);
+			}
+		} else {
+			index = cg_emit_conv(p, cg_build_expr(p, ie->index), t_int);
+		}
+
+		cgValue elem = cg_emit_array_ep(p, array, index);
+
+		if (index_tv.mode != Addressing_Constant) {
+			// cgValue len = cg_const_int(p->module, t_int, t->EnumeratedArray.count);
+			// cg_emit_bounds_check(p, ast_token(ie->index), index, len);
+		}
+		return cg_addr(elem);
+	}
+
+	case Type_Slice: {
+		cgValue slice = {};
+		slice = cg_build_expr(p, ie->expr);
+		if (deref) {
+			slice = cg_emit_load(p, slice);
+		}
+		cgValue elem = cg_builtin_raw_data(p, slice);
+		cgValue index = cg_emit_conv(p, cg_build_expr(p, ie->index), t_int);
+		// cgValue len = cg_builtin_len(p, slice);
+		// cg_emit_bounds_check(p, ast_token(ie->index), index, len);
+		cgValue v = cg_emit_ptr_offset(p, elem, index);
+		v.type = alloc_type_pointer(type_deref(v.type, true));
+		return cg_addr(v);
+	}
+
+	case Type_MultiPointer: {
+		cgValue multi_ptr = {};
+		multi_ptr = cg_build_expr(p, ie->expr);
+		if (deref) {
+			multi_ptr = cg_emit_load(p, multi_ptr);
+		}
+		cgValue index = cg_build_expr(p, ie->index);
+		index = cg_emit_conv(p, index, t_int);
+
+		cgValue v = cg_emit_ptr_offset(p, multi_ptr, index);
+		v.type = alloc_type_pointer(type_deref(v.type, true));
+		return cg_addr(v);
+	}
+
+	case Type_RelativeMultiPointer: {
+		cgValue multi_ptr = {};
+		multi_ptr = cg_build_expr(p, ie->expr);
+		if (deref) {
+			multi_ptr = cg_emit_load(p, multi_ptr);
+		}
+		cgValue index = cg_build_expr(p, ie->index);
+		index = cg_emit_conv(p, index, t_int);
+
+		cgValue v = cg_emit_ptr_offset(p, multi_ptr, index);
+		v.type = alloc_type_pointer(type_deref(v.type, true));
+		return cg_addr(v);
+	}
+
+	case Type_DynamicArray: {
+		cgValue dynamic_array = {};
+		dynamic_array = cg_build_expr(p, ie->expr);
+		if (deref) {
+			dynamic_array = cg_emit_load(p, dynamic_array);
+		}
+		cgValue elem = cg_builtin_raw_data(p, dynamic_array);
+		cgValue index = cg_emit_conv(p, cg_build_expr(p, ie->index), t_int);
+		// cgValue len = cg_dynamic_array_len(p, dynamic_array);
+		// cg_emit_bounds_check(p, ast_token(ie->index), index, len);
+		cgValue v = cg_emit_ptr_offset(p, elem, index);
+		v.type = alloc_type_pointer(type_deref(v.type, true));
+		return cg_addr(v);
+	}
+
+	case Type_Matrix: {
+		GB_PANIC("TODO(bill): matrix");
+		// lbValue matrix = {};
+		// matrix = lb_build_addr_ptr(p, ie->expr);
+		// if (deref) {
+		// 	matrix = lb_emit_load(p, matrix);
+		// }
+		// lbValue index = lb_build_expr(p, ie->index);
+		// index = lb_emit_conv(p, index, t_int);
+		// lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
+		// elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr)));
+
+		// auto index_tv = type_and_value_of_expr(ie->index);
+		// if (index_tv.mode != Addressing_Constant) {
+		// 	lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count);
+		// 	lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+		// }
+		// return lb_addr(elem);
+	}
+
+
+	case Type_Basic: { // Basic_string
+		cgValue str;
+		cgValue elem;
+		cgValue len;
+		cgValue index;
+
+		str = cg_build_expr(p, ie->expr);
+		if (deref) {
+			str = cg_emit_load(p, str);
+		}
+		elem = cg_builtin_raw_data(p, str);
+		len = cg_builtin_len(p, str);
+
+		index = cg_emit_conv(p, cg_build_expr(p, ie->index), t_int);
+		// cg_emit_bounds_check(p, ast_token(ie->index), index, len);
+
+		cgValue v = cg_emit_ptr_offset(p, elem, index);
+		v.type = alloc_type_pointer(type_deref(v.type, true));
+		return cg_addr(v);
+	}
+	}
+	return {};
+}
+
+gb_internal cgAddr cg_build_addr_internal(cgProcedure *p, Ast *expr) {
+	switch (expr->kind) {
+	case_ast_node(i, Implicit, expr);
+		cgAddr v = {};
+		switch (i->kind) {
+		case Token_context:
+			v = cg_find_or_generate_context_ptr(p);
+			break;
+		}
+
+		GB_ASSERT(v.addr.node != nullptr);
+		return v;
+	case_end;
+
+	case_ast_node(i, Ident, expr);
+		if (is_blank_ident(expr)) {
+			cgAddr val = {};
+			return val;
+		}
+		String name = i->token.string;
+		Entity *e = entity_of_node(expr);
+		return cg_build_addr_from_entity(p, e, expr);
+	case_end;
+
+	case_ast_node(de, DerefExpr, expr);
+		Type *t = type_of_expr(de->expr);
+		if (is_type_relative_pointer(t)) {
+			cgAddr addr = cg_build_addr(p, de->expr);
+			addr.relative.deref = true;
+			return addr;
+		} else if (is_type_soa_pointer(t)) {
+			cgValue value = cg_build_expr(p, de->expr);
+			cgValue ptr = cg_emit_struct_ev(p, value, 0);
+			cgValue idx = cg_emit_struct_ev(p, value, 1);
+			GB_PANIC("TODO(bill): cg_addr_soa_variable");
+			// return cg_addr_soa_variable(ptr, idx, nullptr);
+		}
+		cgValue addr = cg_build_expr(p, de->expr);
+		return cg_addr(addr);
+	case_end;
+
+	case_ast_node(ie, IndexExpr, expr);
+		return cg_build_addr_index_expr(p, expr);
+	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		return cg_build_addr_slice_expr(p, expr);
+	case_end;
+
+	case_ast_node(se, SelectorExpr, expr);
+		Ast *sel_node = unparen_expr(se->selector);
+		if (sel_node->kind != Ast_Ident) {
+			GB_PANIC("Unsupported selector expression");
+		}
+		String selector = sel_node->Ident.token.string;
+		TypeAndValue tav = type_and_value_of_expr(se->expr);
+
+		if (tav.mode == Addressing_Invalid) {
+			// NOTE(bill): Imports
+			Entity *imp = entity_of_node(se->expr);
+			if (imp != nullptr) {
+				GB_ASSERT(imp->kind == Entity_ImportName);
+			}
+			return cg_build_addr(p, unparen_expr(se->selector));
+		}
+
+
+		Type *type = base_type(tav.type);
+		if (tav.mode == Addressing_Type) { // Addressing_Type
+			Selection sel = lookup_field(tav.type, selector, true);
+			if (sel.pseudo_field) {
+				GB_ASSERT(sel.entity->kind == Entity_Procedure);
+				return cg_addr(cg_find_value_from_entity(p->module, sel.entity));
+			}
+			GB_PANIC("Unreachable %.*s", LIT(selector));
+		}
+
+		if (se->swizzle_count > 0) {
+			Type *array_type = base_type(type_deref(tav.type));
+			GB_ASSERT(array_type->kind == Type_Array);
+			u8 swizzle_count = se->swizzle_count;
+			u8 swizzle_indices_raw = se->swizzle_indices;
+			u8 swizzle_indices[4] = {};
+			for (u8 i = 0; i < swizzle_count; i++) {
+				u8 index = swizzle_indices_raw>>(i*2) & 3;
+				swizzle_indices[i] = index;
+			}
+			cgValue a = {};
+			if (is_type_pointer(tav.type)) {
+				a = cg_build_expr(p, se->expr);
+			} else {
+				cgAddr addr = cg_build_addr(p, se->expr);
+				a = cg_addr_get_ptr(p, addr);
+			}
+
+			GB_ASSERT(is_type_array(expr->tav.type));
+			GB_PANIC("TODO(bill): cg_addr_swizzle");
+			// return cg_addr_swizzle(a, expr->tav.type, swizzle_count, swizzle_indices);
+		}
+
+		Selection sel = lookup_field(type, selector, false);
+		GB_ASSERT(sel.entity != nullptr);
+		if (sel.pseudo_field) {
+			GB_ASSERT(sel.entity->kind == Entity_Procedure);
+			Entity *e = entity_of_node(sel_node);
+			return cg_addr(cg_find_value_from_entity(p->module, e));
+		}
+
+		{
+			cgAddr addr = cg_build_addr(p, se->expr);
+			if (addr.kind == cgAddr_Map) {
+				cgValue v = cg_addr_load(p, addr);
+				cgValue a = cg_address_from_load_or_generate_local(p, v);
+				a = cg_emit_deep_field_gep(p, a, sel);
+				return cg_addr(a);
+			} else if (addr.kind == cgAddr_Context) {
+				GB_ASSERT(sel.index.count > 0);
+				if (addr.ctx.sel.index.count >= 0) {
+					sel = selection_combine(addr.ctx.sel, sel);
+				}
+				addr.ctx.sel = sel;
+				addr.kind = cgAddr_Context;
+				return addr;
+			} else if (addr.kind == cgAddr_SoaVariable) {
+				cgValue index = addr.soa.index;
+				i64 first_index = sel.index[0];
+				Selection sub_sel = sel;
+				sub_sel.index.data += 1;
+				sub_sel.index.count -= 1;
+
+				cgValue arr = cg_emit_struct_ep(p, addr.addr, first_index);
+
+				Type *t = base_type(type_deref(addr.addr.type));
+				GB_ASSERT(is_type_soa_struct(t));
+
+				// TODO(bill): bounds checking for soa variable
+				// if (addr.soa.index_expr != nullptr && (!cg_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) {
+				// 	cgValue len = cg_soa_struct_len(p, addr.addr);
+				// 	cg_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
+				// }
+
+				cgValue item = {};
+
+				if (t->Struct.soa_kind == StructSoa_Fixed) {
+					item = cg_emit_array_ep(p, arr, index);
+				} else {
+					item = cg_emit_ptr_offset(p, cg_emit_load(p, arr), index);
+				}
+				if (sub_sel.index.count > 0) {
+					item = cg_emit_deep_field_gep(p, item, sub_sel);
+				}
+				item.type = alloc_type_pointer(type_deref(item.type, true));
+				return cg_addr(item);
+			} else if (addr.kind == cgAddr_Swizzle) {
+				GB_ASSERT(sel.index.count > 0);
+				// NOTE(bill): just patch the index in place
+				sel.index[0] = addr.swizzle.indices[sel.index[0]];
+			} else if (addr.kind == cgAddr_SwizzleLarge) {
+				GB_ASSERT(sel.index.count > 0);
+				// NOTE(bill): just patch the index in place
+				sel.index[0] = addr.swizzle.indices[sel.index[0]];
+			}
+
+			cgValue a = cg_addr_get_ptr(p, addr);
+			a = cg_emit_deep_field_gep(p, a, sel);
+			return cg_addr(a);
+		}
+	case_end;
+
+	case_ast_node(ce, CallExpr, expr);
+		cgValue res = cg_build_expr(p, expr);
+		switch (res.kind) {
+		case cgValue_Value:
+			return cg_addr(cg_address_from_load_or_generate_local(p, res));
+		case cgValue_Addr:
+			return cg_addr(res);
+		case cgValue_Multi:
+			GB_PANIC("cannot address a multi-valued expression");
+			break;
+		}
+	case_end;
+
+	case_ast_node(cl, CompoundLit, expr);
+		return cg_build_addr_compound_lit(p, expr);
+	case_end;
+
+	}
+
+	TokenPos token_pos = ast_token(expr).pos;
+	GB_PANIC("Unexpected address expression\n"
+	         "\tAst: %.*s @ "
+	         "%s\n",
+	         LIT(ast_strings[expr->kind]),
+	         token_pos_to_string(token_pos));
+
+	return {};
+}

+ 1307 - 0
src/tilde_proc.cpp

@@ -0,0 +1,1307 @@
+gb_internal TB_FunctionPrototype *cg_procedure_type_as_prototype(cgModule *m, Type *type) {
+	GB_ASSERT(is_type_proc(type));
+	mutex_lock(&m->proc_proto_mutex);
+	defer (mutex_unlock(&m->proc_proto_mutex));
+
+	if (type->kind == Type_Named) {
+		type = base_type(type);
+	}
+	TB_FunctionPrototype **found = map_get(&m->proc_proto_map, type);
+	if (found) {
+		return *found;
+	}
+
+	TB_DebugType *dbg = cg_debug_type_for_proc(m, type);
+	TB_FunctionPrototype *proto = tb_prototype_from_dbg(m->mod, dbg);
+
+	map_set(&m->proc_proto_map, type, proto);
+	return proto;
+}
+
+gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool ignore_body) {
+	GB_ASSERT(entity != nullptr);
+	GB_ASSERT(entity->kind == Entity_Procedure);
+	if (!entity->Procedure.is_foreign) {
+		if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+			GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
+		}
+	}
+
+	String link_name = cg_get_entity_name(m, entity);
+
+	cgProcedure *p = nullptr;
+	{
+		StringHashKey key = string_hash_string(link_name);
+		cgValue *found = string_map_get(&m->members, key);
+		if (found) {
+			cg_add_entity(m, entity, *found);
+			rw_mutex_lock(&m->values_mutex);
+			p = string_map_must_get(&m->procedures, key);
+			rw_mutex_unlock(&m->values_mutex);
+			if (!ignore_body && p->func != nullptr) {
+				return nullptr;
+			}
+		}
+	}
+
+	if (p == nullptr) {
+		p = gb_alloc_item(permanent_allocator(), cgProcedure);
+	}
+
+	p->module = m;
+	p->entity = entity;
+	p->name = link_name;
+
+	DeclInfo *decl = entity->decl_info;
+
+	ast_node(pl, ProcLit, decl->proc_lit);
+	Type *pt = base_type(entity->type);
+	GB_ASSERT(pt->kind == Type_Proc);
+
+	p->type           = entity->type;
+	p->type_expr      = decl->type_expr;
+	p->body           = pl->body;
+	p->inlining       = pl->inlining;
+	p->is_foreign     = entity->Procedure.is_foreign;
+	p->is_export      = entity->Procedure.is_export;
+	p->is_entry_point = false;
+	p->split_returns_index = -1;
+
+	gbAllocator a = heap_allocator();
+	p->children.allocator      = a;
+
+	p->defer_stack.allocator   = a;
+	p->scope_stack.allocator   = a;
+	p->context_stack.allocator = a;
+
+	p->control_regions.allocator = a;
+	p->branch_regions.allocator = a;
+
+	map_init(&p->variable_map);
+
+	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
+	if (p->is_export) {
+		linkage = TB_LINKAGE_PUBLIC;
+	} else if (p->is_foreign || ignore_body) {
+		if (ignore_body) {
+			linkage = TB_LINKAGE_PUBLIC;
+		}
+		p->symbol = cast(TB_Symbol *)tb_extern_create(m->mod, link_name.len, cast(char const *)link_name.text, TB_EXTERNAL_SO_LOCAL);
+	}
+	if (p->name == "main") {
+		// TODO(bill): figure out when this should be public or not
+		linkage = TB_LINKAGE_PUBLIC;
+	}
+
+	if (p->symbol == nullptr)  {
+		p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
+
+		p->debug_type = cg_debug_type_for_proc(m, p->type);
+		p->proto = tb_prototype_from_dbg(m->mod, p->debug_type);
+
+		p->symbol = cast(TB_Symbol *)p->func;
+	}
+
+	p->value = cg_value(p->symbol, p->type);
+
+	cg_add_symbol(m, entity, p->symbol);
+	cg_add_entity(m, entity, p->value);
+	cg_add_member(m, p->name, p->value);
+	cg_add_procedure_value(m, p);
+
+
+	return p;
+}
+
+gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &link_name, Type *type) {
+	auto *prev_found = string_map_get(&m->members, link_name);
+	GB_ASSERT_MSG(prev_found == nullptr, "failed to create dummy procedure for: %.*s", LIT(link_name));
+
+	cgProcedure *p = gb_alloc_item(permanent_allocator(), cgProcedure);
+
+	p->module = m;
+	p->name = link_name;
+
+	p->type           = type;
+	p->type_expr      = nullptr;
+	p->body           = nullptr;
+	p->tags           = 0;
+	p->inlining       = ProcInlining_none;
+	p->is_foreign     = false;
+	p->is_export      = false;
+	p->is_entry_point = false;
+	p->split_returns_index = -1;
+
+	gbAllocator a = heap_allocator();
+	p->children.allocator      = a;
+
+	p->defer_stack.allocator   = a;
+	p->scope_stack.allocator   = a;
+	p->context_stack.allocator = a;
+
+	p->control_regions.allocator = a;
+	p->branch_regions.allocator = a;
+
+	map_init(&p->variable_map);
+
+
+	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
+
+	p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
+
+	p->debug_type = cg_debug_type_for_proc(m, p->type);
+	p->proto = tb_prototype_from_dbg(m->mod, p->debug_type);
+
+	p->symbol = cast(TB_Symbol *)p->func;
+
+	cgValue proc_value = cg_value(p->symbol, p->type);
+	cg_add_member(m, p->name, proc_value);
+	cg_add_procedure_value(m, p);
+
+	return p;
+}
+
+gb_internal cgProcedure *cg_procedure_generate_anonymous(cgModule *m, Ast *expr, cgProcedure *parent) {
+	expr = unparen_expr(expr);
+	ast_node(pl, ProcLit, expr);
+
+	mutex_lock(&m->anonymous_proc_lits_mutex);
+	defer (mutex_unlock(&m->anonymous_proc_lits_mutex));
+
+	cgProcedure **found = map_get(&m->anonymous_proc_lits_map, expr);
+	if (found) {
+		return *found;
+	}
+
+	TokenPos pos = ast_token(expr).pos;
+
+	// NOTE(bill): Generate a new name
+	// parent$count
+
+	String prefix_name = str_lit("proc_lit");
+	if (parent) {
+		prefix_name = parent->name;
+	}
+
+	isize name_len = prefix_name.len + 6 + 11;
+	char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+
+	static std::atomic<i32> name_id;
+	name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), 1+name_id.fetch_add(1));
+	String name = make_string((u8 *)name_text, name_len-1);
+
+	Type *type = type_of_expr(expr);
+
+	GB_ASSERT(pl->decl->entity == nullptr);
+	Token token = {};
+	token.pos = ast_token(expr).pos;
+	token.kind = Token_Ident;
+	token.string = name;
+	Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
+	e->file = expr->file();
+
+	// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
+	e->decl_info = pl->decl;
+	pl->decl->entity = e;
+	e->flags |= EntityFlag_ProcBodyChecked;
+
+	cgProcedure *p = cg_procedure_create(m, e);
+
+	map_set(&m->anonymous_proc_lits_map, expr, p);
+
+	if (parent != nullptr) {
+		array_add(&parent->children, p);
+	}
+
+	cg_add_procedure_to_queue(p);
+	return p;
+
+}
+
+gb_internal void cg_procedure_begin(cgProcedure *p) {
+	if (p == nullptr || p->func == nullptr) {
+		return;
+	}
+
+	tb_function_set_prototype(p->func, p->proto, cg_arena());
+
+	if (p->body == nullptr) {
+		return;
+	}
+
+
+	DeclInfo *decl = decl_info_of_entity(p->entity);
+	if (decl != nullptr) {
+		for_array(i, decl->labels) {
+			BlockLabel bl = decl->labels[i];
+			cgBranchRegions bb = {bl.label, nullptr, nullptr};
+			array_add(&p->branch_regions, bb);
+		}
+	}
+
+	GB_ASSERT(p->type->kind == Type_Proc);
+	TypeProc *pt = &p->type->Proc;
+	bool is_odin_like_cc = is_calling_convention_odin(pt->calling_convention);
+	int param_index = 0;
+	int param_count = p->proto->param_count;
+
+	if (pt->results) {
+		Type *result_type = nullptr;
+		if (is_odin_like_cc) {
+			result_type = pt->results->Tuple.variables[pt->results->Tuple.variables.count-1]->type;
+		} else {
+			result_type = pt->results;
+		}
+		TB_DebugType *debug_type = cg_debug_type(p->module, result_type);
+		TB_PassingRule rule = tb_get_passing_rule_from_dbg(p->module->mod, debug_type, true);
+		if (rule == TB_PASSING_INDIRECT) {
+			p->return_by_ptr = true;
+			param_index++;
+		}
+	}
+
+	if (pt->params != nullptr) for (Entity *e : pt->params->Tuple.variables) {
+		if (e->kind != Entity_Variable) {
+			continue;
+		}
+
+		GB_ASSERT_MSG(param_index < param_count, "%d < %d %.*s :: %s", param_index, param_count, LIT(p->name), type_to_string(p->type));
+
+		TB_Node *param_ptr = nullptr;
+
+		TB_CharUnits size  = cast(TB_CharUnits)type_size_of(e->type);
+		TB_CharUnits align = cast(TB_CharUnits)type_align_of(e->type);
+		TB_DebugType *debug_type = cg_debug_type(p->module, e->type);
+		TB_PassingRule rule = tb_get_passing_rule_from_dbg(p->module->mod, debug_type, false);
+		switch (rule) {
+		case TB_PASSING_DIRECT: {
+			TB_Node *param = tb_inst_param(p->func, param_index++);
+			param_ptr = tb_inst_local(p->func, size, align);
+			tb_inst_store(p->func, param->dt, param_ptr, param, align, false);
+		} break;
+		case TB_PASSING_INDIRECT:
+			// TODO(bill): does this need a copy? for non-odin calling convention stuff?
+			param_ptr = tb_inst_param(p->func, param_index++);
+			break;
+		case TB_PASSING_IGNORE:
+			continue;
+		}
+
+		GB_ASSERT(param_ptr->dt.type == TB_PTR);
+
+		cgValue local = cg_value(param_ptr, alloc_type_pointer(e->type));
+
+		if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
+			// NOTE(bill): for debugging purposes only
+			String name = e->token.string;
+			TB_DebugType *param_debug_type = debug_type;
+			TB_Node *     param_ptr_to_use = param_ptr;
+			if (rule == TB_PASSING_INDIRECT) {
+				// HACK TODO(bill): this is just to get the debug information
+				TB_CharUnits ptr_size = cast(TB_CharUnits)build_context.ptr_size;
+				TB_Node *dummy_param = tb_inst_local(p->func, ptr_size, ptr_size);
+				tb_inst_store(p->func, TB_TYPE_PTR, dummy_param, param_ptr, ptr_size, false);
+				param_ptr_to_use = dummy_param;
+				param_debug_type = tb_debug_create_ptr(p->module->mod, param_debug_type);
+			}
+			tb_node_append_attrib(
+				param_ptr_to_use,
+				tb_function_attrib_variable(
+					p->func,
+					name.len, cast(char const *)name.text,
+					param_debug_type
+				)
+			);
+		}
+		cgAddr addr = cg_addr(local);
+		if (e) {
+			map_set(&p->variable_map, e, addr);
+		}
+	}
+
+	if (is_odin_like_cc) {
+		p->split_returns_index = param_index;
+	}
+
+	if (pt->calling_convention == ProcCC_Odin) {
+		// NOTE(bill): Push context on to stack from implicit parameter
+
+		String name = str_lit("__.context_ptr");
+
+		Entity *e = alloc_entity_param(nullptr, make_token_ident(name), t_context_ptr, false, false);
+		e->flags |= EntityFlag_NoAlias;
+
+		TB_Node *param_ptr = tb_inst_param(p->func, param_count-1);
+		cgValue local = cg_value(param_ptr, t_context_ptr);
+		cgAddr addr = cg_addr(local);
+		map_set(&p->variable_map, e, addr);
+
+
+		cgContextData *cd = array_add_and_get(&p->context_stack);
+		cd->ctx = addr;
+		cd->scope_index = -1;
+		cd->uses = +1; // make sure it has been used already
+	}
+
+	if (pt->has_named_results) {
+		auto const &results = pt->results->Tuple.variables;
+		for_array(i, results) {
+			Entity *e = results[i];
+			GB_ASSERT(e->kind == Entity_Variable);
+
+			if (e->token.string == "") {
+				continue;
+			}
+			GB_ASSERT(!is_blank_ident(e->token));
+
+			cgAddr res = cg_add_local(p, e->type, e, true);
+
+			if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+				cgValue c = cg_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
+				cg_addr_store(p, res, c);
+			}
+		}
+	}
+}
+
+
+gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
+	cgProcedure *p = cast(cgProcedure *)data;
+
+	TB_Passes *opt = tb_pass_enter(p->func, cg_arena());
+	defer (tb_pass_exit(opt));
+
+	// optimization passes
+	if (false) {
+		tb_pass_peephole(opt);
+		tb_pass_mem2reg(opt);
+		tb_pass_peephole(opt);
+	}
+
+	bool emit_asm = false;
+	if (
+	    // string_starts_with(p->name, str_lit("runtime@_windows_default_alloc_or_resize")) ||
+	    false
+	) {
+		emit_asm = true;
+	}
+
+	// emit ir
+	if (
+	    // string_starts_with(p->name, str_lit("bug@main")) ||
+	    // p->name == str_lit("runtime@_windows_default_alloc_or_resize") ||
+	    false
+	) { // IR Printing
+		TB_Arena *arena = cg_arena();
+		TB_Passes *passes = tb_pass_enter(p->func, arena);
+		defer (tb_pass_exit(passes));
+
+		tb_pass_print(passes);
+		fprintf(stdout, "\n");
+	}
+	if (false) { // GraphViz printing
+		tb_function_print(p->func, tb_default_print_callback, stdout);
+	}
+
+	// compile
+	TB_FunctionOutput *output = tb_pass_codegen(opt, emit_asm);
+	if (emit_asm) {
+		tb_output_print_asm(output, stdout);
+		fprintf(stdout, "\n");
+	}
+
+	return 0;
+}
+
+gb_internal void cg_procedure_end(cgProcedure *p) {
+	if (p == nullptr || p->func == nullptr) {
+		return;
+	}
+	if (tb_inst_get_control(p->func)) {
+		if (p->type->Proc.result_count == 0) {
+			tb_inst_ret(p->func, 0, nullptr);
+		} else {
+			tb_inst_unreachable(p->func);
+		}
+	}
+
+	if (p->module->do_threading) {
+		thread_pool_add_task(cg_procedure_compile_worker_proc, p);
+	} else {
+		cg_procedure_compile_worker_proc(p);
+	}
+}
+
+gb_internal void cg_procedure_generate(cgProcedure *p) {
+	if (p->body == nullptr) {
+		return;
+	}
+
+	cg_procedure_begin(p);
+	cg_build_stmt(p, p->body);
+	cg_procedure_end(p);
+}
+
+gb_internal void cg_build_nested_proc(cgProcedure *p, AstProcLit *pd, Entity *e) {
+	GB_ASSERT(pd->body != nullptr);
+	cgModule *m = p->module;
+	auto *min_dep_set = &m->info->minimum_dependency_set;
+
+	if (ptr_set_exists(min_dep_set, e) == false) {
+		// NOTE(bill): Nothing depends upon it so doesn't need to be built
+		return;
+	}
+
+	// NOTE(bill): Generate a new name
+	// parent.name-guid
+	String original_name = e->token.string;
+	String pd_name = original_name;
+	if (e->Procedure.link_name.len > 0) {
+		pd_name = e->Procedure.link_name;
+	}
+
+
+	isize name_len = p->name.len + 1 + pd_name.len + 1 + 10 + 1;
+	char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+
+	i32 guid = cast(i32)p->children.count;
+	name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%d", LIT(p->name), LIT(pd_name), guid);
+	String name = make_string(cast(u8 *)name_text, name_len-1);
+
+	e->Procedure.link_name = name;
+
+	cgProcedure *nested_proc = cg_procedure_create(p->module, e);
+	e->cg_procedure = nested_proc;
+
+	cgValue value = nested_proc->value;
+
+	cg_add_entity(m, e, value);
+	array_add(&p->children, nested_proc);
+	cg_add_procedure_to_queue(nested_proc);
+}
+
+
+
+
+
+gb_internal cgValue cg_find_procedure_value_from_entity(cgModule *m, Entity *e) {
+	GB_ASSERT(is_type_proc(e->type));
+	e = strip_entity_wrapping(e);
+	GB_ASSERT(e != nullptr);
+	GB_ASSERT(e->kind == Entity_Procedure);
+
+	cgValue *found = nullptr;
+	rw_mutex_shared_lock(&m->values_mutex);
+	found = map_get(&m->values, e);
+	rw_mutex_shared_unlock(&m->values_mutex);
+	if (found) {
+		GB_ASSERT(found->node != nullptr);
+		return *found;
+	}
+
+	GB_PANIC("Error in: %s, missing procedure %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string));
+	return {};
+}
+
+
+
+gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr);
+gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr) {
+	expr = unparen_expr(expr);
+	ast_node(ce, CallExpr, expr);
+
+	cgValue res = cg_build_call_expr_internal(p, expr);
+
+	if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
+		GB_ASSERT(res.kind == cgValue_Multi);
+		GB_ASSERT(res.multi->values.count == 2);
+		return res.multi->values[0];
+	}
+	return res;
+}
+
+gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue> const &args) {
+	if (value.kind == cgValue_Symbol) {
+		value = cg_value(tb_inst_get_symbol_address(p->func, value.symbol), value.type);
+	}
+	GB_ASSERT(value.kind == cgValue_Value);
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	TB_Module *m = p->module->mod;
+
+
+	Type *type = base_type(value.type);
+	GB_ASSERT(type->kind == Type_Proc);
+	TypeProc *pt = &type->Proc;
+	gb_unused(pt);
+
+	TB_FunctionPrototype *proto = cg_procedure_type_as_prototype(p->module, type);
+	TB_Node *target = value.node;
+	auto params = slice_make<TB_Node *>(temporary_allocator(), proto->param_count);
+
+
+	GB_ASSERT(build_context.metrics.os == TargetOs_windows);
+	// TODO(bill): Support more than Win64 ABI
+
+	bool is_odin_like_cc = is_calling_convention_odin(pt->calling_convention);
+
+	bool return_is_indirect = false;
+
+	Slice<Entity *> result_entities = {};
+	Slice<Entity *> param_entities  = {};
+	if (pt->results) {
+		result_entities = pt->results->Tuple.variables;
+	}
+	if (pt->params) {
+		param_entities = pt->params->Tuple.variables;
+	}
+
+	isize param_index = 0;
+	if (pt->result_count != 0) {
+		Type *return_type = nullptr;
+		if (is_odin_like_cc) {
+			return_type = result_entities[result_entities.count-1]->type;
+		} else {
+			return_type = pt->results;
+		}
+		TB_DebugType *dbg = cg_debug_type(p->module, return_type);
+		TB_PassingRule rule = tb_get_passing_rule_from_dbg(m, dbg, true);
+		if (rule == TB_PASSING_INDIRECT) {
+			return_is_indirect = true;
+			TB_CharUnits size = cast(TB_CharUnits)type_size_of(return_type);
+			TB_CharUnits align = cast(TB_CharUnits)gb_max(type_align_of(return_type), 16);
+			TB_Node *local = tb_inst_local(p->func, size, align);
+			tb_inst_memzero(p->func, local, tb_inst_uint(p->func, TB_TYPE_INT, size), align, false);
+			params[param_index++] = local;
+		}
+	}
+	isize param_entity_index = 0;
+	for_array(i, args) {
+		Entity *param_entity = nullptr;
+		do {
+			param_entity = param_entities[param_entity_index++];
+		} while (param_entity->kind != Entity_Variable);
+		Type *param_type = param_entity->type;
+		cgValue arg = args[i];
+		arg = cg_emit_conv(p, arg, param_type);
+		arg = cg_flatten_value(p, arg);
+
+		TB_Node *param = nullptr;
+
+		TB_DebugType *dbg = cg_debug_type(p->module, param_type);
+		TB_PassingRule rule = tb_get_passing_rule_from_dbg(m, dbg, false);
+		switch (rule) {
+		case TB_PASSING_DIRECT:
+			GB_ASSERT(arg.kind == cgValue_Value);
+			param = arg.node;
+			break;
+		case TB_PASSING_INDIRECT:
+			{
+				cgValue arg_ptr = {};
+				// indirect
+				if (is_odin_like_cc) {
+					arg_ptr = cg_address_from_load_or_generate_local(p, arg);
+				} else {
+					arg_ptr = cg_copy_value_to_ptr(p, arg, param_type, 16);
+				}
+				GB_ASSERT(arg_ptr.kind == cgValue_Value);
+				param = arg_ptr.node;
+			}
+			break;
+		case TB_PASSING_IGNORE:
+			continue;
+		}
+
+		params[param_index++] = param;
+	}
+
+	// Split returns
+	isize split_offset = -1;
+	if (is_odin_like_cc) {
+		split_offset = param_index;
+		for (isize i = 0; i < pt->result_count-1; i++) {
+			Type *result = result_entities[i]->type;
+			TB_CharUnits size = cast(TB_CharUnits)type_size_of(result);
+			TB_CharUnits align = cast(TB_CharUnits)gb_max(type_align_of(result), 16);
+			TB_Node *local = tb_inst_local(p->func, size, align);
+			// TODO(bill): Should this need to be zeroed any way?
+			tb_inst_memzero(p->func, local, tb_inst_uint(p->func, TB_TYPE_INT, size), align, false);
+			params[param_index++] = local;
+		}
+	}
+
+	if (pt->calling_convention == ProcCC_Odin) {
+		cgValue ctx_ptr = cg_find_or_generate_context_ptr(p).addr;
+		GB_ASSERT(ctx_ptr.kind == cgValue_Value);
+		params[param_index++] = ctx_ptr.node;
+	}
+	GB_ASSERT_MSG(param_index == params.count, "%td vs %td\n %s %u %u",
+	              param_index, params.count,
+	              type_to_string(type),
+	              proto->return_count,
+	              proto->param_count);
+
+	for (TB_Node *param : params) {
+		GB_ASSERT(param != nullptr);
+	}
+
+	GB_ASSERT(target != nullptr);
+	TB_MultiOutput multi_output = tb_inst_call(p->func, proto, target, params.count, params.data);
+	gb_unused(multi_output);
+
+	switch (pt->result_count) {
+	case 0:
+		return {};
+	case 1:
+		if (return_is_indirect) {
+			return cg_lvalue_addr(params[0], pt->results->Tuple.variables[0]->type);
+		} else {
+			GB_ASSERT(multi_output.count == 1);
+			TB_Node *node = multi_output.single;
+			return cg_value(node, pt->results->Tuple.variables[0]->type);
+		}
+	}
+
+	cgValueMulti *multi = gb_alloc_item(permanent_allocator(), cgValueMulti);
+	multi->values = slice_make<cgValue>(permanent_allocator(), pt->result_count);
+
+	if (is_odin_like_cc) {
+		GB_ASSERT(split_offset >= 0);
+		for (isize i = 0; i < pt->result_count-1; i++) {
+			multi->values[i] = cg_lvalue_addr(params[split_offset+i], result_entities[i]->type);
+		}
+
+		Type *end_type = result_entities[pt->result_count-1]->type;
+		if (return_is_indirect) {
+			multi->values[pt->result_count-1] = cg_lvalue_addr(params[0], end_type);
+		} else {
+			GB_ASSERT(multi_output.count == 1);
+			TB_DataType dt = cg_data_type(end_type);
+			TB_Node *res = multi_output.single;
+			if (res->dt.raw != dt.raw) {
+				// struct-like returns passed in registers
+				TB_CharUnits size  = cast(TB_CharUnits)type_size_of(end_type);
+				TB_CharUnits align = cast(TB_CharUnits)type_align_of(end_type);
+				TB_Node *addr = tb_inst_local(p->func, size, align);
+				tb_inst_store(p->func, res->dt, addr, res, align, false);
+				multi->values[pt->result_count-1] = cg_lvalue_addr(addr, end_type);
+			} else {
+				multi->values[pt->result_count-1] = cg_value(res, end_type);
+			}
+		}
+	} else {
+		TB_Node *the_tuple = {};
+		if (return_is_indirect) {
+			the_tuple = params[0];
+		} else {
+			GB_ASSERT(multi_output.count == 1);
+			TB_Node *res = multi_output.single;
+
+			// struct-like returns passed in registers
+			TB_CharUnits size  = cast(TB_CharUnits)type_size_of(pt->results);
+			TB_CharUnits align = cast(TB_CharUnits)type_align_of(pt->results);
+			the_tuple = tb_inst_local(p->func, size, align);
+			tb_inst_store(p->func, res->dt, the_tuple, res, align, false);
+		}
+		for (isize i = 0; i < pt->result_count; i++) {
+			i64 offset = type_offset_of(pt->results, i, nullptr);
+			TB_Node *ptr = tb_inst_member_access(p->func, the_tuple, offset);
+			multi->values[i] = cg_lvalue_addr(ptr, result_entities[i]->type);
+		}
+	}
+
+	return cg_value_multi(multi, pt->results);
+}
+
+gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args) {
+	AstPackage *pkg = p->module->info->runtime_package;
+	Entity *e = scope_lookup_current(pkg->scope, make_string_c(name));
+	cgValue value = cg_find_procedure_value_from_entity(p->module, e);
+	return cg_emit_call(p, value, args);
+}
+
+gb_internal cgValue cg_handle_param_value(cgProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos) {
+	switch (param_value.kind) {
+	case ParameterValue_Constant:
+		if (is_type_constant_type(parameter_type)) {
+			auto res = cg_const_value(p, parameter_type, param_value.value);
+			return res;
+		} else {
+			ExactValue ev = param_value.value;
+			cgValue arg = {};
+			Type *type = type_of_expr(param_value.original_ast_expr);
+			if (type != nullptr) {
+				arg = cg_const_value(p, type, ev);
+			} else {
+				arg = cg_const_value(p, parameter_type, param_value.value);
+			}
+			return cg_emit_conv(p, arg, parameter_type);
+		}
+
+	case ParameterValue_Nil:
+		return cg_const_nil(p, parameter_type);
+	case ParameterValue_Location:
+		{
+			String proc_name = {};
+			if (p->entity != nullptr) {
+				proc_name = p->entity->token.string;
+			}
+			return cg_emit_source_code_location_as_global(p, proc_name, pos);
+		}
+	case ParameterValue_Value:
+		return cg_build_expr(p, param_value.ast_value);
+	}
+	return cg_const_nil(p, parameter_type);
+}
+
+gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) {
+	ast_node(ce, CallExpr, expr);
+
+	TypeAndValue tv = type_and_value_of_expr(expr);
+
+	TypeAndValue proc_tv = type_and_value_of_expr(ce->proc);
+	AddressingMode proc_mode = proc_tv.mode;
+	if (proc_mode == Addressing_Type) {
+		GB_ASSERT(ce->args.count == 1);
+		cgValue x = cg_build_expr(p, ce->args[0]);
+		return cg_emit_conv(p, x, tv.type);
+	}
+
+	Ast *proc_expr = unparen_expr(ce->proc);
+	if (proc_mode == Addressing_Builtin) {
+		Entity *e = entity_of_node(proc_expr);
+		BuiltinProcId id = BuiltinProc_Invalid;
+		if (e != nullptr) {
+			id = cast(BuiltinProcId)e->Builtin.id;
+		} else {
+			id = BuiltinProc_DIRECTIVE;
+		}
+		if (id == BuiltinProc___entry_point) {
+			if (p->module->info->entry_point) {
+				cgValue entry_point = cg_find_procedure_value_from_entity(p->module, p->module->info->entry_point);
+				GB_ASSERT(entry_point.node != nullptr);
+				cg_emit_call(p, entry_point, {});
+			}
+			return {};
+		}
+
+		return cg_build_builtin(p, id, expr);
+	}
+
+	// NOTE(bill): Regular call
+	cgValue value = {};
+
+	Entity *proc_entity = entity_of_node(proc_expr);
+	if (proc_entity != nullptr) {
+		if (proc_entity->flags & EntityFlag_Disabled) {
+			GB_ASSERT(tv.type == nullptr);
+			return {};
+		}
+	}
+
+	if (proc_expr->tav.mode == Addressing_Constant) {
+		ExactValue v = proc_expr->tav.value;
+		switch (v.kind) {
+		case ExactValue_Integer:
+			{
+				u64 u = big_int_to_u64(&v.value_integer);
+				cgValue x = cg_value(tb_inst_uint(p->func, TB_TYPE_PTR, u), t_rawptr);
+				value = cg_emit_conv(p, x, proc_expr->tav.type);
+				break;
+			}
+		case ExactValue_Pointer:
+			{
+				u64 u = cast(u64)v.value_pointer;
+				cgValue x = cg_value(tb_inst_uint(p->func, TB_TYPE_PTR, u), t_rawptr);
+				value = cg_emit_conv(p, x, proc_expr->tav.type);
+				break;
+			}
+		}
+	}
+
+	if (value.node == nullptr) {
+		value = cg_build_expr(p, proc_expr);
+	}
+	if (value.kind == cgValue_Addr) {
+		value = cg_emit_load(p, value);
+	}
+	GB_ASSERT(value.kind == cgValue_Value);
+	GB_ASSERT(value.node != nullptr);
+	GB_ASSERT(is_type_proc(value.type));
+
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	Type *proc_type_ = base_type(value.type);
+	GB_ASSERT(proc_type_->kind == Type_Proc);
+	TypeProc *pt = &proc_type_->Proc;
+
+	GB_ASSERT(ce->split_args != nullptr);
+
+	isize internal_param_count = 0;
+	if (pt->params) for (Entity *e : pt->params->Tuple.variables) {
+		if (e->kind == Entity_Variable) {
+			internal_param_count += 1;
+		}
+	}
+	GB_ASSERT(internal_param_count <= pt->param_count);
+
+	auto args = array_make<cgValue>(temporary_allocator(), 0, internal_param_count);
+
+	bool vari_expand = (ce->ellipsis.pos.line != 0);
+	bool is_c_vararg = pt->c_vararg;
+
+	for_array(i, ce->split_args->positional) {
+		Entity *e = pt->params->Tuple.variables[i];
+		if (e->kind == Entity_TypeName) {
+			continue;
+		} else if (e->kind == Entity_Constant) {
+			continue;
+		}
+
+		GB_ASSERT(e->kind == Entity_Variable);
+
+		if (pt->variadic && pt->variadic_index == i) {
+			cgValue variadic_args = cg_const_nil(p, e->type);
+			auto variadic = slice(ce->split_args->positional, pt->variadic_index, ce->split_args->positional.count);
+			if (variadic.count != 0) {
+				// variadic call argument generation
+				Type *slice_type = e->type;
+				GB_ASSERT(slice_type->kind == Type_Slice);
+
+				if (is_c_vararg) {
+					GB_ASSERT(!vari_expand);
+
+					Type *elem_type = slice_type->Slice.elem;
+
+					for (Ast *var_arg : variadic) {
+						cgValue arg = cg_build_expr(p, var_arg);
+						if (is_type_any(elem_type)) {
+							array_add(&args, cg_emit_conv(p, arg, default_type(arg.type)));
+						} else {
+							array_add(&args, cg_emit_conv(p, arg, elem_type));
+						}
+					}
+					break;
+				} else if (vari_expand) {
+					GB_ASSERT(variadic.count == 1);
+					variadic_args = cg_build_expr(p, variadic[0]);
+					variadic_args = cg_emit_conv(p, variadic_args, slice_type);
+				} else {
+					Type *elem_type = slice_type->Slice.elem;
+
+					auto var_args = array_make<cgValue>(temporary_allocator(), 0, variadic.count);
+					for (Ast *var_arg : variadic) {
+						cgValue v = cg_build_expr(p, var_arg);
+						cg_append_tuple_values(p, &var_args, v);
+					}
+					isize slice_len = var_args.count;
+					if (slice_len > 0) {
+						cgAddr slice = cg_add_local(p, slice_type, nullptr, true);
+						cgAddr base_array = cg_add_local(p, alloc_type_array(elem_type, slice_len), nullptr, true);
+
+						for (isize i = 0; i < var_args.count; i++) {
+							cgValue addr = cg_emit_array_epi(p, base_array.addr, cast(i32)i);
+							cgValue var_arg = var_args[i];
+							var_arg = cg_emit_conv(p, var_arg, elem_type);
+							cg_emit_store(p, addr, var_arg);
+						}
+
+						cgValue base_elem = cg_emit_array_epi(p, base_array.addr, 0);
+						cgValue len = cg_const_int(p, t_int, slice_len);
+						cg_fill_slice(p, slice, base_elem, len);
+
+						variadic_args = cg_addr_load(p, slice);
+					}
+				}
+			}
+			array_add(&args, variadic_args);
+
+			break;
+		} else {
+			cgValue value = cg_build_expr(p, ce->split_args->positional[i]);
+			cg_append_tuple_values(p, &args, value);
+		}
+	}
+
+	if (!is_c_vararg) {
+		array_resize(&args, internal_param_count);
+	}
+
+	for (Ast *arg : ce->split_args->named) {
+		ast_node(fv, FieldValue, arg);
+		GB_ASSERT(fv->field->kind == Ast_Ident);
+		String name = fv->field->Ident.token.string;
+		gb_unused(name);
+		isize param_index = lookup_procedure_parameter(pt, name);
+		GB_ASSERT(param_index >= 0);
+
+		cgValue value = cg_build_expr(p, fv->value);
+		GB_ASSERT(!is_type_tuple(value.type));
+		args[param_index] = value;
+	}
+
+	TokenPos pos = ast_token(ce->proc).pos;
+
+
+	if (pt->params != nullptr)  {
+		isize min_count = internal_param_count;
+		if (is_c_vararg) {
+			min_count -= 1;
+		}
+		GB_ASSERT_MSG(args.count >= min_count, "in %.*s", LIT(p->name));
+		isize arg_index = 0;
+		for_array(param_index, pt->params->Tuple.variables) {
+			Entity *e = pt->params->Tuple.variables[param_index];
+			if (e->kind == Entity_TypeName) {
+				continue;
+			} else if (e->kind == Entity_Constant) {
+				continue;
+			}
+			GB_ASSERT(e->kind == Entity_Variable);
+
+			if (pt->variadic && param_index == pt->variadic_index) {
+				if (!is_c_vararg && args[arg_index].node == nullptr) {
+					args[arg_index++] = cg_const_nil(p, e->type);
+				}
+				continue;
+			}
+
+			cgValue arg = args[arg_index];
+			if (arg.node == nullptr) {
+				GB_ASSERT(e->kind == Entity_Variable);
+				args[arg_index++] = cg_handle_param_value(p, e->type, e->Variable.param_value, pos);
+			} else {
+				args[arg_index++] = cg_emit_conv(p, arg, e->type);
+			}
+		}
+	}
+
+	isize final_count = is_c_vararg ? args.count : internal_param_count;
+	auto call_args = slice(args, 0, final_count);
+
+	return cg_emit_call(p, value, call_args);
+}
+
+
+
+gb_internal cgValue cg_hasher_proc_value_for_type(cgProcedure *p, Type *type) {
+	cgProcedure *found = cg_hasher_proc_for_type(p->module, type);
+	return cg_value(tb_inst_get_symbol_address(p->func, found->symbol), found->type);
+}
+
+gb_internal cgValue cg_equal_proc_value_for_type(cgProcedure *p, Type *type) {
+	cgProcedure *found = cg_equal_proc_for_type(p->module, type);
+	return cg_value(tb_inst_get_symbol_address(p->func, found->symbol), found->type);
+}
+
+
+
+gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type) {
+	type = base_type(type);
+	GB_ASSERT(is_type_comparable(type));
+
+	mutex_lock(&m->generated_procs_mutex);
+	defer (mutex_unlock(&m->generated_procs_mutex));
+
+	cgProcedure **found = map_get(&m->equal_procs, type);
+	if (found) {
+		return *found;
+	}
+
+	static std::atomic<u32> proc_index;
+
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+
+	cgProcedure *p = cg_procedure_create_dummy(m, proc_name, t_equal_proc);
+	map_set(&m->equal_procs, type, p);
+
+	cg_procedure_begin(p);
+
+	TB_Node *x = tb_inst_param(p->func, 0);
+	TB_Node *y = tb_inst_param(p->func, 1);
+	GB_ASSERT(x->dt.type == TB_PTR);
+	GB_ASSERT(y->dt.type == TB_PTR);
+
+	TB_DataType ret_dt = TB_PROTOTYPE_RETURNS(p->proto)->dt;
+
+	TB_Node *node_true  = tb_inst_uint(p->func, ret_dt, true);
+	TB_Node *node_false = tb_inst_uint(p->func, ret_dt, false);
+
+	TB_Node *same_ptr_region = cg_control_region(p, "same_ptr");
+	TB_Node *diff_ptr_region = cg_control_region(p, "diff_ptr");
+
+	TB_Node *is_same_ptr = tb_inst_cmp_eq(p->func, x, y);
+	tb_inst_if(p->func, is_same_ptr, same_ptr_region, diff_ptr_region);
+
+	tb_inst_set_control(p->func, same_ptr_region);
+	tb_inst_ret(p->func, 1, &node_true);
+
+	tb_inst_set_control(p->func, diff_ptr_region);
+
+	Type *pt = alloc_type_pointer(type);
+	cgValue lhs = cg_value(x, pt);
+	cgValue rhs = cg_value(y, pt);
+
+	if (type->kind == Type_Struct) {
+		type_set_offsets(type);
+
+		TB_Node *false_region  = cg_control_region(p, "bfalse");
+		cgValue res = cg_const_bool(p, t_bool, true);
+
+		for_array(i, type->Struct.fields) {
+			TB_Node *next_region = cg_control_region(p, "btrue");
+
+			cgValue plhs  = cg_emit_struct_ep(p, lhs, i);
+			cgValue prhs  = cg_emit_struct_ep(p, rhs, i);
+			cgValue left  = cg_emit_load(p, plhs);
+			cgValue right = cg_emit_load(p, prhs);
+			cgValue ok    = cg_emit_comp(p, Token_CmpEq, left, right);
+
+			cg_emit_if(p, ok, next_region, false_region);
+
+			cg_emit_goto(p, next_region);
+			tb_inst_set_control(p->func, next_region);
+		}
+
+		tb_inst_ret(p->func, 1, &node_true);
+		tb_inst_set_control(p->func, false_region);
+		tb_inst_ret(p->func, 1, &node_false);
+
+	} else if (type->kind == Type_Union) {
+		if (type_size_of(type) == 0) {
+			tb_inst_ret(p->func, 1, &node_true);
+		} else if (is_type_union_maybe_pointer(type)) {
+			Type *v = type->Union.variants[0];
+			Type *pv = alloc_type_pointer(v);
+
+			cgValue left  = cg_emit_load(p, cg_emit_conv(p, lhs, pv));
+			cgValue right = cg_emit_load(p, cg_emit_conv(p, rhs, pv));
+			cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right);
+			cg_build_return_stmt_internal_single(p, ok);
+		} else {
+			TB_Node *false_region  = cg_control_region(p, "bfalse");
+			TB_Node *switch_region = cg_control_region(p, "bswitch");
+
+			cgValue lhs_tag = cg_emit_load(p, cg_emit_union_tag_ptr(p, lhs));
+			cgValue rhs_tag = cg_emit_load(p, cg_emit_union_tag_ptr(p, rhs));
+
+			cgValue tag_eq = cg_emit_comp(p, Token_CmpEq, lhs_tag, rhs_tag);
+			cg_emit_if(p, tag_eq, switch_region, false_region);
+
+			size_t entry_count = type->Union.variants.count;
+			TB_SwitchEntry *keys = gb_alloc_array(temporary_allocator(), TB_SwitchEntry, entry_count);
+			for (size_t i = 0; i < entry_count; i++) {
+				TB_Node *region = cg_control_region(p, "bcase");
+				Type *variant = type->Union.variants[i];
+				keys[i].key = union_variant_index(type, variant);
+				keys[i].value = region;
+
+				tb_inst_set_control(p->func, region);
+				Type *vp = alloc_type_pointer(variant);
+				cgValue left  = cg_emit_load(p, cg_emit_conv(p, lhs, vp));
+				cgValue right = cg_emit_load(p, cg_emit_conv(p, rhs, vp));
+				cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right);
+				cg_build_return_stmt_internal_single(p, ok);
+			}
+
+
+			tb_inst_set_control(p->func, switch_region);
+			TB_DataType tag_dt = cg_data_type(lhs_tag.type);
+			GB_ASSERT(lhs_tag.kind == cgValue_Value);
+			tb_inst_branch(p->func, tag_dt, lhs_tag.node, false_region, entry_count, keys);
+
+			tb_inst_set_control(p->func, false_region);
+			tb_inst_ret(p->func, 1, &node_false);
+		}
+	} else {
+		cgValue left  = cg_lvalue_addr(x, type);
+		cgValue right = cg_lvalue_addr(y, type);
+		cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right);
+		cg_build_return_stmt_internal_single(p, ok);
+	}
+
+	cg_procedure_end(p);
+
+	return p;
+}
+
+
+gb_internal cgValue cg_simple_compare_hash(cgProcedure *p, Type *type, cgValue data, cgValue seed) {
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
+
+	auto args = slice_make<cgValue>(temporary_allocator(), 3);
+	args[0] = data;
+	args[1] = seed;
+	args[2] = cg_const_int(p, t_int, type_size_of(type));
+	return cg_emit_runtime_call(p, "default_hasher", args);
+}
+
+
+
+
+
+gb_internal cgProcedure *cg_hasher_proc_for_type(cgModule *m, Type *type) {
+	type = base_type(type);
+	GB_ASSERT(is_type_valid_for_keys(type));
+
+	mutex_lock(&m->generated_procs_mutex);
+	defer (mutex_unlock(&m->generated_procs_mutex));
+
+	cgProcedure **found = map_get(&m->hasher_procs, type);
+	if (found) {
+		return *found;
+	}
+
+	static std::atomic<u32> proc_index;
+
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$hasher%u", 1+proc_index.fetch_add(1));
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+
+	cgProcedure *p = cg_procedure_create_dummy(m, proc_name, t_hasher_proc);
+	map_set(&m->hasher_procs, type, p);
+
+	cg_procedure_begin(p);
+	defer (cg_procedure_end(p));
+
+	TB_Node *x = tb_inst_param(p->func, 0); // data
+	TB_Node *y = tb_inst_param(p->func, 1); // seed
+
+	cgValue data = cg_value(x, t_rawptr);
+	cgValue seed = cg_value(y, t_uintptr);
+
+	if (is_type_simple_compare(type)) {
+		cgValue res = cg_simple_compare_hash(p, type, data, seed);
+		cg_build_return_stmt_internal_single(p, res);
+		return p;
+	}
+
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	auto args = slice_make<cgValue>(temporary_allocator(), 2);
+
+	if (type->kind == Type_Struct) {
+		type_set_offsets(type);
+		for_array(i, type->Struct.fields) {
+			i64 offset = type->Struct.offsets[i];
+			Entity *field = type->Struct.fields[i];
+			cgValue field_hasher = cg_hasher_proc_value_for_type(p, field->type);
+
+			TB_Node *ptr = tb_inst_member_access(p->func, data.node, offset);
+
+			args[0] = cg_value(ptr, alloc_type_pointer(field->type));
+			args[1] = seed;
+			seed = cg_emit_call(p, field_hasher, args);
+		}
+
+		cg_build_return_stmt_internal_single(p, seed);
+	} else if (type->kind == Type_Union) {
+		if (type_size_of(type) == 0) {
+			cg_build_return_stmt_internal_single(p, seed);
+		} else if (is_type_union_maybe_pointer(type)) {
+			Type *v = type->Union.variants[0];
+			cgValue variant_hasher = cg_hasher_proc_value_for_type(p, v);
+
+			args[0] = data;
+			args[1] = seed;
+			cgValue res = cg_emit_call(p, variant_hasher, args);
+			cg_build_return_stmt_internal_single(p, seed);
+		} else {
+			TB_Node *end_region = cg_control_region(p, "bend");
+			TB_Node *switch_region = cg_control_region(p, "bswitch");
+
+			cg_emit_goto(p, switch_region);
+
+			size_t entry_count = type->Union.variants.count;
+			TB_SwitchEntry *keys = gb_alloc_array(temporary_allocator(), TB_SwitchEntry, entry_count);
+			for (size_t i = 0; i < entry_count; i++) {
+				TB_Node *region = cg_control_region(p, "bcase");
+				Type *variant = type->Union.variants[i];
+				keys[i].key = union_variant_index(type, variant);
+				keys[i].value = region;
+
+				tb_inst_set_control(p->func, region);
+
+				cgValue variant_hasher = cg_hasher_proc_value_for_type(p, variant);
+
+				args[0] = data;
+				args[1] = seed;
+				cgValue res = cg_emit_call(p, variant_hasher, args);
+				cg_build_return_stmt_internal_single(p, res);
+			}
+
+			tb_inst_set_control(p->func, switch_region);
+
+			cgValue tag_ptr = cg_emit_union_tag_ptr(p, data);
+			cgValue tag = cg_emit_load(p, tag_ptr);
+
+			TB_DataType tag_dt = cg_data_type(tag.type);
+			GB_ASSERT(tag.kind == cgValue_Value);
+			tb_inst_branch(p->func, tag_dt, tag.node, end_region, entry_count, keys);
+
+			tb_inst_set_control(p->func, end_region);
+			cg_build_return_stmt_internal_single(p, seed);
+		}
+	} else if (type->kind == Type_Array) {
+		cgAddr pres = cg_add_local(p, t_uintptr, nullptr, false);
+		cg_addr_store(p, pres, seed);
+
+		cgValue elem_hasher = cg_hasher_proc_value_for_type(p, type->Array.elem);
+
+		auto loop_data = cg_loop_start(p, type->Array.count, t_int);
+
+		i64 stride = type_size_of(type->Array.elem);
+		TB_Node *ptr = tb_inst_array_access(p->func, data.node, loop_data.index.node, stride);
+		args[0] = cg_value(ptr, alloc_type_pointer(type->Array.elem));
+		args[1] = cg_addr_load(p, pres);
+
+		cgValue new_seed = cg_emit_call(p, elem_hasher, args);
+		cg_addr_store(p, pres, new_seed);
+
+		cg_loop_end(p, loop_data);
+
+		cgValue res = cg_addr_load(p, pres);
+		cg_build_return_stmt_internal_single(p, res);
+	} else if (type->kind == Type_EnumeratedArray) {
+		cgAddr pres = cg_add_local(p, t_uintptr, nullptr, false);
+		cg_addr_store(p, pres, seed);
+
+		cgValue elem_hasher = cg_hasher_proc_value_for_type(p, type->EnumeratedArray.elem);
+
+		auto loop_data = cg_loop_start(p, type->EnumeratedArray.count, t_int);
+
+		i64 stride = type_size_of(type->EnumeratedArray.elem);
+		TB_Node *ptr = tb_inst_array_access(p->func, data.node, loop_data.index.node, stride);
+		args[0] = cg_value(ptr, alloc_type_pointer(type->EnumeratedArray.elem));
+		args[1] = cg_addr_load(p, pres);
+
+		cgValue new_seed = cg_emit_call(p, elem_hasher, args);
+		cg_addr_store(p, pres, new_seed);
+
+		cg_loop_end(p, loop_data);
+
+		cgValue res = cg_addr_load(p, pres);
+		cg_build_return_stmt_internal_single(p, res);
+	} else if (is_type_cstring(type)) {
+		args[0] = data;
+		args[1] = seed;
+		cgValue res = cg_emit_runtime_call(p, "default_hasher_cstring", args);
+		cg_build_return_stmt_internal_single(p, seed);
+	} else if (is_type_string(type)) {
+		args[0] = data;
+		args[1] = seed;
+		cgValue res = cg_emit_runtime_call(p, "default_hasher_string", args);
+		cg_build_return_stmt_internal_single(p, seed);
+	} else {
+		GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
+	}
+	return p;
+}

+ 2614 - 0
src/tilde_stmt.cpp

@@ -0,0 +1,2614 @@
+gb_internal bool cg_emit_goto(cgProcedure *p, TB_Node *control_region) {
+	if (tb_inst_get_control(p->func)) {
+		tb_inst_goto(p->func, control_region);
+		return true;
+	}
+	return false;
+}
+
+gb_internal TB_Node *cg_control_region(cgProcedure *p, char const *name) {
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	isize n = gb_strlen(name);
+
+	char *new_name = gb_alloc_array(temporary_allocator(), char, n+12);
+	n = -1 + gb_snprintf(new_name, n+11, "%.*s_%u", cast(int)n, name, p->control_regions.count);
+
+	TB_Node *region = tb_inst_region(p->func);
+	tb_inst_set_region_name(region, n, new_name);
+
+	GB_ASSERT(p->scope_index >= 0);
+	array_add(&p->control_regions, cgControlRegion{region, p->scope_index});
+
+	return region;
+}
+
+gb_internal cgValue cg_emit_load(cgProcedure *p, cgValue const &ptr, bool is_volatile) {
+	GB_ASSERT(is_type_pointer(ptr.type));
+	Type *type = type_deref(ptr.type);
+	TB_DataType dt = cg_data_type(type);
+
+	if (TB_IS_VOID_TYPE(dt)) {
+		switch (ptr.kind) {
+		case cgValue_Value:
+			return cg_lvalue_addr(ptr.node, type);
+		case cgValue_Addr:
+			GB_PANIC("NOT POSSIBLE - Cannot load an lvalue to begin with");
+			break;
+		case cgValue_Multi:
+			GB_PANIC("NOT POSSIBLE - Cannot load multiple values at once");
+			break;
+		case cgValue_Symbol:
+			return cg_lvalue_addr(tb_inst_get_symbol_address(p->func, ptr.symbol), type);
+		}
+	}
+
+	// use the natural alignment
+	// if people need a special alignment, they can use `intrinsics.unaligned_load`
+	TB_CharUnits alignment = cast(TB_CharUnits)type_align_of(type);
+
+	TB_Node *the_ptr = nullptr;
+	switch (ptr.kind) {
+	case cgValue_Value:
+		the_ptr = ptr.node;
+		break;
+	case cgValue_Addr:
+		the_ptr = tb_inst_load(p->func, TB_TYPE_PTR, ptr.node, alignment, is_volatile);
+		break;
+	case cgValue_Multi:
+		GB_PANIC("NOT POSSIBLE - Cannot load multiple values at once");
+		break;
+	case cgValue_Symbol:
+		the_ptr = tb_inst_get_symbol_address(p->func, ptr.symbol);
+		break;
+	}
+	return cg_value(tb_inst_load(p->func, dt, the_ptr, alignment, is_volatile), type);
+}
+
+gb_internal void cg_emit_store(cgProcedure *p, cgValue dst, cgValue src, bool is_volatile) {
+	GB_ASSERT_MSG(dst.kind != cgValue_Multi, "cannot store to multiple values at once");
+
+	if (dst.kind == cgValue_Addr) {
+		dst = cg_emit_load(p, dst, is_volatile);
+	} else if (dst.kind == cgValue_Symbol) {
+		dst = cg_value(tb_inst_get_symbol_address(p->func, dst.symbol), dst.type);
+	}
+
+	GB_ASSERT(is_type_pointer(dst.type));
+	Type *dst_type = type_deref(dst.type);
+
+	GB_ASSERT_MSG(are_types_identical(dst_type, src.type), "%s vs %s", type_to_string(dst_type), type_to_string(src.type));
+
+	TB_DataType dt = cg_data_type(dst_type);
+	TB_DataType st = cg_data_type(src.type);
+	GB_ASSERT(dt.raw == st.raw);
+
+	// use the natural alignment
+	// if people need a special alignment, they can use `intrinsics.unaligned_store`
+	TB_CharUnits alignment = cast(TB_CharUnits)type_align_of(dst_type);
+
+	if (TB_IS_VOID_TYPE(dt)) {
+		TB_Node *dst_ptr = nullptr;
+		TB_Node *src_ptr = nullptr;
+
+		switch (dst.kind) {
+		case cgValue_Value:
+			dst_ptr	= dst.node;
+			break;
+		case cgValue_Addr:
+			GB_PANIC("DST cgValue_Addr should be handled above");
+			break;
+		case cgValue_Symbol:
+			dst_ptr = tb_inst_get_symbol_address(p->func, dst.symbol);
+			break;
+		}
+
+		switch (src.kind) {
+		case cgValue_Value:
+			GB_PANIC("SRC cgValue_Value should be handled above");
+			break;
+		case cgValue_Symbol:
+			GB_PANIC("SRC cgValue_Symbol should be handled above");
+			break;
+		case cgValue_Addr:
+			src_ptr = src.node;
+			break;
+		}
+
+		// IMPORTANT TODO(bill): needs to be memmove
+		i64 sz = type_size_of(dst_type);
+		TB_Node *count = tb_inst_uint(p->func, TB_TYPE_INT, cast(u64)sz);
+		tb_inst_memcpy(p->func, dst_ptr, src_ptr, count, alignment, is_volatile);
+		return;
+	}
+
+
+	switch (dst.kind) {
+	case cgValue_Value:
+		switch (src.kind) {
+		case cgValue_Value:
+			if (src.node->dt.type == TB_INT && src.node->dt.data == 1) {
+				src.node = tb_inst_zxt(p->func, src.node, dt);
+			}
+			tb_inst_store(p->func, dt, dst.node, src.node, alignment, is_volatile);
+			return;
+		case cgValue_Addr:
+			tb_inst_store(p->func, dt, dst.node,
+			              tb_inst_load(p->func, st, src.node, alignment, is_volatile),
+			              alignment, is_volatile);
+			return;
+		case cgValue_Symbol:
+			tb_inst_store(p->func, dt, dst.node,
+			              tb_inst_get_symbol_address(p->func, src.symbol),
+			              alignment, is_volatile);
+			return;
+		}
+	case cgValue_Addr:
+		GB_PANIC("cgValue_Addr should be handled above");
+		break;
+	case cgValue_Symbol:
+		GB_PANIC(" cgValue_Symbol should be handled above");
+		break;
+	}
+}
+
+
+gb_internal cgValue cg_address_from_load(cgProcedure *p, cgValue value) {
+	switch (value.kind) {
+	case cgValue_Value:
+		{
+			TB_Node *load_inst = value.node;
+			GB_ASSERT_MSG(load_inst->type == TB_LOAD, "expected a load instruction");
+			TB_Node *ptr = load_inst->inputs[1];
+			return cg_value(ptr, alloc_type_pointer(value.type));
+		}
+	case cgValue_Addr:
+		return cg_value(value.node, alloc_type_pointer(value.type));
+	case cgValue_Symbol:
+		GB_PANIC("Symbol is an invalid use case for cg_address_from_load");
+		return {};
+	case cgValue_Multi:
+		GB_PANIC("Multi is an invalid use case for cg_address_from_load");
+		break;
+	}
+	GB_PANIC("Invalid cgValue for cg_address_from_load");
+	return {};
+
+}
+
+gb_internal bool cg_addr_is_empty(cgAddr const &addr) {
+	switch (addr.kind) {
+	case cgValue_Value:
+	case cgValue_Addr:
+		return addr.addr.node == nullptr;
+	case cgValue_Symbol:
+		return addr.addr.symbol == nullptr;
+	case cgValue_Multi:
+		return addr.addr.multi == nullptr;
+	}
+	return true;
+}
+
+gb_internal Type *cg_addr_type(cgAddr const &addr) {
+	if (cg_addr_is_empty(addr)) {
+		return nullptr;
+	}
+	switch (addr.kind) {
+	case cgAddr_Map:
+		{
+			Type *t = base_type(addr.map.type);
+			GB_ASSERT(is_type_map(t));
+			return t->Map.value;
+		}
+	case cgAddr_Swizzle:
+		return addr.swizzle.type;
+	case cgAddr_SwizzleLarge:
+		return addr.swizzle_large.type;
+	case cgAddr_Context:
+		if (addr.ctx.sel.index.count > 0) {
+			Type *t = t_context;
+			for_array(i, addr.ctx.sel.index) {
+				GB_ASSERT(is_type_struct(t));
+				t = base_type(t)->Struct.fields[addr.ctx.sel.index[i]]->type;
+			}
+			return t;
+		}
+		break;
+	}
+	return type_deref(addr.addr.type);
+}
+
+gb_internal cgValue cg_addr_load(cgProcedure *p, cgAddr addr) {
+	if (addr.addr.node == nullptr) {
+		return {};
+	}
+	switch (addr.kind) {
+	case cgAddr_Default:
+		return cg_emit_load(p, addr.addr);
+	}
+	GB_PANIC("TODO(bill): cg_addr_load %p", addr.addr.node);
+	return {};
+}
+
+
+gb_internal void cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value) {
+	if (cg_addr_is_empty(addr)) {
+		return;
+	}
+	GB_ASSERT(value.type != nullptr);
+	if (is_type_untyped_uninit(value.type)) {
+		Type *t = cg_addr_type(addr);
+		value = cg_value(tb_inst_poison(p->func), t);
+		// TODO(bill): IS THIS EVEN A GOOD IDEA?
+	} else if (is_type_untyped_nil(value.type)) {
+		Type *t = cg_addr_type(addr);
+		value = cg_const_nil(p, t);
+	}
+
+	if (addr.kind == cgAddr_RelativePointer && addr.relative.deref) {
+		addr = cg_addr(cg_address_from_load(p, cg_addr_load(p, addr)));
+	}
+
+	if (addr.kind == cgAddr_RelativePointer) {
+		GB_PANIC("TODO(bill): cgAddr_RelativePointer");
+	} else if (addr.kind == cgAddr_RelativeSlice) {
+		GB_PANIC("TODO(bill): cgAddr_RelativeSlice");
+	} else if (addr.kind == cgAddr_Map) {
+		GB_PANIC("TODO(bill): cgAddr_Map");
+	} else if (addr.kind == cgAddr_Context) {
+		cgAddr old_addr = cg_find_or_generate_context_ptr(p);
+
+		bool create_new = true;
+		for_array(i, p->context_stack) {
+			cgContextData *ctx_data = &p->context_stack[i];
+			if (ctx_data->ctx.addr.node == old_addr.addr.node) {
+				if (ctx_data->uses > 0) {
+					create_new = true;
+				} else if (p->scope_index > ctx_data->scope_index) {
+					create_new = true;
+				} else {
+					// gb_printf_err("%.*s (curr:%td) (ctx:%td) (uses:%td)\n", LIT(p->name), p->scope_index, ctx_data->scope_index, ctx_data->uses);
+					create_new = false;
+				}
+				break;
+			}
+		}
+
+		cgValue next = {};
+		if (create_new) {
+			cgValue old = cg_addr_load(p, old_addr);
+			cgAddr next_addr = cg_add_local(p, t_context, nullptr, true);
+			cg_addr_store(p, next_addr, old);
+			cg_push_context_onto_stack(p, next_addr);
+			next = next_addr.addr;
+		} else {
+			next = old_addr.addr;
+		}
+
+		if (addr.ctx.sel.index.count > 0) {
+			cgValue lhs = cg_emit_deep_field_gep(p, next, addr.ctx.sel);
+			cgValue rhs = cg_emit_conv(p, value, type_deref(lhs.type));
+			cg_emit_store(p, lhs, rhs);
+		} else {
+			cgValue lhs = next;
+			cgValue rhs = cg_emit_conv(p, value, cg_addr_type(addr));
+			cg_emit_store(p, lhs, rhs);
+		}
+		return;
+	} else if (addr.kind == cgAddr_SoaVariable) {
+		GB_PANIC("TODO(bill): cgAddr_SoaVariable");
+	} else if (addr.kind == cgAddr_Swizzle) {
+		GB_ASSERT(addr.swizzle.count <= 4);
+		GB_PANIC("TODO(bill): cgAddr_Swizzle");
+	} else if (addr.kind == cgAddr_SwizzleLarge) {
+		GB_PANIC("TODO(bill): cgAddr_SwizzleLarge");
+	}
+
+	value = cg_emit_conv(p, value, cg_addr_type(addr));
+	cg_emit_store(p, addr.addr, value);
+}
+
+gb_internal cgValue cg_addr_get_ptr(cgProcedure *p, cgAddr const &addr) {
+	if (cg_addr_is_empty(addr)) {
+		GB_PANIC("Illegal addr -> nullptr");
+		return {};
+	}
+
+	switch (addr.kind) {
+	case cgAddr_Map:
+		GB_PANIC("TODO(bill): cg_addr_get_ptr cgAddr_Map");
+		// return cg_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
+		break;
+
+	case cgAddr_RelativePointer: {
+		Type *rel_ptr = base_type(cg_addr_type(addr));
+		GB_ASSERT(rel_ptr->kind == Type_RelativePointer);
+
+		cgValue ptr = cg_emit_conv(p, addr.addr, t_uintptr);
+		cgValue offset = cg_emit_conv(p, ptr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
+		offset = cg_emit_load(p, offset);
+
+		if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
+			offset = cg_emit_conv(p, offset, t_i64);
+		}
+		offset = cg_emit_conv(p, offset, t_uintptr);
+
+		cgValue absolute_ptr = cg_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
+		absolute_ptr = cg_emit_conv(p, absolute_ptr, rel_ptr->RelativePointer.pointer_type);
+
+		GB_PANIC("TODO(bill): cg_addr_get_ptr cgAddr_RelativePointer");
+		// cgValue cond = cg_emit_comp(p, Token_CmpEq, offset, cg_const_nil(p->module, rel_ptr->RelativePointer.base_integer));
+
+		// NOTE(bill): nil check
+		// cgValue nil_ptr = cg_const_nil(p->module, rel_ptr->RelativePointer.pointer_type);
+		// cgValue final_ptr = cg_emit_select(p, cond, nil_ptr, absolute_ptr);
+		// return final_ptr;
+		break;
+	}
+
+	case cgAddr_SoaVariable:
+		// TODO(bill): FIX THIS HACK
+		return cg_address_from_load(p, cg_addr_load(p, addr));
+
+	case cgAddr_Context:
+		GB_PANIC("cgAddr_Context should be handled elsewhere");
+		break;
+
+	case cgAddr_Swizzle:
+	case cgAddr_SwizzleLarge:
+		// TOOD(bill): is this good enough logic?
+		break;
+	}
+
+	return addr.addr;
+}
+
+gb_internal cgValue cg_emit_ptr_offset(cgProcedure *p, cgValue ptr, cgValue index) {
+	GB_ASSERT(ptr.kind == cgValue_Value);
+	GB_ASSERT(index.kind == cgValue_Value);
+	GB_ASSERT(is_type_pointer(ptr.type) || is_type_multi_pointer(ptr.type));
+	GB_ASSERT(is_type_integer(index.type));
+
+	Type *elem = type_deref(ptr.type, true);
+	i64 stride = type_size_of(elem);
+	return cg_value(tb_inst_array_access(p->func, ptr.node, index.node, stride), alloc_type_pointer(elem));
+}
+gb_internal cgValue cg_emit_array_ep(cgProcedure *p, cgValue s, cgValue index) {
+	GB_ASSERT(s.kind == cgValue_Value);
+	GB_ASSERT(index.kind == cgValue_Value);
+
+	Type *t = s.type;
+	GB_ASSERT_MSG(is_type_pointer(t), "%s", type_to_string(t));
+	Type *st = base_type(type_deref(t));
+	GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st));
+	GB_ASSERT_MSG(is_type_integer(core_type(index.type)), "%s", type_to_string(index.type));
+
+
+	Type *elem = base_array_type(st);
+	i64 stride = type_size_of(elem);
+	return cg_value(tb_inst_array_access(p->func, s.node, index.node, stride), alloc_type_pointer(elem));
+}
+gb_internal cgValue cg_emit_array_epi(cgProcedure *p, cgValue s, i64 index) {
+	return cg_emit_array_ep(p, s, cg_const_int(p, t_int, index));
+}
+
+
+gb_internal cgValue cg_emit_struct_ep(cgProcedure *p, cgValue s, i64 index) {
+	s = cg_flatten_value(p, s);
+
+	GB_ASSERT(is_type_pointer(s.type));
+	Type *t = base_type(type_deref(s.type));
+	Type *result_type = nullptr;
+
+	if (is_type_relative_pointer(t)) {
+		s = cg_addr_get_ptr(p, cg_addr(s));
+	}
+	i64 offset = -1;
+	i64 int_size = build_context.int_size;
+	i64 ptr_size = build_context.ptr_size;
+
+	switch (t->kind) {
+	case Type_Struct:
+		type_set_offsets(t);
+		result_type = t->Struct.fields[index]->type;
+		offset = t->Struct.offsets[index];
+		break;
+	case Type_Union:
+		GB_ASSERT(index == -1);
+		GB_PANIC("TODO(bill): cg_emit_union_tag_ptr");
+		break;
+		// return cg_emit_union_tag_ptr(p, s);
+	case Type_Tuple:
+		type_set_offsets(t);
+		result_type = t->Tuple.variables[index]->type;
+		offset = t->Tuple.offsets[index];
+		GB_PANIC("TODO(bill): cg_emit_tuple_ep %d", s.kind);
+		break;
+		// return cg_emit_tuple_ep(p, s, index);
+	case Type_Slice:
+		switch (index) {
+		case 0:
+			result_type = alloc_type_multi_pointer(t->Slice.elem);
+			offset = 0;
+			break;
+		case 1:
+			result_type = t_int;
+			offset = int_size;
+			break;
+		}
+		break;
+	case Type_Basic:
+		switch (t->Basic.kind) {
+		case Basic_string:
+			switch (index) {
+			case 0:
+				result_type = t_u8_multi_ptr;
+				offset = 0;
+				break;
+			case 1:
+				result_type = t_int;
+				offset = int_size;
+				break;
+			}
+			break;
+		case Basic_any:
+			switch (index) {
+			case 0:
+				result_type = t_rawptr;
+				offset = 0;
+				break;
+			case 1:
+				result_type = t_typeid;
+				offset = ptr_size;
+				break;
+			}
+			break;
+
+		case Basic_complex32:
+		case Basic_complex64:
+		case Basic_complex128:
+			{
+				Type *ft = base_complex_elem_type(t);
+				i64 sz = type_size_of(ft);
+				switch (index) {
+				case 0: case 1:
+					result_type = ft; offset = sz * index; break;
+				default: goto error_case;
+				}
+				break;
+			}
+		case Basic_quaternion64:
+		case Basic_quaternion128:
+		case Basic_quaternion256:
+			{
+				Type *ft = base_complex_elem_type(t);
+				i64 sz = type_size_of(ft);
+				switch (index) {
+				case 0: case 1: case 2: case 3:
+					result_type = ft; offset = sz * index; break;
+				default: goto error_case;
+				}
+			}
+			break;
+		default:
+			goto error_case;
+		}
+		break;
+	case Type_DynamicArray:
+		switch (index) {
+		case 0:
+			result_type = alloc_type_multi_pointer(t->DynamicArray.elem);
+			offset = index*int_size;
+			break;
+		case 1: case 2:
+			result_type = t_int;
+			offset = index*int_size;
+			break;
+		case 3:
+			result_type = t_allocator;
+			offset = index*int_size;
+			break;
+		default: goto error_case;
+		}
+		break;
+	case Type_Map:
+		{
+			init_map_internal_types(t);
+			Type *itp = alloc_type_pointer(t_raw_map);
+			s = cg_emit_transmute(p, s, itp);
+
+			Type *rms = base_type(t_raw_map);
+			GB_ASSERT(rms->kind == Type_Struct);
+
+			if (0 <= index && index < 3) {
+				result_type = rms->Struct.fields[index]->type;
+				offset = rms->Struct.offsets[index];
+			} else {
+				goto error_case;
+			}
+			break;
+		}
+	case Type_Array:
+		return cg_emit_array_epi(p, s, index);
+	case Type_SoaPointer:
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
+		case 1: result_type = t_int; break;
+		}
+		break;
+	default:
+	error_case:;
+		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(s.type), index);
+		break;
+	}
+
+	GB_ASSERT_MSG(result_type != nullptr, "%s %d", type_to_string(t), index);
+	GB_ASSERT(offset >= 0);
+
+	GB_ASSERT(s.kind == cgValue_Value);
+	return cg_value(
+		tb_inst_member_access(p->func, s.node, offset),
+		alloc_type_pointer(result_type)
+	);
+}
+
+
+gb_internal cgValue cg_emit_struct_ev(cgProcedure *p, cgValue s, i64 index) {
+	s = cg_address_from_load_or_generate_local(p, s);
+	cgValue ptr = cg_emit_struct_ep(p, s, index);
+	return cg_flatten_value(p, cg_emit_load(p, ptr));
+}
+
+
+gb_internal cgValue cg_emit_deep_field_gep(cgProcedure *p, cgValue e, Selection const &sel) {
+	GB_ASSERT(sel.index.count > 0);
+	Type *type = type_deref(e.type);
+
+	for_array(i, sel.index) {
+		i64 index = sel.index[i];
+		if (is_type_pointer(type)) {
+			type = type_deref(type);
+			e = cg_emit_load(p, e);
+		}
+		type = core_type(type);
+
+		switch (type->kind) {
+		case Type_SoaPointer: {
+			cgValue addr = cg_emit_struct_ep(p, e, 0);
+			cgValue index = cg_emit_struct_ep(p, e, 1);
+			addr = cg_emit_load(p, addr);
+			index = cg_emit_load(p, index);
+
+			i64 first_index = sel.index[0];
+			Selection sub_sel = sel;
+			sub_sel.index.data += 1;
+			sub_sel.index.count -= 1;
+
+			cgValue arr = cg_emit_struct_ep(p, addr, first_index);
+
+			Type *t = base_type(type_deref(addr.type));
+			GB_ASSERT(is_type_soa_struct(t));
+
+			if (t->Struct.soa_kind == StructSoa_Fixed) {
+				e = cg_emit_array_ep(p, arr, index);
+			} else {
+				e = cg_emit_ptr_offset(p, cg_emit_load(p, arr), index);
+			}
+			break;
+		}
+		case Type_Basic:
+			switch (type->Basic.kind) {
+			case Basic_any:
+				if (index == 0) {
+					type = t_rawptr;
+				} else if (index == 1) {
+					type = t_type_info_ptr;
+				}
+				e = cg_emit_struct_ep(p, e, index);
+				break;
+			default:
+				e = cg_emit_struct_ep(p, e, index);
+				break;
+			}
+			break;
+		case Type_Struct:
+			if (type->Struct.is_raw_union) {
+				type = get_struct_field_type(type, index);
+				GB_ASSERT(is_type_pointer(e.type));
+				e = cg_emit_transmute(p, e, alloc_type_pointer(type));
+			} else {
+				type = get_struct_field_type(type, index);
+				e = cg_emit_struct_ep(p, e, index);
+			}
+			break;
+		case Type_Union:
+			GB_ASSERT(index == -1);
+			type = t_type_info_ptr;
+			e = cg_emit_struct_ep(p, e, index);
+			break;
+		case Type_Tuple:
+			type = type->Tuple.variables[index]->type;
+			e = cg_emit_struct_ep(p, e, index);
+			break;
+		case Type_Slice:
+		case Type_DynamicArray:
+		case Type_Map:
+		case Type_RelativePointer:
+			e = cg_emit_struct_ep(p, e, index);
+			break;
+		case Type_Array:
+			e = cg_emit_array_epi(p, e, index);
+			break;
+		default:
+			GB_PANIC("un-gep-able type %s", type_to_string(type));
+			break;
+		}
+	}
+
+	return e;
+}
+
+
+
+
+
+
+
+
+gb_internal cgBranchRegions cg_lookup_branch_regions(cgProcedure *p, Ast *ident) {
+	GB_ASSERT(ident->kind == Ast_Ident);
+	Entity *e = entity_of_node(ident);
+	GB_ASSERT(e->kind == Entity_Label);
+	for (cgBranchRegions const &b : p->branch_regions) {
+		if (b.label == e->Label.node) {
+			return b;
+		}
+	}
+
+	GB_PANIC("Unreachable");
+	cgBranchRegions empty = {};
+	return empty;
+}
+
+gb_internal cgTargetList *cg_push_target_list(cgProcedure *p, Ast *label, TB_Node *break_, TB_Node *continue_, TB_Node *fallthrough_) {
+	cgTargetList *tl = gb_alloc_item(permanent_allocator(), cgTargetList);
+	tl->prev = p->target_list;
+	tl->break_ = break_;
+	tl->continue_ = continue_;
+	tl->fallthrough_ = fallthrough_;
+	p->target_list = tl;
+
+	if (label != nullptr) { // Set label blocks
+		GB_ASSERT(label->kind == Ast_Label);
+
+		for (cgBranchRegions &b : p->branch_regions) {
+			GB_ASSERT(b.label != nullptr && label != nullptr);
+			GB_ASSERT(b.label->kind == Ast_Label);
+			if (b.label == label) {
+				b.break_    = break_;
+				b.continue_ = continue_;
+				return tl;
+			}
+		}
+
+		GB_PANIC("Unreachable");
+	}
+
+	return tl;
+}
+
+gb_internal void cg_pop_target_list(cgProcedure *p) {
+	p->target_list = p->target_list->prev;
+}
+gb_internal cgAddr cg_add_local(cgProcedure *p, Type *type, Entity *e, bool zero_init) {
+	GB_ASSERT(type != nullptr);
+
+	isize size = type_size_of(type);
+	TB_CharUnits alignment = cast(TB_CharUnits)type_align_of(type);
+	if (is_type_matrix(type)) {
+		alignment *= 2; // NOTE(bill): Just in case
+	}
+
+	TB_Node *local = tb_inst_local(p->func, cast(u32)size, alignment);
+
+	if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
+		// NOTE(bill): for debugging purposes only
+		String name = e->token.string;
+		TB_DebugType *debug_type = cg_debug_type(p->module, type);
+		tb_node_append_attrib(local, tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, debug_type));
+	}
+
+	if (zero_init) {
+		bool is_volatile = false;
+		TB_Node *zero = tb_inst_uint(p->func, TB_TYPE_I8, 0);
+		TB_Node *count = tb_inst_uint(p->func, TB_TYPE_I32, cast(u64)size);
+		tb_inst_memset(p->func, local, zero, count, alignment, is_volatile);
+	}
+
+	cgAddr addr = cg_addr(cg_value(local, alloc_type_pointer(type)));
+	if (e) {
+		map_set(&p->variable_map, e, addr);
+	}
+	return addr;
+}
+
+gb_internal cgAddr cg_add_global(cgProcedure *p, Type *type, Entity *e) {
+	GB_ASSERT(type != nullptr);
+
+	isize size = type_size_of(type);
+	TB_CharUnits alignment = cast(TB_CharUnits)type_align_of(type);
+	if (is_type_matrix(type)) {
+		alignment *= 2; // NOTE(bill): Just in case
+	}
+
+	TB_Global *global = tb_global_create(p->module->mod, 0, "", nullptr, TB_LINKAGE_PRIVATE);
+	tb_global_set_storage(p->module->mod, tb_module_get_data(p->module->mod), global, size, alignment, 0);
+	TB_Node *local = tb_inst_get_symbol_address(p->func, cast(TB_Symbol *)global);
+
+	if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
+		// NOTE(bill): for debugging purposes only
+		String name = e->token.string;
+		TB_DebugType *debug_type = cg_debug_type(p->module, type);
+		tb_node_append_attrib(local, tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, debug_type));
+	}
+
+	cgAddr addr = cg_addr(cg_value(local, alloc_type_pointer(type)));
+	if (e) {
+		map_set(&p->variable_map, e, addr);
+	}
+	return addr;
+}
+
+
+gb_internal cgValue cg_copy_value_to_ptr(cgProcedure *p, cgValue value, Type *original_type, isize min_alignment) {
+	TB_CharUnits size  = cast(TB_CharUnits)type_size_of(original_type);
+	TB_CharUnits align = cast(TB_CharUnits)gb_max(type_align_of(original_type), min_alignment);
+	TB_Node *copy = tb_inst_local(p->func, size, align);
+	if (value.kind == cgValue_Value) {
+		tb_inst_store(p->func, cg_data_type(original_type), copy, value.node, align, false);
+	} else {
+		GB_ASSERT(value.kind == cgValue_Addr);
+		tb_inst_memcpy(p->func, copy, value.node, tb_inst_uint(p->func, TB_TYPE_INT, size), align, false);
+	}
+
+	return cg_value(copy, alloc_type_pointer(original_type));
+}
+
+gb_internal cgValue cg_address_from_load_or_generate_local(cgProcedure *p, cgValue value) {
+	switch (value.kind) {
+	case cgValue_Value:
+		if (value.node->type == TB_LOAD) {
+			TB_Node *ptr = value.node->inputs[1];
+			return cg_value(ptr, alloc_type_pointer(value.type));
+		}
+		break;
+	case cgValue_Addr:
+		return cg_value(value.node, alloc_type_pointer(value.type));
+	case cgValue_Multi:
+		GB_PANIC("cgValue_Multi not allowed");
+	}
+
+	cgAddr res = cg_add_local(p, value.type, nullptr, false);
+	cg_addr_store(p, res, value);
+	return res.addr;
+}
+
+
+gb_internal void cg_build_defer_stmt(cgProcedure *p, cgDefer const &d) {
+	TB_Node *curr_region = tb_inst_get_control(p->func);
+	if (curr_region == nullptr) {
+		return;
+	}
+
+	// NOTE(bill): The prev block may defer injection before it's terminator
+	TB_Node *last_inst = nullptr;
+	// if (curr_region->input_count) {
+	// 	last_inst = *(curr_region->inputs + curr_region->input_count);
+	// }
+	// if (last_inst && TB_IS_NODE_TERMINATOR(last_inst->type)) {
+	// 	// NOTE(bill): ReturnStmt defer stuff will be handled previously
+	// 	return;
+	// }
+
+	isize prev_context_stack_count = p->context_stack.count;
+	GB_ASSERT(prev_context_stack_count <= p->context_stack.capacity);
+	defer (p->context_stack.count = prev_context_stack_count);
+	p->context_stack.count = d.context_stack_count;
+
+	TB_Node *b = cg_control_region(p, "defer");
+	if (last_inst == nullptr) {
+		cg_emit_goto(p, b);
+	}
+
+	tb_inst_set_control(p->func, b);
+	if (d.kind == cgDefer_Node) {
+		cg_build_stmt(p, d.stmt);
+	} else if (d.kind == cgDefer_Proc) {
+		cg_emit_call(p, d.proc.deferred, d.proc.result_as_args);
+	}
+}
+
+
+gb_internal void cg_emit_defer_stmts(cgProcedure *p, cgDeferExitKind kind, TB_Node *control_region) {
+	isize count = p->defer_stack.count;
+	isize i = count;
+	while (i --> 0) {
+		cgDefer const &d = p->defer_stack[i];
+
+		if (kind == cgDeferExit_Default) {
+			if (p->scope_index == d.scope_index &&
+			    d.scope_index > 0) {
+				cg_build_defer_stmt(p, d);
+				array_pop(&p->defer_stack);
+				continue;
+			} else {
+				break;
+			}
+		} else if (kind == cgDeferExit_Return) {
+			cg_build_defer_stmt(p, d);
+		} else if (kind == cgDeferExit_Branch) {
+			GB_ASSERT(control_region != nullptr);
+			isize lower_limit = -1;
+			for (auto const &cr : p->control_regions) {
+				if (cr.control_region == control_region) {
+					lower_limit = cr.scope_index;
+					break;
+				}
+			}
+			GB_ASSERT(lower_limit >= 0);
+			if (lower_limit < d.scope_index) {
+				cg_build_defer_stmt(p, d);
+			}
+		}
+	}
+}
+
+gb_internal void cg_scope_open(cgProcedure *p, Scope *scope) {
+	// TODO(bill): debug scope information
+
+	p->scope_index += 1;
+	array_add(&p->scope_stack, scope);
+}
+
+gb_internal void cg_scope_close(cgProcedure *p, cgDeferExitKind kind, TB_Node *control_region) {
+	cg_emit_defer_stmts(p, kind, control_region);
+	GB_ASSERT(p->scope_index > 0);
+
+	while (p->context_stack.count > 0) {
+		auto *ctx = &p->context_stack[p->context_stack.count-1];
+		if (ctx->scope_index < p->scope_index) {
+			break;
+		}
+		array_pop(&p->context_stack);
+	}
+
+	p->scope_index -= 1;
+	array_pop(&p->scope_stack);
+}
+
+
+gb_internal isize cg_append_tuple_values(cgProcedure *p, Array<cgValue> *dst_values, cgValue src_value) {
+	isize init_count = dst_values->count;
+	Type *t = src_value.type;
+	if (t && t->kind == Type_Tuple) {
+		GB_ASSERT(src_value.kind == cgValue_Multi);
+		GB_ASSERT(src_value.multi != nullptr);
+		GB_ASSERT(src_value.multi->values.count == t->Tuple.variables.count);
+		for (cgValue const &value : src_value.multi->values) {
+			array_add(dst_values, value);
+		}
+	} else {
+		array_add(dst_values, src_value);
+	}
+	return dst_values->count - init_count;
+}
+gb_internal void cg_build_assignment(cgProcedure *p, Array<cgAddr> const &lvals, Slice<Ast *> const &values) {
+	if (values.count == 0) {
+		return;
+	}
+
+	auto inits = array_make<cgValue>(permanent_allocator(), 0, lvals.count);
+
+	for (Ast *rhs : values) {
+		cgValue init = cg_build_expr(p, rhs);
+		cg_append_tuple_values(p, &inits, init);
+	}
+
+	bool prev_in_assignment = p->in_multi_assignment;
+
+	isize lval_count = 0;
+	for (cgAddr const &lval : lvals) {
+		if (!cg_addr_is_empty(lval)) {
+			// check if it is not a blank identifier
+			lval_count += 1;
+		}
+	}
+	p->in_multi_assignment = lval_count > 1;
+
+	GB_ASSERT(lvals.count == inits.count);
+
+
+	if (inits.count > 1) for_array(i, inits) {
+		cgAddr lval = lvals[i];
+		cgValue init = cg_flatten_value(p, inits[i]);
+
+		GB_ASSERT(init.kind != cgValue_Multi);
+		if (init.type == nullptr) {
+			continue;
+		}
+
+	    	Type *type = cg_addr_type(lval);
+		if (!cg_addr_is_empty(lval)) {
+			GB_ASSERT_MSG(are_types_identical(init.type, type), "%s = %s", type_to_string(init.type), type_to_string(type));
+		}
+
+		if (init.kind == cgValue_Addr &&
+		    !cg_addr_is_empty(lval)) {
+		    	// NOTE(bill): This is needed for certain constructs such as this:
+		    	// a, b = b, a
+		    	// NOTE(bill): This is a bodge and not necessarily a good way of doing things whatsoever
+		    	TB_CharUnits size  = cast(TB_CharUnits)type_size_of(type);
+		    	TB_CharUnits align = cast(TB_CharUnits)type_align_of(type);
+		    	TB_Node *copy = tb_inst_local(p->func, size, align);
+		    	tb_inst_memcpy(p->func, copy, init.node, tb_inst_uint(p->func, TB_TYPE_INT, size), align, false);
+		    	// use the copy instead
+		    	init.node = copy;
+		}
+		inits[i] = init;
+	}
+
+	for_array(i, inits) {
+		cgAddr  lval = lvals[i];
+		cgValue init = inits[i];
+		GB_ASSERT(init.kind != cgValue_Multi);
+		if (init.type == nullptr) {
+			continue;
+		}
+		cg_addr_store(p, lval, init);
+	}
+
+	p->in_multi_assignment = prev_in_assignment;
+}
+
+gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) {
+	if (as->op.kind == Token_Eq) {
+		auto lvals = array_make<cgAddr>(permanent_allocator(), 0, as->lhs.count);
+
+		for (Ast *lhs : as->lhs) {
+			cgAddr lval = {};
+			if (!is_blank_ident(lhs)) {
+				lval = cg_build_addr(p, lhs);
+			}
+			array_add(&lvals, lval);
+		}
+		cg_build_assignment(p, lvals, as->rhs);
+		return;
+	}
+
+	GB_ASSERT(as->lhs.count == 1);
+	GB_ASSERT(as->rhs.count == 1);
+	// NOTE(bill): Only 1 += 1 is allowed, no tuples
+	// +=, -=, etc
+
+	i32 op_ = cast(i32)as->op.kind;
+	op_ += Token_Add - Token_AddEq; // Convert += to +
+	TokenKind op = cast(TokenKind)op_;
+
+	if (op == Token_CmpAnd || op == Token_CmpOr) {
+		GB_PANIC("TODO(bill): cg_emit_logical_binary_expr");
+		// Type *type = as->lhs[0]->tav.type;
+		// cgValue new_value = cg_emit_logical_binary_expr(p, op, as->lhs[0], as->rhs[0], type);
+
+		// cgAddr lhs = cg_build_addr(p, as->lhs[0]);
+		// cg_addr_store(p, lhs, new_value);
+	} else {
+		cgAddr lhs = cg_build_addr(p, as->lhs[0]);
+		cgValue value = cg_build_expr(p, as->rhs[0]);
+		Type *lhs_type = cg_addr_type(lhs);
+
+		// NOTE(bill): Allow for the weird edge case of:
+		// array *= matrix
+		if (op == Token_Mul && is_type_matrix(value.type) && is_type_array(lhs_type)) {
+			GB_PANIC("TODO(bill): array *= matrix");
+			// cgValue old_value = cg_addr_load(p, lhs);
+			// Type *type = old_value.type;
+			// cgValue new_value = cg_emit_vector_mul_matrix(p, old_value, value, type);
+			// cg_addr_store(p, lhs, new_value);
+			// return;
+		}
+
+		if (is_type_array(lhs_type)) {
+			GB_PANIC("TODO(bill): cg_build_assign_stmt_array");
+			// cg_build_assign_stmt_array(p, op, lhs, value);
+			// return;
+		} else {
+			cgValue old_value = cg_addr_load(p, lhs);
+			Type *type = old_value.type;
+
+			cgValue change = cg_emit_conv(p, value, type);
+			cgValue new_value = cg_emit_arith(p, op, old_value, change, type);
+			cg_addr_store(p, lhs, new_value);
+		}
+	}
+}
+
+gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result) {
+	Slice<cgValue> results = {};
+	results.data = &result;
+	results.count = 1;
+	cg_build_return_stmt_internal(p, results);
+}
+
+
+gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results) {
+	TypeTuple *tuple  = &p->type->Proc.results->Tuple;
+	isize return_count = p->type->Proc.result_count;
+
+	if (return_count == 0) {
+		tb_inst_ret(p->func, 0, nullptr);
+		return;
+	}
+
+	if (p->split_returns_index >= 0) {
+		GB_ASSERT(is_calling_convention_odin(p->type->Proc.calling_convention));
+
+		for (isize i = 0; i < return_count-1; i++) {
+			Entity *e = tuple->variables[i];
+			TB_Node *ret_ptr = tb_inst_param(p->func, cast(int)(p->split_returns_index+i));
+			cgValue ptr = cg_value(ret_ptr, alloc_type_pointer(e->type));
+			cg_emit_store(p, ptr, results[i]);
+		}
+
+		if (p->return_by_ptr) {
+			Entity *e = tuple->variables[return_count-1];
+			TB_Node *ret_ptr = tb_inst_param(p->func, 0);
+			cgValue ptr = cg_value(ret_ptr, alloc_type_pointer(e->type));
+			cg_emit_store(p, ptr, results[return_count-1]);
+
+			tb_inst_ret(p->func, 0, nullptr);
+			return;
+		} else {
+			GB_ASSERT(p->proto->return_count == 1);
+			TB_DataType dt = TB_PROTOTYPE_RETURNS(p->proto)->dt;
+
+			cgValue result = results[return_count-1];
+			result = cg_flatten_value(p, result);
+			TB_Node *final_res = nullptr;
+			if (result.kind == cgValue_Addr) {
+				TB_CharUnits align = cast(TB_CharUnits)type_align_of(result.type);
+				final_res = tb_inst_load(p->func, dt, result.node, align, false);
+			} else {
+				GB_ASSERT(result.kind == cgValue_Value);
+				TB_DataType st = result.node->dt;
+				GB_ASSERT(st.type == dt.type);
+				if (st.raw == dt.raw) {
+					final_res = result.node;
+				} else if (st.type == TB_INT && st.data == 1) {
+					final_res = tb_inst_zxt(p->func, result.node, dt);
+				} else {
+					final_res = tb_inst_bitcast(p->func, result.node, dt);
+				}
+			}
+			GB_ASSERT(final_res != nullptr);
+
+			tb_inst_ret(p->func, 1, &final_res);
+			return;
+		}
+
+	} else {
+		GB_ASSERT(!is_calling_convention_odin(p->type->Proc.calling_convention));
+
+		if (p->return_by_ptr) {
+			Entity *e = tuple->variables[return_count-1];
+			TB_Node *ret_ptr = tb_inst_param(p->func, 0);
+			cgValue ptr = cg_value(ret_ptr, alloc_type_pointer(e->type));
+			cg_emit_store(p, ptr, results[return_count-1]);
+
+			tb_inst_ret(p->func, 0, nullptr);
+			return;
+		} else {
+			GB_ASSERT(p->proto->return_count == 1);
+			TB_DataType dt = TB_PROTOTYPE_RETURNS(p->proto)->dt;
+			if (results.count == 1) {
+				cgValue result = results[0];
+				result = cg_flatten_value(p, result);
+
+				TB_Node *final_res = nullptr;
+				if (result.kind == cgValue_Addr) {
+					TB_CharUnits align = cast(TB_CharUnits)type_align_of(result.type);
+					final_res = tb_inst_load(p->func, dt, result.node, align, false);
+				} else {
+					GB_ASSERT(result.kind == cgValue_Value);
+					TB_DataType st = result.node->dt;
+					GB_ASSERT(st.type == dt.type);
+					if (st.raw == dt.raw) {
+						final_res = result.node;
+					} else if (st.type == TB_INT && st.data == 1) {
+						final_res = tb_inst_zxt(p->func, result.node, dt);
+					} else {
+						final_res = tb_inst_bitcast(p->func, result.node, dt);
+					}
+				}
+
+				GB_ASSERT(final_res != nullptr);
+
+				tb_inst_ret(p->func, 1, &final_res);
+				return;
+			} else {
+				GB_ASSERT_MSG(results.count == 1, "TODO(bill): multi-return values for the return");
+				return;
+			}
+		}
+
+	}
+}
+
+
+gb_internal void cg_build_return_stmt(cgProcedure *p, Slice<Ast *> const &return_results) {
+	TypeTuple *tuple  = &p->type->Proc.results->Tuple;
+	isize return_count = p->type->Proc.result_count;
+
+	if (return_count == 0) {
+		tb_inst_ret(p->func, 0, nullptr);
+		return;
+	}
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	auto results = array_make<cgValue>(temporary_allocator(), 0, return_count);
+
+	if (return_results.count != 0) {
+		for (isize i = 0; i < return_results.count; i++) {
+			cgValue res = cg_build_expr(p, return_results[i]);
+			cg_append_tuple_values(p, &results, res);
+		}
+	} else {
+		for_array(i, tuple->variables) {
+			Entity *e = tuple->variables[i];
+			cgAddr addr = map_must_get(&p->variable_map, e);
+			cgValue res = cg_addr_load(p, addr);
+			array_add(&results, res);
+		}
+	}
+	GB_ASSERT(results.count == return_count);
+
+	if (return_results.count != 0 && p->type->Proc.has_named_results) {
+		// NOTE(bill): store the named values before returning
+		for_array(i, tuple->variables) {
+			Entity *e = tuple->variables[i];
+			cgAddr addr = map_must_get(&p->variable_map, e);
+			cg_addr_store(p, addr, results[i]);
+		}
+	}
+	for_array(i, tuple->variables) {
+		Entity *e = tuple->variables[i];
+		results[i] = cg_emit_conv(p, results[i], e->type);
+	}
+
+	cg_build_return_stmt_internal(p, slice_from_array(results));
+}
+
+gb_internal void cg_build_if_stmt(cgProcedure *p, Ast *node) {
+	ast_node(is, IfStmt, node);
+	cg_scope_open(p, is->scope); // Scope #1
+	defer (cg_scope_close(p, cgDeferExit_Default, nullptr));
+
+	if (is->init != nullptr) {
+		TB_Node *init = cg_control_region(p, "if_init");
+		cg_emit_goto(p, init);
+		tb_inst_set_control(p->func, init);
+		cg_build_stmt(p, is->init);
+	}
+
+	TB_Node *then  = cg_control_region(p, "if_then");
+	TB_Node *done  = cg_control_region(p, "if_done");
+	TB_Node *else_ = done;
+	if (is->else_stmt != nullptr) {
+		else_ = cg_control_region(p, "if_else");
+	}
+
+	cgValue cond = cg_build_cond(p, is->cond, then, else_);
+	gb_unused(cond);
+
+	if (is->label != nullptr) {
+		cgTargetList *tl = cg_push_target_list(p, is->label, done, nullptr, nullptr);
+		tl->is_block = true;
+	}
+
+	// TODO(bill): should we do a constant check?
+	// Which philosophy are we following?
+	// - IR represents what the code represents (probably this)
+	// - IR represents what the code executes
+
+	tb_inst_set_control(p->func, then);
+
+	cg_build_stmt(p, is->body);
+
+	cg_emit_goto(p, done);
+
+	if (is->else_stmt != nullptr) {
+		tb_inst_set_control(p->func, else_);
+
+		cg_scope_open(p, scope_of_node(is->else_stmt));
+		cg_build_stmt(p, is->else_stmt);
+		cg_scope_close(p, cgDeferExit_Default, nullptr);
+
+		cg_emit_goto(p, done);
+	}
+
+	tb_inst_set_control(p->func, done);
+}
+
+gb_internal void cg_build_for_stmt(cgProcedure *p, Ast *node) {
+	ast_node(fs, ForStmt, node);
+
+	cg_scope_open(p, fs->scope);
+	defer (cg_scope_close(p, cgDeferExit_Default, nullptr));
+
+	if (fs->init != nullptr) {
+		TB_Node *init = cg_control_region(p, "for_init");
+		cg_emit_goto(p, init);
+		tb_inst_set_control(p->func, init);
+		cg_build_stmt(p, fs->init);
+	}
+	TB_Node *body = cg_control_region(p, "for_body");
+	TB_Node *done = cg_control_region(p, "for_done");
+	TB_Node *loop = body;
+	if (fs->cond != nullptr) {
+		loop = cg_control_region(p, "for_loop");
+	}
+	TB_Node *post = loop;
+	if (fs->post != nullptr) {
+		post = cg_control_region(p, "for_post");
+	}
+
+	cg_emit_goto(p, loop);
+	tb_inst_set_control(p->func, loop);
+
+	if (loop != body) {
+		cg_build_cond(p, fs->cond, body, done);
+		tb_inst_set_control(p->func, body);
+	}
+
+	cg_push_target_list(p, fs->label, done, post, nullptr);
+	cg_build_stmt(p, fs->body);
+	cg_pop_target_list(p);
+
+	cg_emit_goto(p, post);
+
+	if (fs->post != nullptr) {
+		tb_inst_set_control(p->func, post);
+		cg_build_stmt(p, fs->post);
+		cg_emit_goto(p, loop);
+	}
+	tb_inst_set_control(p->func, done);
+}
+
+
+gb_internal Ast *cg_strip_and_prefix(Ast *ident) {
+	if (ident != nullptr) {
+		if (ident->kind == Ast_UnaryExpr && ident->UnaryExpr.op.kind == Token_And) {
+			ident = ident->UnaryExpr.expr;
+		}
+		GB_ASSERT(ident->kind == Ast_Ident);
+	}
+	return ident;
+}
+
+gb_internal void cg_emit_increment(cgProcedure *p, cgValue addr) {
+	GB_ASSERT(is_type_pointer(addr.type));
+	Type *type = type_deref(addr.type);
+	cgValue v_one = cg_const_value(p, type, exact_value_i64(1));
+	cg_emit_store(p, addr, cg_emit_arith(p, Token_Add, cg_emit_load(p, addr), v_one, type));
+
+}
+
+gb_internal void cg_range_stmt_store_val(cgProcedure *p, Ast *stmt_val, cgValue const &value) {
+	Entity *e = entity_of_node(stmt_val);
+	if (e == nullptr) {
+		return;
+	}
+
+	if (e->flags & EntityFlag_Value) {
+		if (value.kind == cgValue_Addr) {
+			cgValue ptr = cg_address_from_load_or_generate_local(p, value);
+			cg_add_entity(p->module, e, ptr);
+			return;
+		}
+	}
+
+	cgAddr addr = cg_add_local(p, e->type, e, false);
+	cg_addr_store(p, addr, value);
+	return;
+}
+
+gb_internal void cg_build_range_stmt_interval(cgProcedure *p, AstBinaryExpr *node,
+                                              AstRangeStmt *rs, Scope *scope) {
+	bool ADD_EXTRA_WRAPPING_CHECK = true;
+
+	cg_scope_open(p, scope);
+
+	Ast *val0 = rs->vals.count > 0 ? cg_strip_and_prefix(rs->vals[0]) : nullptr;
+	Ast *val1 = rs->vals.count > 1 ? cg_strip_and_prefix(rs->vals[1]) : nullptr;
+	Type *val0_type = nullptr;
+	Type *val1_type = nullptr;
+	if (val0 != nullptr && !is_blank_ident(val0)) {
+		val0_type = type_of_expr(val0);
+	}
+	if (val1 != nullptr && !is_blank_ident(val1)) {
+		val1_type = type_of_expr(val1);
+	}
+
+	TokenKind op = Token_Lt;
+	switch (node->op.kind) {
+	case Token_Ellipsis:  op = Token_LtEq; break;
+	case Token_RangeFull: op = Token_LtEq; break;
+	case Token_RangeHalf: op = Token_Lt;  break;
+	default: GB_PANIC("Invalid interval operator"); break;
+	}
+
+
+	cgValue lower = cg_build_expr(p, node->left);
+	cgValue upper = {}; // initialized each time in the loop
+
+	cgAddr value;
+	if (val0_type != nullptr) {
+		value = cg_add_local(p, val0_type, entity_of_node(val0), false);
+	} else {
+		value = cg_add_local(p, lower.type, nullptr, false);
+	}
+	cg_addr_store(p, value, lower);
+
+	cgAddr index;
+	if (val1_type != nullptr) {
+		index = cg_add_local(p, val1_type, entity_of_node(val1), false);
+	} else {
+		index = cg_add_local(p, t_int, nullptr, false);
+	}
+	cg_addr_store(p, index, cg_const_int(p, t_int, 0));
+
+	TB_Node *loop = cg_control_region(p, "for_interval_loop");
+	TB_Node *body = cg_control_region(p, "for_interval_body");
+	TB_Node *done = cg_control_region(p, "for_interval_done");
+
+	cg_emit_goto(p, loop);
+	tb_inst_set_control(p->func, loop);
+
+	upper = cg_build_expr(p, node->right);
+	cgValue curr_value = cg_addr_load(p, value);
+	cgValue cond = cg_emit_comp(p, op, curr_value, upper);
+	cg_emit_if(p, cond, body, done);
+	tb_inst_set_control(p->func, body);
+
+	cgValue val = cg_addr_load(p, value);
+	cgValue idx = cg_addr_load(p, index);
+
+	if (val0_type) cg_range_stmt_store_val(p, val0, val);
+	if (val1_type) cg_range_stmt_store_val(p, val1, idx);
+
+
+	{
+		// NOTE: this check block will most likely be optimized out, and is here
+		// to make this code easier to read
+		TB_Node *check = nullptr;
+		TB_Node *post = cg_control_region(p, "for_interval_post");
+
+		TB_Node *continue_block = post;
+
+		if (ADD_EXTRA_WRAPPING_CHECK &&
+		    op == Token_LtEq) {
+			check = cg_control_region(p, "for_interval_check");
+			continue_block = check;
+		}
+
+		cg_push_target_list(p, rs->label, done, continue_block, nullptr);
+
+		cg_build_stmt(p, rs->body);
+
+		cg_scope_close(p, cgDeferExit_Default, nullptr);
+		cg_pop_target_list(p);
+
+		if (check != nullptr) {
+			cg_emit_goto(p, check);
+			tb_inst_set_control(p->func, check);
+
+			cgValue check_cond = cg_emit_comp(p, Token_NotEq, curr_value, upper);
+			cg_emit_if(p, check_cond, post, done);
+		} else {
+			cg_emit_goto(p, post);
+		}
+
+		tb_inst_set_control(p->func, post);
+		cg_emit_increment(p, value.addr);
+		cg_emit_increment(p, index.addr);
+		cg_emit_goto(p, loop);
+	}
+
+	tb_inst_set_control(p->func, done);
+}
+
+gb_internal void cg_build_range_stmt_indexed(cgProcedure *p, cgValue expr, Type *val_type, cgValue count_ptr,
+                                             cgValue *val_, cgValue *idx_, TB_Node **loop_, TB_Node **done_,
+                                             bool is_reverse) {
+	cgValue count = {};
+	Type *expr_type = base_type(type_deref(expr.type));
+	switch (expr_type->kind) {
+	case Type_Array:
+		count = cg_const_int(p, t_int, expr_type->Array.count);
+		break;
+	}
+
+	cgValue val = {};
+	cgValue idx = {};
+	TB_Node *loop = nullptr;
+	TB_Node *done = nullptr;
+	TB_Node *body = nullptr;
+
+	loop = cg_control_region(p, "for_index_loop");
+	body = cg_control_region(p, "for_index_body");
+	done = cg_control_region(p, "for_index_done");
+
+	cgAddr index = cg_add_local(p, t_int, nullptr, false);
+
+	if (!is_reverse) {
+		/*
+			for x, i in array {
+				...
+			}
+
+			i := -1
+			for {
+				i += 1
+				if !(i < len(array)) {
+					break
+				}
+				#no_bounds_check x := array[i]
+				...
+			}
+		*/
+
+		cg_addr_store(p, index, cg_const_int(p, t_int, cast(u64)-1));
+
+		cg_emit_goto(p, loop);
+		tb_inst_set_control(p->func, loop);
+
+		cgValue incr = cg_emit_arith(p, Token_Add, cg_addr_load(p, index), cg_const_int(p, t_int, 1), t_int);
+		cg_addr_store(p, index, incr);
+
+		if (count.node == nullptr) {
+			GB_ASSERT(count_ptr.node != nullptr);
+			count = cg_emit_load(p, count_ptr);
+		}
+		cgValue cond = cg_emit_comp(p, Token_Lt, incr, count);
+		cg_emit_if(p, cond, body, done);
+	} else {
+		// NOTE(bill): REVERSED LOGIC
+		/*
+			#reverse for x, i in array {
+				...
+			}
+
+			i := len(array)
+			for {
+				i -= 1
+				if i < 0 {
+					break
+				}
+				#no_bounds_check x := array[i]
+				...
+			}
+		*/
+
+		if (count.node == nullptr) {
+			GB_ASSERT(count_ptr.node != nullptr);
+			count = cg_emit_load(p, count_ptr);
+		}
+		count = cg_emit_conv(p, count, t_int);
+		cg_addr_store(p, index, count);
+
+		cg_emit_goto(p, loop);
+		tb_inst_set_control(p->func, loop);
+
+		cgValue incr = cg_emit_arith(p, Token_Sub, cg_addr_load(p, index), cg_const_int(p, t_int, 1), t_int);
+		cg_addr_store(p, index, incr);
+
+		cgValue anti_cond = cg_emit_comp(p, Token_Lt, incr, cg_const_int(p, t_int, 0));
+		cg_emit_if(p, anti_cond, done, body);
+	}
+
+	tb_inst_set_control(p->func, body);
+
+	idx = cg_addr_load(p, index);
+	switch (expr_type->kind) {
+	case Type_Array: {
+		if (val_type != nullptr) {
+			val = cg_emit_load(p, cg_emit_array_ep(p, expr, idx));
+		}
+		break;
+	}
+	case Type_EnumeratedArray: {
+		if (val_type != nullptr) {
+			val = cg_emit_load(p, cg_emit_array_ep(p, expr, idx));
+			// NOTE(bill): Override the idx value for the enumeration
+			Type *index_type = expr_type->EnumeratedArray.index;
+			if (compare_exact_values(Token_NotEq, *expr_type->EnumeratedArray.min_value, exact_value_u64(0))) {
+				idx = cg_emit_arith(p, Token_Add, idx, cg_const_value(p, index_type, *expr_type->EnumeratedArray.min_value), index_type);
+			}
+		}
+		break;
+	}
+	case Type_Slice: {
+		if (val_type != nullptr) {
+			cgValue elem = cg_builtin_raw_data(p, expr);
+			val = cg_emit_load(p, cg_emit_ptr_offset(p, elem, idx));
+		}
+		break;
+	}
+	case Type_DynamicArray: {
+		if (val_type != nullptr) {
+			cgValue elem = cg_emit_struct_ep(p, expr, 0);
+			elem = cg_emit_load(p, elem);
+			val = cg_emit_load(p, cg_emit_ptr_offset(p, elem, idx));
+		}
+		break;
+	}
+	case Type_Struct: {
+		GB_ASSERT(is_type_soa_struct(expr_type));
+		break;
+	}
+
+	default:
+		GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type));
+		break;
+	}
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+
+}
+
+gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node) {
+	ast_node(rs, RangeStmt, node);
+
+	Ast *expr = unparen_expr(rs->expr);
+
+	if (is_ast_range(expr)) {
+		cg_build_range_stmt_interval(p, &expr->BinaryExpr, rs, rs->scope);
+		return;
+	}
+
+	Type *expr_type = type_of_expr(expr);
+	if (expr_type != nullptr) {
+		Type *et = base_type(type_deref(expr_type));
+	 	if (is_type_soa_struct(et)) {
+	 		GB_PANIC("TODO(bill): #soa array range statements");
+			// cg_build_range_stmt_struct_soa(p, rs, scope);
+			return;
+		}
+	}
+
+	cg_scope_open(p, rs->scope);
+
+
+	Ast *val0 = rs->vals.count > 0 ? cg_strip_and_prefix(rs->vals[0]) : nullptr;
+	Ast *val1 = rs->vals.count > 1 ? cg_strip_and_prefix(rs->vals[1]) : nullptr;
+	Type *val0_type = nullptr;
+	Type *val1_type = nullptr;
+	if (val0 != nullptr && !is_blank_ident(val0)) {
+		val0_type = type_of_expr(val0);
+	}
+	if (val1 != nullptr && !is_blank_ident(val1)) {
+		val1_type = type_of_expr(val1);
+	}
+
+	cgValue val = {};
+	cgValue key = {};
+	TB_Node *loop = nullptr;
+	TB_Node *done = nullptr;
+	bool is_map = false;
+	TypeAndValue tav = type_and_value_of_expr(expr);
+
+	if (tav.mode == Addressing_Type) {
+		GB_PANIC("TODO(bill): range statement over enum type");
+	} else {
+		Type *expr_type = type_of_expr(expr);
+		Type *et = base_type(type_deref(expr_type));
+		switch (et->kind) {
+		case Type_Map: {
+			is_map = true;
+			cgValue map = cg_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(map.type))) {
+				map = cg_emit_load(p, map);
+			}
+			GB_PANIC("TODO(bill): cg_build_range_map");
+			// cg_build_range_map(p, map, val1_type, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_Array: {
+			cgValue array = cg_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(array.type))) {
+				array = cg_emit_load(p, array);
+			}
+			cgAddr count_ptr = cg_add_local(p, t_int, nullptr, false);
+			cg_addr_store(p, count_ptr, cg_const_int(p, t_int, et->Array.count));
+			cg_build_range_stmt_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse);
+			break;
+		}
+		case Type_EnumeratedArray: {
+			cgValue array = cg_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(array.type))) {
+				array = cg_emit_load(p, array);
+			}
+			cgAddr count_ptr = cg_add_local(p, t_int, nullptr, false);
+			cg_addr_store(p, count_ptr, cg_const_int(p, t_int, et->EnumeratedArray.count));
+			cg_build_range_stmt_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse);
+			break;
+		}
+		case Type_DynamicArray: {
+			cgValue count_ptr = {};
+			cgValue array = cg_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(array.type))) {
+				array = cg_emit_load(p, array);
+			}
+			count_ptr = cg_emit_struct_ep(p, array, 1);
+			cg_build_range_stmt_indexed(p, array, val0_type, count_ptr, &val, &key, &loop, &done, rs->reverse);
+			break;
+		}
+		case Type_Slice: {
+			cgValue count_ptr = {};
+			cgValue slice = cg_build_expr(p, expr);
+			if (is_type_pointer(slice.type)) {
+				count_ptr = cg_emit_struct_ep(p, slice, 1);
+				slice = cg_emit_load(p, slice);
+			} else {
+				count_ptr = cg_add_local(p, t_int, nullptr, false).addr;
+				cg_emit_store(p, count_ptr, cg_builtin_len(p, slice));
+			}
+			cg_build_range_stmt_indexed(p, slice, val0_type, count_ptr, &val, &key, &loop, &done, rs->reverse);
+			break;
+		}
+		case Type_Basic: {
+			cgValue string = cg_build_expr(p, expr);
+			if (is_type_pointer(string.type)) {
+				string = cg_emit_load(p, string);
+			}
+			if (is_type_untyped(expr_type)) {
+				cgAddr s = cg_add_local(p, default_type(string.type), nullptr, false);
+				cg_addr_store(p, s, string);
+				string = cg_addr_load(p, s);
+			}
+			Type *t = base_type(string.type);
+			GB_ASSERT(!is_type_cstring(t));
+			GB_PANIC("TODO(bill): cg_build_range_string");
+			// cg_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+			break;
+		}
+		case Type_Tuple:
+			GB_PANIC("TODO(bill): cg_build_range_tuple");
+			// cg_build_range_tuple(p, expr, val0_type, val1_type, &val, &key, &loop, &done);
+			break;
+		default:
+			GB_PANIC("Cannot range over %s", type_to_string(expr_type));
+			break;
+		}
+	}
+
+	if (is_map) {
+		if (val0_type) cg_range_stmt_store_val(p, val0, key);
+		if (val1_type) cg_range_stmt_store_val(p, val1, val);
+	} else {
+		if (val0_type) cg_range_stmt_store_val(p, val0, val);
+		if (val1_type) cg_range_stmt_store_val(p, val1, key);
+	}
+
+	cg_push_target_list(p, rs->label, done, loop, nullptr);
+
+	cg_build_stmt(p, rs->body);
+
+	cg_scope_close(p, cgDeferExit_Default, nullptr);
+	cg_pop_target_list(p);
+	cg_emit_goto(p, loop);
+	tb_inst_set_control(p->func, done);
+}
+
+gb_internal bool cg_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss) {
+	if (ss->tag == nullptr) {
+		return false;
+	}
+	bool is_typeid = false;
+	TypeAndValue tv = type_and_value_of_expr(ss->tag);
+	if (is_type_integer(core_type(tv.type))) {
+		if (type_size_of(tv.type) > 8) {
+			return false;
+		}
+		// okay
+	} else if (is_type_typeid(tv.type)) {
+		// okay
+		is_typeid = true;
+	} else {
+		return false;
+	}
+
+	ast_node(body, BlockStmt, ss->body);
+	for (Ast *clause : body->stmts) {
+		ast_node(cc, CaseClause, clause);
+
+		if (cc->list.count == 0) {
+			continue;
+		}
+
+		for (Ast *expr : cc->list) {
+			expr = unparen_expr(expr);
+			if (is_ast_range(expr)) {
+				return false;
+			}
+			if (expr->tav.mode == Addressing_Type) {
+				GB_ASSERT(is_typeid);
+				continue;
+			}
+			tv = type_and_value_of_expr(expr);
+			if (tv.mode != Addressing_Constant) {
+				return false;
+			}
+			if (!is_type_integer(core_type(tv.type))) {
+				return false;
+			}
+		}
+
+	}
+
+	return true;
+}
+
+
+gb_internal void cg_build_switch_stmt(cgProcedure *p, Ast *node) {
+	ast_node(ss, SwitchStmt, node);
+	cg_scope_open(p, ss->scope);
+
+	if (ss->init != nullptr) {
+		cg_build_stmt(p, ss->init);
+	}
+	cgValue tag = {};
+	if (ss->tag != nullptr) {
+		tag = cg_build_expr(p, ss->tag);
+	} else {
+		tag = cg_const_bool(p, t_bool, true);
+	}
+
+	TB_Node *done = cg_control_region(p, "switch_done");
+
+	ast_node(body, BlockStmt, ss->body);
+
+	isize case_count = body->stmts.count;
+	Slice<Ast *> default_stmts = {};
+	TB_Node *default_fall  = nullptr;
+	TB_Node *default_block = nullptr;
+	Scope *  default_scope = nullptr;
+	TB_Node *fall = nullptr;
+
+
+	auto body_regions = slice_make<TB_Node *>(permanent_allocator(), body->stmts.count);
+	auto body_scopes = slice_make<Scope *>(permanent_allocator(), body->stmts.count);
+	for_array(i, body->stmts) {
+		Ast *clause = body->stmts[i];
+		ast_node(cc, CaseClause, clause);
+
+		body_regions[i] = cg_control_region(p, cc->list.count == 0 ? "switch_default_body" : "switch_case_body");
+		body_scopes[i] = cc->scope;
+		if (cc->list.count == 0) {
+			default_block = body_regions[i];
+			default_scope = cc->scope;
+		}
+	}
+
+	bool is_trivial = cg_switch_stmt_can_be_trivial_jump_table(ss);
+	if (is_trivial) {
+		isize key_count = 0;
+		for (Ast *clause : body->stmts) {
+			ast_node(cc, CaseClause, clause);
+			key_count += cc->list.count;
+		}
+		TB_SwitchEntry *keys = gb_alloc_array(temporary_allocator(), TB_SwitchEntry, key_count);
+		isize key_index = 0;
+		for_array(i, body->stmts) {
+			Ast *clause = body->stmts[i];
+			ast_node(cc, CaseClause, clause);
+
+			TB_Node *region = body_regions[i];
+			for (Ast *expr : cc->list) {
+				i64 key = 0;
+				expr = unparen_expr(expr);
+				GB_ASSERT(!is_ast_range(expr));
+				if (expr->tav.mode == Addressing_Type) {
+					Type *type = expr->tav.value.value_typeid;
+					if (type == nullptr || type == t_invalid) {
+						type = expr->tav.type;
+					}
+					key = cg_typeid_as_u64(p->module, type);
+				} else {
+					auto tv = type_and_value_of_expr(expr);
+					GB_ASSERT(tv.mode == Addressing_Constant);
+					key = exact_value_to_i64(tv.value);
+				}
+				keys[key_index++] = {key, region};
+			}
+		}
+		GB_ASSERT(key_index == key_count);
+
+		TB_Node *end_block = done;
+		if (default_block) {
+			end_block = default_block;
+		}
+
+		TB_DataType dt = cg_data_type(tag.type);
+		GB_ASSERT(tag.kind == cgValue_Value);
+		GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+
+		tb_inst_branch(p->func, dt, tag.node, end_block, key_count, keys);
+	}
+
+	for_array(i, body->stmts) {
+		Ast *clause = body->stmts[i];
+		ast_node(cc, CaseClause, clause);
+
+		TB_Node *body_region = body_regions[i];
+		Scope *body_scope = body_scopes[i];
+		fall = done;
+		if (i+1 < case_count) {
+			fall = body_regions[i+1];
+		}
+
+		if (cc->list.count == 0) {
+			// default case
+			default_stmts = cc->stmts;
+			default_fall  = fall;
+			GB_ASSERT(default_block == body_region);
+			continue;
+		}
+
+		TB_Node *next_cond = nullptr;
+		if (!is_trivial) for (Ast *expr : cc->list) {
+			expr = unparen_expr(expr);
+
+			next_cond = cg_control_region(p, "switch_case_next");
+
+			cgValue cond = {};
+			if (is_ast_range(expr)) {
+				ast_node(ie, BinaryExpr, expr);
+				TokenKind op = Token_Invalid;
+				switch (ie->op.kind) {
+				case Token_Ellipsis:  op = Token_LtEq; break;
+				case Token_RangeFull: op = Token_LtEq; break;
+				case Token_RangeHalf: op = Token_Lt;   break;
+				default: GB_PANIC("Invalid interval operator"); break;
+				}
+				cgValue lhs = cg_build_expr(p, ie->left);
+				cgValue rhs = cg_build_expr(p, ie->right);
+
+				cgValue cond_lhs = cg_emit_comp(p, Token_LtEq, lhs, tag);
+				cgValue cond_rhs = cg_emit_comp(p, op, tag, rhs);
+				cond = cg_emit_arith(p, Token_And, cond_lhs, cond_rhs, t_bool);
+			} else {
+				if (expr->tav.mode == Addressing_Type) {
+					GB_ASSERT(is_type_typeid(tag.type));
+					cgValue e = cg_typeid(p, expr->tav.type);
+					e = cg_emit_conv(p, e, tag.type);
+					cond = cg_emit_comp(p, Token_CmpEq, tag, e);
+				} else {
+					cond = cg_emit_comp(p, Token_CmpEq, tag, cg_build_expr(p, expr));
+				}
+			}
+
+			GB_ASSERT(cond.kind == cgValue_Value);
+			tb_inst_if(p->func, cond.node, body_region, next_cond);
+			tb_inst_set_control(p->func, next_cond);
+		}
+
+		tb_inst_set_control(p->func, body_region);
+
+		cg_push_target_list(p, ss->label, done, nullptr, fall);
+		cg_scope_open(p, body_scope);
+		cg_build_stmt_list(p, cc->stmts);
+		cg_scope_close(p, cgDeferExit_Default, body_region);
+		cg_pop_target_list(p);
+
+		cg_emit_goto(p, done);
+		tb_inst_set_control(p->func, next_cond);
+	}
+
+	if (default_block != nullptr) {
+		if (!is_trivial) {
+			cg_emit_goto(p, default_block);
+		}
+		tb_inst_set_control(p->func, default_block);
+
+		cg_push_target_list(p, ss->label, done, nullptr, default_fall);
+		cg_scope_open(p, default_scope);
+		cg_build_stmt_list(p, default_stmts);
+		cg_scope_close(p, cgDeferExit_Default, default_block);
+		cg_pop_target_list(p);
+	}
+
+
+	cg_emit_goto(p, done);
+	tb_inst_set_control(p->func, done);
+
+	cg_scope_close(p, cgDeferExit_Default, done);
+}
+
+gb_internal void cg_build_type_switch_stmt(cgProcedure *p, Ast *node) {
+	ast_node(ss, TypeSwitchStmt, node);
+
+	TB_Node *done_region = cg_control_region(p, "typeswitch_done");
+	TB_Node *else_region = done_region;
+	TB_Node *default_region = nullptr;
+	isize num_cases = 0;
+
+	cg_scope_open(p, ss->scope);
+	defer (cg_scope_close(p, cgDeferExit_Default, done_region));
+
+	ast_node(as, AssignStmt, ss->tag);
+	GB_ASSERT(as->lhs.count == 1);
+	GB_ASSERT(as->rhs.count == 1);
+
+	cgValue parent = cg_build_expr(p, as->rhs[0]);
+	bool is_parent_ptr = is_type_pointer(parent.type);
+	Type *parent_base_type = type_deref(parent.type);
+	gb_unused(parent_base_type);
+
+	TypeSwitchKind switch_kind = check_valid_type_switch_type(parent.type);
+	GB_ASSERT(switch_kind != TypeSwitch_Invalid);
+
+
+	cgValue parent_value = parent;
+
+	cgValue parent_ptr = parent;
+	if (!is_parent_ptr) {
+		parent_ptr = cg_address_from_load_or_generate_local(p, parent);
+	}
+
+	cgValue tag = {};
+	cgValue union_data = {};
+	if (switch_kind == TypeSwitch_Union) {
+		union_data = cg_emit_conv(p, parent_ptr, t_rawptr);
+		Type *union_type = type_deref(parent_ptr.type);
+		if (is_type_union_maybe_pointer(union_type)) {
+			tag = cg_emit_conv(p, cg_emit_comp_against_nil(p, Token_NotEq, union_data), t_int);
+		} else if (union_tag_size(union_type) == 0) {
+			tag = {}; // there is no tag for a zero sized union
+		} else {
+			cgValue tag_ptr = cg_emit_union_tag_ptr(p, parent_ptr);
+			tag = cg_emit_load(p, tag_ptr);
+		}
+	} else if (switch_kind == TypeSwitch_Any) {
+		tag = cg_emit_load(p, cg_emit_struct_ep(p, parent_ptr, 1));
+	} else {
+		GB_PANIC("Unknown switch kind");
+	}
+
+	ast_node(body, BlockStmt, ss->body);
+
+	for (Ast *clause : body->stmts) {
+		ast_node(cc, CaseClause, clause);
+		num_cases += cc->list.count;
+		if (cc->list.count == 0) {
+			GB_ASSERT(default_region == nullptr);
+			default_region = cg_control_region(p, "typeswitch_default_body");
+			else_region = default_region;
+		}
+	}
+
+	bool all_by_reference = false;
+	for (Ast *clause : body->stmts) {
+		ast_node(cc, CaseClause, clause);
+		if (cc->list.count != 1) {
+			continue;
+		}
+		Entity *case_entity = implicit_entity_of_node(clause);
+		all_by_reference |= (case_entity->flags & EntityFlag_Value) == 0;
+		break;
+	}
+
+	TB_Node *backing_ptr = nullptr;
+	if (!all_by_reference) {
+		bool variants_found = false;
+		i64 max_size = 0;
+		i64 max_align = 1;
+		for (Ast *clause : body->stmts) {
+			ast_node(cc, CaseClause, clause);
+			if (cc->list.count != 1) {
+				continue;
+			}
+			Entity *case_entity = implicit_entity_of_node(clause);
+			if (!is_type_untyped_nil(case_entity->type)) {
+				max_size = gb_max(max_size, type_size_of(case_entity->type));
+				max_align = gb_max(max_align, type_align_of(case_entity->type));
+				variants_found = true;
+			}
+		}
+		if (variants_found) {
+			backing_ptr = tb_inst_local(p->func, cast(TB_CharUnits)max_size, cast(TB_CharUnits)max_align);
+		}
+	}
+
+	TEMPORARY_ALLOCATOR_GUARD();
+	TB_Node **control_regions = gb_alloc_array(temporary_allocator(), TB_Node *, body->stmts.count);
+	TB_SwitchEntry *switch_entries = gb_alloc_array(temporary_allocator(), TB_SwitchEntry, num_cases);
+
+	isize case_index = 0;
+	for_array(i, body->stmts) {
+		Ast *clause = body->stmts[i];
+		ast_node(cc, CaseClause, clause);
+		if (cc->list.count == 0) {
+			control_regions[i] = default_region;
+			continue;
+		}
+
+		TB_Node *region = cg_control_region(p, "typeswitch_body");
+		control_regions[i] = region;
+
+		for (Ast *type_expr : cc->list) {
+			Type *case_type = type_of_expr(type_expr);
+			i64 key = -1;
+			if (switch_kind == TypeSwitch_Union) {
+				Type *ut = base_type(type_deref(parent.type));
+				if (is_type_untyped_nil(case_type)) {
+					key = 0;
+				} else {
+					key = union_variant_index(ut, case_type);
+				}
+			} else if (switch_kind == TypeSwitch_Any) {
+				if (is_type_untyped_nil(case_type)) {
+					key = 0;
+				} else {
+					key = cast(i64)cg_typeid_as_u64(p->module, case_type);
+				}
+			}
+			GB_ASSERT(key >= 0);
+
+			switch_entries[case_index++] = TB_SwitchEntry{key, region};
+		}
+	}
+
+	GB_ASSERT(case_index == num_cases);
+
+	{
+		TB_DataType dt = {};
+		TB_Node *key = nullptr;
+		if (type_size_of(parent_base_type) == 0) {
+			GB_ASSERT(tag.node == nullptr);
+			key = tb_inst_bool(p->func, false);
+			dt = cg_data_type(t_bool);
+		} else {
+			GB_ASSERT(tag.kind == cgValue_Value && tag.node != nullptr);
+			dt = cg_data_type(tag.type);
+			key = tag.node;
+		}
+
+		GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+		tb_inst_branch(p->func, dt, key, else_region, num_cases, switch_entries);
+	}
+
+
+	for_array(i, body->stmts) {
+		Ast *clause = body->stmts[i];
+		ast_node(cc, CaseClause, clause);
+
+		bool saw_nil = false;
+		for (Ast *type_expr : cc->list) {
+			Type *case_type = type_of_expr(type_expr);
+			if (is_type_untyped_nil(case_type)) {
+				saw_nil = true;
+			}
+		}
+
+		Entity *case_entity = implicit_entity_of_node(clause);
+		bool by_reference = (case_entity->flags & EntityFlag_Value) == 0;
+
+		cg_scope_open(p, cc->scope);
+
+		TB_Node *body_region = control_regions[i];
+		tb_inst_set_control(p->func, body_region);
+
+		if (cc->list.count == 1 && !saw_nil) {
+			cgValue data = {};
+			if (switch_kind == TypeSwitch_Union) {
+				data = union_data;
+			} else if (switch_kind == TypeSwitch_Any) {
+				data = cg_emit_load(p, cg_emit_struct_ep(p, parent_ptr, 0));
+			}
+			GB_ASSERT(data.kind == cgValue_Value);
+
+			Type *ct = case_entity->type;
+			Type *ct_ptr = alloc_type_pointer(ct);
+
+			cgValue ptr = {};
+
+			if (backing_ptr) { // by value
+				GB_ASSERT(!by_reference);
+
+				i64 size = type_size_of(case_entity->type);
+				i64 align = type_align_of(case_entity->type);
+
+				// make a copy of the case value
+				tb_inst_memcpy(p->func,
+				               backing_ptr, // dst
+				               data.node,   // src
+				               tb_inst_uint(p->func, TB_TYPE_INT, size),
+				               cast(TB_CharUnits)align,
+				               false
+				);
+
+				ptr = cg_value(backing_ptr, ct_ptr);
+
+			} else { // by reference
+				GB_ASSERT(by_reference);
+				ptr = cg_emit_conv(p, data, ct_ptr);
+			}
+			GB_ASSERT(are_types_identical(case_entity->type, type_deref(ptr.type)));
+
+			cg_add_entity(p->module, case_entity, ptr);
+			String name = case_entity->token.string;
+			TB_Attrib *dbg = tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, cg_debug_type(p->module, ct));
+			tb_node_append_attrib(ptr.node, dbg);
+		} else {
+			if (case_entity->flags & EntityFlag_Value) {
+				// by value
+				cgAddr x = cg_add_local(p, case_entity->type, case_entity, false);
+				cg_addr_store(p, x, parent_value);
+			} else {
+				// by reference
+				cg_add_entity(p->module, case_entity, parent_value);
+			}
+		}
+
+		cg_push_target_list(p, ss->label, done_region, nullptr, nullptr);
+		cg_build_stmt_list(p, cc->stmts);
+		cg_scope_close(p, cgDeferExit_Default, body_region);
+		cg_pop_target_list(p);
+
+		cg_emit_goto(p, done_region);
+	}
+
+	cg_emit_goto(p, done_region);
+	tb_inst_set_control(p->func, done_region);
+}
+
+
+gb_internal void cg_build_mutable_value_decl(cgProcedure *p, Ast *node) {
+	ast_node(vd, ValueDecl, node);
+	if (!vd->is_mutable) {
+		return;
+	}
+
+	bool is_static = false;
+	for (Ast *name : vd->names) if (!is_blank_ident(name)) {
+		// NOTE(bill): Sanity check to check for the existence of the variable's Entity
+		GB_ASSERT(name->kind == Ast_Ident);
+		Entity *e = entity_of_node(name);
+		TokenPos pos = ast_token(name).pos;
+		GB_ASSERT_MSG(e != nullptr, "\n%s missing entity for %.*s", token_pos_to_string(pos), LIT(name->Ident.token.string));
+		if (e->flags & EntityFlag_Static) {
+			// NOTE(bill): If one of the entities is static, they all are
+			is_static = true;
+		}
+	}
+
+	if (is_static) {
+		for_array(i, vd->names) {
+			Ast *ident = vd->names[i];
+			GB_ASSERT(!is_blank_ident(ident));
+			Entity *e = entity_of_node(ident);
+			GB_ASSERT(e->flags & EntityFlag_Static);
+			String name = e->token.string;
+
+			String mangled_name = {};
+			{
+				gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len);
+				str = gb_string_appendc(str, "-");
+				str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id);
+				mangled_name.text = cast(u8 *)str;
+				mangled_name.len = gb_string_length(str);
+			}
+
+			cgModule *m = p->module;
+
+			TB_DebugType *debug_type = cg_debug_type(m, e->type);
+			TB_Global *global = tb_global_create(m->mod, mangled_name.len, cast(char const *)mangled_name.text, debug_type, TB_LINKAGE_PRIVATE);
+
+			TB_ModuleSection *section = tb_module_get_data(m->mod);
+			if (e->Variable.thread_local_model != "") {
+				section = tb_module_get_tls(m->mod);
+				String model = e->Variable.thread_local_model;
+				if (model == "default") {
+					// TODO(bill): Thread Local Storage models
+				} else if (model == "localdynamic") {
+					// TODO(bill): Thread Local Storage models
+				} else if (model == "initialexec") {
+					// TODO(bill): Thread Local Storage models
+				} else if (model == "localexec") {
+					// TODO(bill): Thread Local Storage models
+				} else {
+					GB_PANIC("Unhandled thread local mode %.*s", LIT(model));
+				}
+			}
+
+			i64 max_objects = 0;
+			ExactValue value = {};
+
+			if (vd->values.count > 0) {
+				GB_ASSERT(vd->names.count == vd->values.count);
+				Ast *ast_value = vd->values[i];
+				GB_ASSERT(ast_value->tav.mode == Addressing_Constant ||
+				          ast_value->tav.mode == Addressing_Invalid);
+
+				value = ast_value->tav.value;
+				max_objects = cg_global_const_calculate_region_count(value, e->type);
+			}
+			tb_global_set_storage(m->mod, section, global, type_size_of(e->type), type_align_of(e->type), max_objects);
+
+			cg_global_const_add_region(m, value, e->type, global, 0);
+
+			TB_Node *node = tb_inst_get_symbol_address(p->func, cast(TB_Symbol *)global);
+			cgValue global_val = cg_value(node, alloc_type_pointer(e->type));
+			cg_add_entity(p->module, e, global_val);
+			cg_add_member(p->module, mangled_name, global_val);
+		}
+		return;
+	}
+
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	auto inits = array_make<cgValue>(temporary_allocator(), 0, vd->values.count != 0 ? vd->names.count : 0);
+	for (Ast *rhs : vd->values) {
+		cgValue init = cg_build_expr(p, rhs);
+		cg_append_tuple_values(p, &inits, init);
+	}
+
+
+	auto lvals = slice_make<cgAddr>(temporary_allocator(), vd->names.count);
+	for_array(i, vd->names) {
+		Ast *name = vd->names[i];
+		if (!is_blank_ident(name)) {
+			Entity *e = entity_of_node(name);
+			bool zero_init = vd->values.count == 0;
+			if (vd->names.count == vd->values.count) {
+				Ast *expr = unparen_expr(vd->values[i]);
+				if (expr->kind == Ast_CompoundLit &&
+				    inits[i].kind == cgValue_Addr) {
+				    	TB_Node *ptr = inits[i].node;
+
+				    	if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
+				    		// NOTE(bill): for debugging purposes only
+				    		String name = e->token.string;
+				    		TB_DebugType *debug_type = cg_debug_type(p->module, e->type);
+				    		tb_node_append_attrib(ptr, tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, debug_type));
+				    	}
+
+					cgAddr addr = cg_addr(inits[i]);
+					map_set(&p->variable_map, e, addr);
+					continue;
+				}
+			}
+
+			lvals[i] = cg_add_local(p, e->type, e, zero_init);
+		}
+	}
+
+
+	GB_ASSERT(vd->values.count == 0 || lvals.count == inits.count);
+	for_array(i, inits) {
+		cgAddr  lval = lvals[i];
+		cgValue init = inits[i];
+		cg_addr_store(p, lval, init);
+	}
+}
+
+
+gb_internal void cg_build_stmt(cgProcedure *p, Ast *node) {
+	Ast *prev_stmt = p->curr_stmt;
+	defer (p->curr_stmt = prev_stmt);
+	p->curr_stmt = node;
+
+	// TODO(bill): check if last instruction was a terminating one or not
+
+	cg_set_debug_pos_from_node(p, node);
+
+	u16 prev_state_flags = p->state_flags;
+	defer (p->state_flags = prev_state_flags);
+
+	if (node->state_flags != 0) {
+		u16 in = node->state_flags;
+		u16 out = p->state_flags;
+
+		if (in & StateFlag_bounds_check) {
+			out |= StateFlag_bounds_check;
+			out &= ~StateFlag_no_bounds_check;
+		} else if (in & StateFlag_no_bounds_check) {
+			out |= StateFlag_no_bounds_check;
+			out &= ~StateFlag_bounds_check;
+		}
+		if (in & StateFlag_no_type_assert) {
+			out |= StateFlag_no_type_assert;
+			out &= ~StateFlag_type_assert;
+		} else if (in & StateFlag_type_assert) {
+			out |= StateFlag_type_assert;
+			out &= ~StateFlag_no_type_assert;
+		}
+
+		p->state_flags = out;
+	}
+
+	switch (node->kind) {
+	case_ast_node(bs, EmptyStmt, node);
+	case_end;
+
+	case_ast_node(us, UsingStmt, node);
+	case_end;
+
+	case_ast_node(ws, WhenStmt, node);
+		cg_build_when_stmt(p, ws);
+	case_end;
+
+	case_ast_node(bs, BlockStmt, node);
+		TB_Node *done = nullptr;
+		if (bs->label != nullptr) {
+			done = cg_control_region(p, "block_done");
+			cgTargetList *tl = cg_push_target_list(p, bs->label, done, nullptr, nullptr);
+			tl->is_block = true;
+		}
+
+		cg_scope_open(p, bs->scope);
+		cg_build_stmt_list(p, bs->stmts);
+		cg_scope_close(p, cgDeferExit_Default, nullptr);
+
+		if (done != nullptr) {
+			cg_emit_goto(p, done);
+			tb_inst_set_control(p->func, done);
+		}
+
+		if (bs->label != nullptr) {
+			cg_pop_target_list(p);
+		}
+	case_end;
+
+	case_ast_node(vd, ValueDecl, node);
+		cg_build_mutable_value_decl(p, node);
+	case_end;
+
+	case_ast_node(bs, BranchStmt, node);
+ 		TB_Node *block = nullptr;
+
+		if (bs->label != nullptr) {
+			cgBranchRegions bb = cg_lookup_branch_regions(p, bs->label);
+			switch (bs->token.kind) {
+			case Token_break:    block = bb.break_;    break;
+			case Token_continue: block = bb.continue_; break;
+			case Token_fallthrough:
+				GB_PANIC("fallthrough cannot have a label");
+				break;
+			}
+		} else {
+			for (cgTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) {
+				if (t->is_block) {
+					continue;
+				}
+
+				switch (bs->token.kind) {
+				case Token_break:       block = t->break_;       break;
+				case Token_continue:    block = t->continue_;    break;
+				case Token_fallthrough: block = t->fallthrough_; break;
+				}
+			}
+		}
+		GB_ASSERT(block != nullptr);
+
+		cg_emit_defer_stmts(p, cgDeferExit_Branch, block);
+		cg_emit_goto(p, block);
+	case_end;
+
+	case_ast_node(es, ExprStmt, node);
+		cg_build_expr(p, es->expr);
+	case_end;
+
+	case_ast_node(as, AssignStmt, node);
+		cg_build_assign_stmt(p, as);
+	case_end;
+
+	case_ast_node(rs, ReturnStmt, node);
+		cg_build_return_stmt(p, rs->results);
+	case_end;
+
+	case_ast_node(is, IfStmt, node);
+		cg_build_if_stmt(p, node);
+	case_end;
+
+	case_ast_node(fs, ForStmt, node);
+		cg_build_for_stmt(p, node);
+	case_end;
+
+	case_ast_node(rs, RangeStmt, node);
+		cg_build_range_stmt(p, node);
+	case_end;
+
+	case_ast_node(rs, UnrollRangeStmt, node);
+		GB_PANIC("TODO(bill): lb_build_unroll_range_stmt");
+		// cg_build_range_stmt(p, rs, rs->scope);
+	case_end;
+
+	case_ast_node(fs, SwitchStmt, node);
+		cg_build_switch_stmt(p, node);
+	case_end;
+
+	case_ast_node(ts, TypeSwitchStmt, node);
+		cg_build_type_switch_stmt(p, node);
+	case_end;
+
+	case_ast_node(ds, DeferStmt, node);
+		Type *pt = base_type(p->type);
+		GB_ASSERT(pt->kind == Type_Proc);
+		if (pt->Proc.calling_convention == ProcCC_Odin) {
+			GB_ASSERT(p->context_stack.count != 0);
+		}
+
+		cgDefer *d = array_add_and_get(&p->defer_stack);
+		d->kind = cgDefer_Node;
+		d->scope_index = p->scope_index;
+		d->context_stack_count = p->context_stack.count;
+		d->control_region = tb_inst_get_control(p->func);
+		GB_ASSERT(d->control_region != nullptr);
+		d->stmt = ds->stmt;
+	case_end;
+
+
+
+	default:
+		GB_PANIC("TODO cg_build_stmt %.*s", LIT(ast_strings[node->kind]));
+		break;
+	}
+}
+
+gb_internal void cg_build_constant_value_decl(cgProcedure *p, AstValueDecl *vd) {
+	if (vd == nullptr || vd->is_mutable) {
+		return;
+	}
+
+	auto *min_dep_set = &p->module->info->minimum_dependency_set;
+
+	static i32 global_guid = 0;
+
+	for (Ast *ident : vd->names) {
+		GB_ASSERT(ident->kind == Ast_Ident);
+		Entity *e = entity_of_node(ident);
+		GB_ASSERT(e != nullptr);
+		if (e->kind != Entity_TypeName) {
+			continue;
+		}
+
+		bool polymorphic_struct = false;
+		if (e->type != nullptr && e->kind == Entity_TypeName) {
+			Type *bt = base_type(e->type);
+			if (bt->kind == Type_Struct) {
+				polymorphic_struct = bt->Struct.is_polymorphic;
+			}
+		}
+
+		if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+			continue;
+		}
+
+		if (e->TypeName.ir_mangled_name.len != 0) {
+			// NOTE(bill): Already set
+			continue;
+		}
+
+		cg_set_nested_type_name_ir_mangled_name(e, p);
+	}
+
+	for_array(i, vd->names) {
+		Ast *ident = vd->names[i];
+		GB_ASSERT(ident->kind == Ast_Ident);
+		Entity *e = entity_of_node(ident);
+		GB_ASSERT(e != nullptr);
+		if (e->kind != Entity_Procedure) {
+			continue;
+		}
+		GB_ASSERT (vd->values[i] != nullptr);
+
+		Ast *value = unparen_expr(vd->values[i]);
+		if (value->kind != Ast_ProcLit) {
+			continue; // It's an alias
+		}
+
+		DeclInfo *decl = decl_info_of_entity(e);
+		ast_node(pl, ProcLit, decl->proc_lit);
+		if (pl->body != nullptr) {
+			GenProcsData *gpd = e->Procedure.gen_procs;
+			if (gpd) {
+				rw_mutex_shared_lock(&gpd->mutex);
+				for (Entity *e : gpd->procs) {
+					if (!ptr_set_exists(min_dep_set, e)) {
+						continue;
+					}
+					DeclInfo *d = decl_info_of_entity(e);
+					cg_build_nested_proc(p, &d->proc_lit->ProcLit, e);
+				}
+				rw_mutex_shared_unlock(&gpd->mutex);
+			} else {
+				cg_build_nested_proc(p, pl, e);
+			}
+		} else {
+
+			// FFI - Foreign function interace
+			String original_name = e->token.string;
+			String name = original_name;
+
+			if (e->Procedure.is_foreign) {
+				GB_PANIC("cg_add_foreign_library_path");
+				// cg_add_foreign_library_path(p->module, e->Procedure.foreign_library);
+			}
+
+			if (e->Procedure.link_name.len > 0) {
+				name = e->Procedure.link_name;
+			}
+
+			cgValue *prev_value = string_map_get(&p->module->members, name);
+			if (prev_value != nullptr) {
+				// NOTE(bill): Don't do mutliple declarations in the IR
+				return;
+			}
+
+			e->Procedure.link_name = name;
+
+			cgProcedure *nested_proc = cg_procedure_create(p->module, e);
+
+			cgValue value = p->value;
+
+			array_add(&p->children, nested_proc);
+			string_map_set(&p->module->members, name, value);
+			cg_add_procedure_to_queue(nested_proc);
+		}
+	}
+}
+
+
+gb_internal void cg_build_stmt_list(cgProcedure *p, Slice<Ast *> const &stmts) {
+	for (Ast *stmt : stmts) {
+		switch (stmt->kind) {
+		case_ast_node(vd, ValueDecl, stmt);
+			cg_build_constant_value_decl(p, vd);
+		case_end;
+		case_ast_node(fb, ForeignBlockDecl, stmt);
+			ast_node(block, BlockStmt, fb->body);
+			cg_build_stmt_list(p, block->stmts);
+		case_end;
+		}
+	}
+	for (Ast *stmt : stmts) {
+		cg_build_stmt(p, stmt);
+	}
+}
+
+
+gb_internal void cg_build_when_stmt(cgProcedure *p, AstWhenStmt *ws) {
+	TypeAndValue tv = type_and_value_of_expr(ws->cond);
+	GB_ASSERT(is_type_boolean(tv.type));
+	GB_ASSERT(tv.value.kind == ExactValue_Bool);
+	if (tv.value.value_bool) {
+		cg_build_stmt_list(p, ws->body->BlockStmt.stmts);
+	} else if (ws->else_stmt) {
+		switch (ws->else_stmt->kind) {
+		case Ast_BlockStmt:
+			cg_build_stmt_list(p, ws->else_stmt->BlockStmt.stmts);
+			break;
+		case Ast_WhenStmt:
+			cg_build_when_stmt(p, &ws->else_stmt->WhenStmt);
+			break;
+		default:
+			GB_PANIC("Invalid 'else' statement in 'when' statement");
+			break;
+		}
+	}
+}
+

+ 983 - 0
src/tilde_type_info.cpp

@@ -0,0 +1,983 @@
+gb_internal void cg_global_const_type_info_ptr(cgModule *m, Type *type, TB_Global *global, i64 offset) {
+	GB_ASSERT(type != nullptr);
+	TB_Symbol *type_table_array = cg_find_symbol_from_entity(m, cg_global_type_info_data_entity);
+
+
+	i64 index_in_bytes = cast(i64)cg_type_info_index(m->info, type);
+	index_in_bytes *= type_size_of(t_type_info);
+
+	void *ti_ptr_ptr = tb_global_add_region(m->mod, global, offset, build_context.ptr_size);
+	// NOTE(bill): define the byte offset for the pointer
+	cg_write_int_at_ptr(ti_ptr_ptr, index_in_bytes, t_uintptr);
+
+	// NOTE(bill): this will add to the byte offset set previously
+	tb_global_add_symbol_reloc(m->mod, global, offset, type_table_array);
+}
+
+gb_internal cgValue cg_global_type_info_data_ptr(cgProcedure *p) {
+	cgValue v = cg_find_value_from_entity(p->module, cg_global_type_info_data_entity);
+	return cg_flatten_value(p, v);
+}
+
+gb_internal isize cg_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found) {
+	auto *set = &info->minimum_dependency_type_info_set;
+	isize index = type_info_index(info, type, err_on_not_found);
+	if (index >= 0) {
+		auto *found = map_get(set, index);
+		if (found) {
+			GB_ASSERT(*found >= 0);
+			return *found + 1;
+		}
+	}
+	if (err_on_not_found) {
+		GB_PANIC("NOT FOUND lb_type_info_index '%s' @ index %td", type_to_string(type), index);
+	}
+	return -1;
+}
+
+gb_internal cgValue cg_type_info(cgProcedure *p, Type *type) {
+	GB_ASSERT(!build_context.no_rtti);
+
+	type = default_type(type);
+
+	isize index = cg_type_info_index(p->module->info, type);
+	GB_ASSERT(index >= 0);
+
+	cgValue data = cg_global_type_info_data_ptr(p);
+	return cg_emit_array_epi(p, data, index);
+}
+
+
+gb_internal u64 cg_typeid_as_u64(cgModule *m, Type *type) {
+	GB_ASSERT(!build_context.no_rtti);
+
+	type = default_type(type);
+
+	u64 id = cast(u64)cg_type_info_index(m->info, type);
+	GB_ASSERT(id >= 0);
+
+	u64 kind = Typeid_Invalid;
+	u64 named = is_type_named(type) && type->kind != Type_Basic;
+	u64 special = 0;
+	u64 reserved = 0;
+
+	Type *bt = base_type(type);
+	TypeKind tk = bt->kind;
+	switch (tk) {
+	case Type_Basic: {
+		u32 flags = bt->Basic.flags;
+		if (flags & BasicFlag_Boolean)  kind = Typeid_Boolean;
+		if (flags & BasicFlag_Integer)  kind = Typeid_Integer;
+		if (flags & BasicFlag_Unsigned) kind = Typeid_Integer;
+		if (flags & BasicFlag_Float)    kind = Typeid_Float;
+		if (flags & BasicFlag_Complex)  kind = Typeid_Complex;
+		if (flags & BasicFlag_Pointer)  kind = Typeid_Pointer;
+		if (flags & BasicFlag_String)   kind = Typeid_String;
+		if (flags & BasicFlag_Rune)     kind = Typeid_Rune;
+	} break;
+	case Type_Pointer:              kind = Typeid_Pointer;                break;
+	case Type_MultiPointer:         kind = Typeid_Multi_Pointer;          break;
+	case Type_Array:                kind = Typeid_Array;                  break;
+	case Type_Matrix:               kind = Typeid_Matrix;                 break;
+	case Type_EnumeratedArray:      kind = Typeid_Enumerated_Array;       break;
+	case Type_Slice:                kind = Typeid_Slice;                  break;
+	case Type_DynamicArray:         kind = Typeid_Dynamic_Array;          break;
+	case Type_Map:                  kind = Typeid_Map;                    break;
+	case Type_Struct:               kind = Typeid_Struct;                 break;
+	case Type_Enum:                 kind = Typeid_Enum;                   break;
+	case Type_Union:                kind = Typeid_Union;                  break;
+	case Type_Tuple:                kind = Typeid_Tuple;                  break;
+	case Type_Proc:                 kind = Typeid_Procedure;              break;
+	case Type_BitSet:               kind = Typeid_Bit_Set;                break;
+	case Type_SimdVector:           kind = Typeid_Simd_Vector;            break;
+	case Type_RelativePointer:      kind = Typeid_Relative_Pointer;       break;
+	case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
+	case Type_SoaPointer:           kind = Typeid_SoaPointer;             break;
+	}
+
+	if (is_type_cstring(type)) {
+		special = 1;
+	} else if (is_type_integer(type) && !is_type_unsigned(type)) {
+		special = 1;
+	}
+
+	u64 data = 0;
+	if (build_context.ptr_size == 4) {
+		GB_ASSERT(id <= (1u<<24u));
+		data |= (id       &~ (1u<<24)) << 0u;  // index
+		data |= (kind     &~ (1u<<5))  << 24u; // kind
+		data |= (named    &~ (1u<<1))  << 29u; // named
+		data |= (special  &~ (1u<<1))  << 30u; // special
+		data |= (reserved &~ (1u<<1))  << 31u; // reserved
+	} else {
+		GB_ASSERT(build_context.ptr_size == 8);
+		GB_ASSERT(id <= (1ull<<56u));
+		data |= (id       &~ (1ull<<56)) << 0ul;  // index
+		data |= (kind     &~ (1ull<<5))  << 56ull; // kind
+		data |= (named    &~ (1ull<<1))  << 61ull; // named
+		data |= (special  &~ (1ull<<1))  << 62ull; // special
+		data |= (reserved &~ (1ull<<1))  << 63ull; // reserved
+	}
+	return data;
+}
+
+gb_internal cgValue cg_typeid(cgProcedure *p, Type *t) {
+	u64 x = cg_typeid_as_u64(p->module, t);
+	return cg_value(tb_inst_uint(p->func, cg_data_type(t_typeid), x), t_typeid);
+}
+
+
+
+
+gb_internal void cg_set_type_info_member_types(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, Type *(*type_proc)(isize index, void *userdata)) {
+	if (count == 0) {
+		return;
+	}
+
+	void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
+	i64 offset_in_bytes = cg_global_type_info_member_types.index * type_size_of(cg_global_type_info_member_types.elem_type);
+	cg_global_type_info_member_types.index += count;
+
+	cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_types.global);
+
+	for (isize i = 0; i < count; i++) {
+		i64 elem_size = type_size_of(cg_global_type_info_member_types.elem_type);
+		Type *type = type_proc(i, userdata);
+		i64 offset_for_elem = offset_in_bytes + i*elem_size;
+		cg_global_const_type_info_ptr(m, type, cg_global_type_info_member_types.global, offset_for_elem);
+	}
+
+	void *len_ptr  = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, count, t_int);
+}
+
+
+gb_internal void cg_set_type_info_member_names(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, String (*name_proc)(isize index, void *userdata)) {
+	if (count == 0) {
+		return;
+	}
+	void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
+	i64 offset_in_bytes = cg_global_type_info_member_names.index * type_size_of(cg_global_type_info_member_names.elem_type);
+	cg_global_type_info_member_names.index += count;
+
+	cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_names.global);
+
+	for (isize i = 0; i < count; i++) {
+		i64 elem_size = type_size_of(cg_global_type_info_member_names.elem_type);
+		String name = name_proc(i, userdata);
+		i64 offset_for_elem = offset_in_bytes + i*elem_size;
+		cg_global_const_string(m, name, cg_global_type_info_member_names.elem_type, cg_global_type_info_member_names.global, offset_for_elem);
+
+	}
+
+	void *len_ptr  = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, count, t_int);
+}
+
+
+gb_internal void cg_set_type_info_member_offsets(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, i64 (*offset_proc)(isize index, void *userdata)) {
+	if (count == 0) {
+		return;
+	}
+	void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
+	i64 offset_in_bytes = cg_global_type_info_member_offsets.index * type_size_of(cg_global_type_info_member_offsets.elem_type);
+	cg_global_type_info_member_offsets.index += count;
+
+	cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_offsets.global);
+
+	for (isize i = 0; i < count; i++) {
+		i64 elem_size = type_size_of(cg_global_type_info_member_offsets.elem_type);
+		i64 the_offset = offset_proc(i, userdata);
+		i64 offset_for_elem = offset_in_bytes + i*elem_size;
+
+		void *offset_ptr = tb_global_add_region(m->mod, cg_global_type_info_member_offsets.global, offset_for_elem, elem_size);
+		cg_write_uint_at_ptr(offset_ptr, the_offset, t_uintptr);
+	}
+
+	void *len_ptr  = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, count, t_int);
+}
+
+gb_internal void cg_set_type_info_member_usings(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, bool (*usings_proc)(isize index, void *userdata)) {
+	if (count == 0) {
+		return;
+	}
+	void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
+	i64 offset_in_bytes = cg_global_type_info_member_usings.index * type_size_of(cg_global_type_info_member_usings.elem_type);
+	cg_global_type_info_member_usings.index += count;
+
+	cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_usings.global);
+
+	for (isize i = 0; i < count; i++) {
+		i64 elem_size = type_size_of(cg_global_type_info_member_usings.elem_type);
+		GB_ASSERT(elem_size == 1);
+		bool the_usings = usings_proc(i, userdata);
+		i64 offset_for_elem = offset_in_bytes + i*elem_size;
+
+		bool *usings_ptr = cast(bool *)tb_global_add_region(m->mod, cg_global_type_info_member_usings.global, offset_for_elem, 1);
+		*usings_ptr = the_usings;
+	}
+
+	void *len_ptr  = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, count, t_int);
+}
+
+
+
+gb_internal void cg_set_type_info_member_tags(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, String (*tag_proc)(isize index, void *userdata)) {
+	if (count == 0) {
+		return;
+	}
+	void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
+	i64 offset_in_bytes = cg_global_type_info_member_tags.index * type_size_of(cg_global_type_info_member_tags.elem_type);
+	cg_global_type_info_member_tags.index += count;
+
+	cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_tags.global);
+
+	for (isize i = 0; i < count; i++) {
+		i64 elem_size = type_size_of(cg_global_type_info_member_tags.elem_type);
+		String tag = tag_proc(i, userdata);
+		i64 offset_for_elem = offset_in_bytes + i*elem_size;
+		cg_global_const_string(m, tag, cg_global_type_info_member_tags.elem_type, cg_global_type_info_member_tags.global, offset_for_elem);
+
+	}
+
+	void *len_ptr  = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, count, t_int);
+}
+
+gb_internal void cg_set_type_info_member_enum_values(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, i64 (*value_proc)(isize index, void *userdata)) {
+	if (count == 0) {
+		return;
+	}
+	void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
+	i64 offset_in_bytes = cg_global_type_info_member_enum_values.index * type_size_of(cg_global_type_info_member_enum_values.elem_type);
+	cg_global_type_info_member_enum_values.index += count;
+
+	cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
+	tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_enum_values.global);
+
+	for (isize i = 0; i < count; i++) {
+		i64 elem_size = type_size_of(cg_global_type_info_member_enum_values.elem_type);
+		GB_ASSERT(elem_size == 8);
+		i64 the_value = value_proc(i, userdata);
+		i64 offset_for_elem = offset_in_bytes + i*elem_size;
+
+		void *offset_ptr = tb_global_add_region(m->mod, cg_global_type_info_member_enum_values.global, offset_for_elem, elem_size);
+		cg_write_uint_at_ptr(offset_ptr, the_value, cg_global_type_info_member_enum_values.elem_type);
+	}
+
+	void *len_ptr  = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, count, t_int);
+}
+
+
+
+gb_internal void cg_setup_type_info_data(cgModule *m) {
+	if (build_context.no_rtti) {
+		return;
+	}
+
+	CheckerInfo *info = m->info;
+	{ // Add type info data
+		isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
+		// gb_printf_err("max_type_info_count: %td\n", max_type_info_count);
+		Type *t = alloc_type_array(t_type_info, max_type_info_count);
+
+		i64 max_objects = cast(i64)max_type_info_count * cg_global_const_calculate_region_count_from_basic_type(t_type_info);
+
+		TB_Global *g = tb_global_create(m->mod, -1, CG_TYPE_INFO_DATA_NAME, nullptr, TB_LINKAGE_PRIVATE);
+		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, max_objects);
+
+		cgValue value = cg_value(g, alloc_type_pointer(t));
+		cg_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(CG_TYPE_INFO_DATA_NAME), t, EntityState_Resolved);
+		cg_add_symbol(m, cg_global_type_info_data_entity, cast(TB_Symbol *)g);
+		cg_add_entity(m, cg_global_type_info_data_entity, value);
+	}
+
+	{ // Type info member buffer
+		// NOTE(bill): Removes need for heap allocation by making it global memory
+		isize count = 0;
+		isize enum_count = 0;
+
+		for (Type *t : m->info->type_info_types) {
+			isize index = cg_type_info_index(m->info, t, false);
+			if (index < 0) {
+				continue;
+			}
+
+			switch (t->kind) {
+			case Type_Union:
+				count += t->Union.variants.count;
+				break;
+			case Type_Struct:
+				count += t->Struct.fields.count;
+				break;
+			case Type_Tuple:
+				count += t->Tuple.variables.count;
+				break;
+			case Type_Enum:
+				enum_count += t->Enum.fields.count;
+				break;
+			}
+		}
+
+		if (count > 0) {
+			{
+				char const *name = CG_TYPE_INFO_TYPES_NAME;
+				Type *t = alloc_type_array(t_type_info_ptr, count);
+				TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count*2);
+				cg_global_type_info_member_types = GlobalTypeInfoData{g, t, t_type_info_ptr, 0};
+			}
+			{
+				char const *name = CG_TYPE_INFO_NAMES_NAME;
+				Type *t = alloc_type_array(t_string, count);
+				TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count*2);
+				cg_global_type_info_member_names = GlobalTypeInfoData{g, t, t_string, 0};
+			}
+			{
+				char const *name = CG_TYPE_INFO_OFFSETS_NAME;
+				Type *t = alloc_type_array(t_uintptr, count);
+				TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_offsets = GlobalTypeInfoData{g, t, t_uintptr, 0};
+			}
+
+			{
+				char const *name = CG_TYPE_INFO_USINGS_NAME;
+				Type *t = alloc_type_array(t_bool, count);
+				TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_usings = GlobalTypeInfoData{g, t, t_bool, 0};
+			}
+
+			{
+				char const *name = CG_TYPE_INFO_TAGS_NAME;
+				Type *t = alloc_type_array(t_string, count);
+				TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count*2);
+				cg_global_type_info_member_tags = GlobalTypeInfoData{g, t, t_string, 0};
+			}
+		}
+
+		if (enum_count > 0) {
+			char const *name = CG_TYPE_INFO_ENUM_VALUES_NAME;
+			Type *t = alloc_type_array(t_i64, enum_count);
+			TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+			tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, enum_count);
+			cg_global_type_info_member_enum_values = GlobalTypeInfoData{g, t, t_i64, 0};
+		}
+	}
+	gb_unused(info);
+
+
+	i64 global_type_info_data_entity_count = 0;
+
+	// NOTE(bill): Set the type_table slice with the global backing array
+	TB_Global *type_table_slice = cast(TB_Global *)cg_find_symbol_from_entity(m, scope_lookup_current(m->info->runtime_package->scope, str_lit("type_table")));
+	GB_ASSERT(type_table_slice != nullptr);
+
+	TB_Global *type_table_array = cast(TB_Global *)cg_find_symbol_from_entity(m, cg_global_type_info_data_entity);
+	GB_ASSERT(type_table_array != nullptr);
+
+	Type *type = base_type(cg_global_type_info_data_entity->type);
+	GB_ASSERT(is_type_array(type));
+	global_type_info_data_entity_count = type->Array.count;
+
+	tb_global_add_symbol_reloc(m->mod, type_table_slice, 0, cast(TB_Symbol *)type_table_array);
+
+	void *len_ptr = tb_global_add_region(m->mod, type_table_slice, build_context.int_size, build_context.int_size);
+	cg_write_int_at_ptr(len_ptr, type->Array.count, t_int);
+
+	// 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;
+	GB_ASSERT(type_size_of(t_type_info_flags) == 4);
+
+	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;
+
+
+	i64 type_info_size = type_size_of(t_type_info);
+	i64 size_offset    = type_offset_of(t_type_info, 0);
+	i64 align_offset   = type_offset_of(t_type_info, 1);
+	i64 flags_offset   = type_offset_of(t_type_info, 2);
+	i64 id_offset      = type_offset_of(t_type_info, 3);
+	i64 variant_offset = type_offset_of(t_type_info, 4);
+
+	Type *type_info_union = base_type(t_type_info)->Struct.fields[4]->type;
+	GB_ASSERT(type_info_union->kind == Type_Union);
+
+	i64 union_tag_offset    = type_info_union->Union.variant_block_size;
+	Type *ti_union_tag_type = union_tag_type(type_info_union);
+	u64 union_tag_type_size = type_size_of(ti_union_tag_type);
+
+	auto const &set_bool = [](cgModule *m, TB_Global *global, i64 offset, bool value) {
+		bool *ptr = cast(bool *)tb_global_add_region(m->mod, global, offset, 1);
+		*ptr = value;
+	};
+
+
+	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 = cg_type_info_index(info, t, false);
+		if (entry_index <= 0) {
+			continue;
+		}
+
+		if (entries_handled[entry_index]) {
+			continue;
+		}
+		entries_handled[entry_index] = true;
+
+		TB_Global *global = type_table_array;
+
+		i64 offset = entry_index * type_info_size;
+
+		i64 size  = type_size_of(t);
+		i64 align = type_align_of(t);
+		u32 flags = type_info_flags_of_type(t);
+		u64 id    = cg_typeid_as_u64(m, t);
+
+		void *size_ptr  = tb_global_add_region(m->mod,  global, offset+size_offset, build_context.int_size);
+		void *align_ptr = tb_global_add_region(m->mod, global, offset+align_offset, build_context.int_size);
+		void *flags_ptr = tb_global_add_region(m->mod, global, offset+flags_offset, 4);
+		void *id_ptr    = tb_global_add_region(m->mod, global, offset+id_offset,    build_context.ptr_size);
+		cg_write_int_at_ptr (size_ptr,  size,  t_int);
+		cg_write_int_at_ptr (align_ptr, align, t_int);
+		cg_write_int_at_ptr (flags_ptr, flags, t_u32);
+		cg_write_uint_at_ptr(id_ptr,    id,    t_typeid);
+
+
+		// add data to the offset to make it easier to deal with later on
+		offset += variant_offset;
+
+		Type *tag_type = nullptr;
+
+		switch (t->kind) {
+		case Type_Named: {
+			// Type_Info_Named :: struct {
+			// 	name: string,
+			// 	base: ^Type_Info,
+			// 	pkg:  string,
+			// 	loc:  Source_Code_Location,
+			// }
+			tag_type = t_type_info_named;
+
+			if (t->Named.type_name->pkg) {
+				i64 pkg_offset = type_offset_of(tag_type, 2);
+				String pkg_name = t->Named.type_name->pkg->name;
+				cg_global_const_string(m, pkg_name, t_string, global, offset+pkg_offset);
+			}
+
+			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) {
+					i64 name_offset = type_offset_of(tag_type, 0);
+					proc_name = decl->entity->token.string;
+					cg_global_const_string(m, proc_name, t_string, global, offset+name_offset);
+				}
+			}
+
+			i64 loc_offset = type_offset_of(tag_type, 3);
+			TokenPos pos = t->Named.type_name->token.pos;
+			cg_global_source_code_location_const(m, proc_name, pos, global, offset+loc_offset);
+
+			i64 base_offset = type_offset_of(tag_type, 1);
+			cg_global_const_type_info_ptr(m, t->Named.base, global, offset+base_offset);
+			break;
+		}
+
+		case Type_Basic:
+			switch (t->Basic.kind) {
+			case Basic_bool:
+			case Basic_b8:
+			case Basic_b16:
+			case Basic_b32:
+			case Basic_b64:
+				tag_type = t_type_info_boolean;
+				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_type = t_type_info_integer;
+
+				bool is_signed = (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;
+				}
+				u8 *signed_ptr     = cast(u8 *)tb_global_add_region(m->mod, global, offset+0, 1);
+				u8 *endianness_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+1, 1);
+				*signed_ptr     = is_signed;
+				*endianness_ptr = endianness_value;
+				break;
+			}
+
+			case Basic_rune:
+				tag_type = t_type_info_rune;
+				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_type = t_type_info_float;
+
+					// // 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;
+					}
+
+					u8 *ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+0, 1);
+					*ptr = endianness_value;
+				}
+				break;
+
+			case Basic_complex32:
+			case Basic_complex64:
+			case Basic_complex128:
+				tag_type = t_type_info_complex;
+				break;
+
+			case Basic_quaternion64:
+			case Basic_quaternion128:
+			case Basic_quaternion256:
+				tag_type = t_type_info_quaternion;
+				break;
+
+			case Basic_rawptr:
+				tag_type = t_type_info_pointer;
+				break;
+
+			case Basic_string:
+				tag_type = t_type_info_string;
+				break;
+
+			case Basic_cstring:
+				tag_type = t_type_info_string;
+				set_bool(m, global, offset+0, true);
+				break;
+
+			case Basic_any:
+				tag_type = t_type_info_any;
+				break;
+
+			case Basic_typeid:
+				tag_type = t_type_info_typeid;
+				break;
+			}
+			break;
+
+		case Type_Pointer:
+			tag_type = t_type_info_pointer;
+			cg_global_const_type_info_ptr(m, t->Pointer.elem, global, offset+0);
+			break;
+		case Type_MultiPointer:
+			tag_type = t_type_info_multi_pointer;
+			cg_global_const_type_info_ptr(m, t->MultiPointer.elem, global, offset+0);
+			break;
+		case Type_SoaPointer:
+			tag_type = t_type_info_soa_pointer;
+			cg_global_const_type_info_ptr(m, t->SoaPointer.elem, global, offset+0);
+			break;
+
+		case Type_Array:
+			{
+				tag_type = t_type_info_array;
+
+				cg_global_const_type_info_ptr(m, t->Array.elem, global, offset+0);
+				void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+1*build_context.int_size, build_context.int_size);
+				void *count_ptr     = tb_global_add_region(m->mod, global, offset+2*build_context.int_size, build_context.int_size);
+
+				cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->Array.elem), t_int);
+				cg_write_int_at_ptr(count_ptr,     t->Array.count,              t_int);
+			}
+			break;
+
+		case Type_EnumeratedArray:
+			{
+				tag_type = t_type_info_enumerated_array;
+
+				i64 elem_offset      = type_offset_of(tag_type, 0);
+				i64 index_offset     = type_offset_of(tag_type, 1);
+				i64 elem_size_offset = type_offset_of(tag_type, 2);
+				i64 count_offset     = type_offset_of(tag_type, 3);
+				i64 min_value_offset = type_offset_of(tag_type, 4);
+				i64 max_value_offset = type_offset_of(tag_type, 5);
+				i64 is_sparse_offset = type_offset_of(tag_type, 6);
+
+				cg_global_const_type_info_ptr(m, t->EnumeratedArray.elem,  global, offset+elem_offset);
+				cg_global_const_type_info_ptr(m, t->EnumeratedArray.index, global, offset+index_offset);
+
+				void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+elem_size_offset, build_context.int_size);
+				void *count_ptr     = tb_global_add_region(m->mod, global, offset+count_offset,     build_context.int_size);
+
+				void *min_value_ptr = tb_global_add_region(m->mod, global, offset+min_value_offset, type_size_of(t_type_info_enum_value));
+				void *max_value_ptr = tb_global_add_region(m->mod, global, offset+max_value_offset, type_size_of(t_type_info_enum_value));
+
+				cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->EnumeratedArray.elem), t_int);
+				cg_write_int_at_ptr(count_ptr,     t->EnumeratedArray.count,              t_int);
+
+				cg_write_int_at_ptr(min_value_ptr, exact_value_to_i64(*t->EnumeratedArray.min_value), t_type_info_enum_value);
+				cg_write_int_at_ptr(max_value_ptr, exact_value_to_i64(*t->EnumeratedArray.max_value), t_type_info_enum_value);
+				set_bool(m, global, offset+is_sparse_offset, t->EnumeratedArray.is_sparse);
+			}
+			break;
+
+		case Type_DynamicArray:
+			{
+				tag_type = t_type_info_dynamic_array;
+
+				cg_global_const_type_info_ptr(m, t->DynamicArray.elem, global, offset+0);
+				void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+1*build_context.int_size, build_context.int_size);
+				cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->DynamicArray.elem), t_int);
+			}
+			break;
+		case Type_Slice:
+			{
+				tag_type = t_type_info_slice;
+
+				cg_global_const_type_info_ptr(m, t->Slice.elem, global, offset+0);
+				void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+1*build_context.int_size, build_context.int_size);
+				cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->Slice.elem), t_int);
+			}
+			break;
+
+		case Type_Proc:
+			{
+				tag_type = t_type_info_procedure;
+
+				i64 params_offset     = type_offset_of(tag_type, 0);
+				i64 results_offset    = type_offset_of(tag_type, 1);
+				i64 variadic_offset   = type_offset_of(tag_type, 2);
+				i64 convention_offset = type_offset_of(tag_type, 3);
+
+				if (t->Proc.params) {
+					cg_global_const_type_info_ptr(m, t->Proc.params, global, offset+params_offset);
+				}
+				if (t->Proc.results) {
+					cg_global_const_type_info_ptr(m, t->Proc.results, global, offset+results_offset);
+				}
+
+				set_bool(m, global, offset+variadic_offset, t->Proc.variadic);
+
+				u8 *convention_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+convention_offset, 1);
+				*convention_ptr = cast(u8)t->Proc.calling_convention;
+			}
+			break;
+
+		case Type_Tuple:
+			{
+				tag_type = t_type_info_parameters;
+
+				i64 types_offset = type_offset_of(tag_type, 0);
+				i64 names_offset = type_offset_of(tag_type, 1);
+
+				i64 count = t->Tuple.variables.count;
+
+				cg_set_type_info_member_types(m, global, offset+types_offset, count, t, [](isize i, void *userdata) -> Type * {
+					Type *t = cast(Type *)userdata;
+					return t->Tuple.variables[i]->type;
+				});
+
+				cg_set_type_info_member_names(m, global, offset+names_offset, count, t, [](isize i, void *userdata) -> String {
+					Type *t = cast(Type *)userdata;
+					return t->Tuple.variables[i]->token.string;
+				});
+			}
+			break;
+
+		case Type_Enum:
+			{
+				tag_type = t_type_info_enum;
+
+				i64 base_offset   = type_offset_of(tag_type, 0);
+				i64 names_offset  = type_offset_of(tag_type, 1);
+				i64 values_offset = type_offset_of(tag_type, 2);
+
+				cg_global_const_type_info_ptr(m, t->Enum.base_type, global, offset+base_offset);
+
+				i64 count = t->Enum.fields.count;
+
+				cg_set_type_info_member_names(m, global, offset+names_offset, count, t, [](isize i, void *userdata) -> String {
+					Type *t = cast(Type *)userdata;
+					return t->Enum.fields[i]->token.string;
+				});
+
+				cg_set_type_info_member_enum_values(m, global, offset+values_offset, count, t, [](isize i, void *userdata) -> i64 {
+					Type *t = cast(Type *)userdata;
+					Entity *e = t->Enum.fields[i];
+					GB_ASSERT(e->kind == Entity_Constant);
+					return exact_value_to_i64(e->Constant.value);
+				});
+			}
+			break;
+		case Type_Struct:
+			{
+				tag_type = t_type_info_struct;
+
+				i64 types_offset         = type_offset_of(tag_type, 0);
+				i64 names_offset         = type_offset_of(tag_type, 1);
+				i64 offsets_offset       = type_offset_of(tag_type, 2);
+				i64 usings_offset        = type_offset_of(tag_type, 3);
+				i64 tags_offset          = type_offset_of(tag_type, 4);
+
+				i64 is_packed_offset     = type_offset_of(tag_type, 5);
+				i64 is_raw_union_offset  = type_offset_of(tag_type, 6);
+				i64 is_no_copy_offset    = type_offset_of(tag_type, 7);
+				i64 custom_align_offset  = type_offset_of(tag_type, 8);
+
+				i64 equal_offset         = type_offset_of(tag_type, 9);
+
+				i64 soa_kind_offset      = type_offset_of(tag_type, 10);
+				i64 soa_base_type_offset = type_offset_of(tag_type, 11);
+				i64 soa_len_offset       = type_offset_of(tag_type, 12);
+
+				// TODO(bill): equal proc stuff
+				gb_unused(equal_offset);
+
+				i64 count = t->Struct.fields.count;
+
+				cg_set_type_info_member_types(m, global, offset+types_offset, count, t, [](isize i, void *userdata) -> Type * {
+					Type *t = cast(Type *)userdata;
+					return t->Struct.fields[i]->type;
+				});
+
+				cg_set_type_info_member_names(m, global, offset+names_offset, count, t, [](isize i, void *userdata) -> String {
+					Type *t = cast(Type *)userdata;
+					return t->Struct.fields[i]->token.string;
+				});
+
+				cg_set_type_info_member_offsets(m, global, offset+offsets_offset, count, t, [](isize i, void *userdata) -> i64 {
+					Type *t = cast(Type *)userdata;
+					return t->Struct.offsets[i];
+				});
+
+				cg_set_type_info_member_usings(m, global, offset+usings_offset, count, t, [](isize i, void *userdata) -> bool {
+					Type *t = cast(Type *)userdata;
+					return (t->Struct.fields[i]->flags & EntityFlag_Using) != 0;
+				});
+
+				cg_set_type_info_member_tags(m, global, offset+tags_offset, count, t, [](isize i, void *userdata) -> String {
+					Type *t = cast(Type *)userdata;
+					return t->Struct.tags[i];
+				});
+
+
+				set_bool(m, global, offset+is_packed_offset,    t->Struct.is_packed);
+				set_bool(m, global, offset+is_raw_union_offset, t->Struct.is_raw_union);
+				set_bool(m, global, offset+is_no_copy_offset,   t->Struct.is_no_copy);
+				set_bool(m, global, offset+custom_align_offset, t->Struct.custom_align != 0);
+
+				if (t->Struct.soa_kind != StructSoa_None) {
+					u8 *kind_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+soa_kind_offset, 1);
+					*kind_ptr = cast(u8)t->Struct.soa_kind;
+
+					cg_global_const_type_info_ptr(m, t->Struct.soa_elem, global, offset+soa_base_type_offset);
+
+					void *soa_len_ptr = tb_global_add_region(m->mod, global, offset+soa_len_offset, build_context.int_size);
+					cg_write_int_at_ptr(soa_len_ptr, t->Struct.soa_count, t_int);
+				}
+			}
+			break;
+		case Type_Union:
+			{
+				tag_type = t_type_info_union;
+
+				i64 variants_offset      = type_offset_of(tag_type, 0);
+				i64 tag_offset_offset    = type_offset_of(tag_type, 1);
+				i64 tag_type_offset      = type_offset_of(tag_type, 2);
+
+				i64 equal_offset         = type_offset_of(tag_type, 3);
+
+				i64 custom_align_offset  = type_offset_of(tag_type, 4);
+				i64 no_nil_offset        = type_offset_of(tag_type, 5);
+				i64 shared_nil_offset    = type_offset_of(tag_type, 6);
+
+				// TODO(bill): equal procs
+				gb_unused(equal_offset);
+
+				i64 count = t->Union.variants.count;
+
+				cg_set_type_info_member_types(m, global, offset+variants_offset, count, t, [](isize i, void *userdata) -> Type * {
+					Type *t = cast(Type *)userdata;
+					return t->Union.variants[i];
+				});
+
+				void *tag_offset_ptr = tb_global_add_region(m->mod, global, offset+tag_offset_offset, build_context.ptr_size);
+				cg_write_uint_at_ptr(tag_offset_ptr, t->Union.variant_block_size, t_uintptr);
+
+				cg_global_const_type_info_ptr(m, union_tag_type(t), global, offset+tag_type_offset);
+
+				set_bool(m, global, offset+custom_align_offset, t->Union.custom_align != 0);
+				set_bool(m, global, offset+no_nil_offset, t->Union.kind == UnionType_no_nil);
+				set_bool(m, global, offset+shared_nil_offset, t->Union.kind == UnionType_shared_nil);
+			}
+			break;
+		case Type_Map:
+			{
+				tag_type = t_type_info_map;
+
+				i64 key_offset      = type_offset_of(tag_type, 0);
+				i64 value_offset    = type_offset_of(tag_type, 1);
+				i64 map_info_offset = type_offset_of(tag_type, 2);
+
+				// TODO(bill): map info
+				gb_unused(map_info_offset);
+
+				cg_global_const_type_info_ptr(m, t->Map.key,   global, offset+key_offset);
+				cg_global_const_type_info_ptr(m, t->Map.value, global, offset+value_offset);
+
+			}
+			break;
+		case Type_BitSet:
+			{
+				tag_type = t_type_info_bit_set;
+
+				i64 elem_offset       = type_offset_of(tag_type, 0);
+				i64 underlying_offset = type_offset_of(tag_type, 1);
+				i64 lower_offset      = type_offset_of(tag_type, 2);
+				i64 upper_offset      = type_offset_of(tag_type, 3);
+
+				cg_global_const_type_info_ptr(m, t->BitSet.elem, global, offset+elem_offset);
+				if (t->BitSet.underlying) {
+					cg_global_const_type_info_ptr(m, t->BitSet.underlying, global, offset+underlying_offset);
+				}
+
+				void *lower_ptr = tb_global_add_region(m->mod, global, offset+lower_offset, 8);
+				void *upper_ptr = tb_global_add_region(m->mod, global, offset+upper_offset, 8);
+
+				cg_write_int_at_ptr(lower_ptr, t->BitSet.lower, t_i64);
+				cg_write_int_at_ptr(upper_ptr, t->BitSet.upper, t_i64);
+			}
+			break;
+		case Type_SimdVector:
+			{
+				tag_type = t_type_info_simd_vector;
+
+				i64 elem_offset      = type_offset_of(tag_type, 0);
+				i64 elem_size_offset = type_offset_of(tag_type, 1);
+				i64 count_offset     = type_offset_of(tag_type, 2);
+
+				cg_global_const_type_info_ptr(m, t->SimdVector.elem, global, offset+elem_offset);
+
+				void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+elem_size_offset, build_context.int_size);
+				void *count_ptr     = tb_global_add_region(m->mod, global, offset+count_offset,     build_context.int_size);
+
+				cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->SimdVector.elem), t_int);
+				cg_write_int_at_ptr(count_ptr,     t->SimdVector.count,              t_int);
+			}
+			break;
+
+		case Type_RelativePointer:
+			{
+				tag_type = t_type_info_relative_pointer;
+
+				i64 pointer_offset      = type_offset_of(tag_type, 0);
+				i64 base_integer_offset = type_offset_of(tag_type, 1);
+
+				cg_global_const_type_info_ptr(m, t->RelativePointer.pointer_type, global, offset+pointer_offset);
+				cg_global_const_type_info_ptr(m, t->RelativePointer.base_integer, global, offset+base_integer_offset);
+			}
+			break;
+		case Type_RelativeMultiPointer:
+			{
+				tag_type = t_type_info_relative_multi_pointer;
+
+				i64 pointer_offset      = type_offset_of(tag_type, 0);
+				i64 base_integer_offset = type_offset_of(tag_type, 1);
+
+				cg_global_const_type_info_ptr(m, t->RelativePointer.pointer_type, global, offset+pointer_offset);
+				cg_global_const_type_info_ptr(m, t->RelativePointer.base_integer, global, offset+base_integer_offset);
+			}
+			break;
+		case Type_Matrix:
+			{
+				tag_type = t_type_info_matrix;
+
+				i64 elem_offset         = type_offset_of(tag_type, 0);
+				i64 elem_size_offset    = type_offset_of(tag_type, 1);
+				i64 elem_stride_offset  = type_offset_of(tag_type, 2);
+				i64 row_count_offset    = type_offset_of(tag_type, 3);
+				i64 column_count_offset = type_offset_of(tag_type, 4);
+
+				cg_global_const_type_info_ptr(m, t->Matrix.elem, global, offset+elem_offset);
+
+				void *elem_size_ptr    = tb_global_add_region(m->mod, global, offset+elem_size_offset,    build_context.int_size);
+				void *elem_stride_ptr  = tb_global_add_region(m->mod, global, offset+elem_stride_offset,  build_context.int_size);
+				void *row_count_ptr    = tb_global_add_region(m->mod, global, offset+row_count_offset,    build_context.int_size);
+				void *column_count_ptr = tb_global_add_region(m->mod, global, offset+column_count_offset, build_context.int_size);
+
+				cg_write_int_at_ptr(elem_size_ptr,    type_size_of(t->Matrix.elem),   t_int);
+				cg_write_int_at_ptr(elem_stride_ptr,  matrix_type_stride_in_elems(t), t_int);
+				cg_write_int_at_ptr(row_count_ptr,    t->Matrix.row_count,            t_int);
+				cg_write_int_at_ptr(column_count_ptr, t->Matrix.column_count,         t_int);
+
+			}
+			break;
+		}
+
+		if (tag_type != nullptr) {
+			i64 union_index = union_variant_index(type_info_union, tag_type);
+			GB_ASSERT(union_index != 0);
+			void *tag_ptr = tb_global_add_region(m->mod, global, offset+union_tag_offset, union_tag_type_size);
+			cg_write_int_at_ptr(tag_ptr, union_index, ti_union_tag_type);
+		}
+
+	}
+}

+ 2 - 2
src/tokenizer.cpp

@@ -696,8 +696,8 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
 			if (entry->kind != Token_Invalid && entry->hash == hash) {
 			if (entry->kind != Token_Invalid && entry->hash == hash) {
 				if (str_eq(entry->text, token->string)) {
 				if (str_eq(entry->text, token->string)) {
 					token->kind = entry->kind;
 					token->kind = entry->kind;
-					if (token->kind == Token_not_in && entry->text == "notin") {
-						syntax_warning(*token, "'notin' is deprecated in favour of 'not_in'");
+					if (token->kind == Token_not_in && entry->text.len == 5) {
+						syntax_error(*token, "Did you mean 'not_in'?");
 					}
 					}
 				}
 				}
 			}
 			}

+ 127 - 70
src/types.cpp

@@ -143,6 +143,7 @@ struct TypeStruct {
 	Type *          soa_elem;
 	Type *          soa_elem;
 	i32             soa_count;
 	i32             soa_count;
 	StructSoaKind   soa_kind;
 	StructSoaKind   soa_kind;
+	BlockingMutex   mutex; // for settings offsets
 
 
 	bool            is_polymorphic;
 	bool            is_polymorphic;
 	bool            are_offsets_set             : 1;
 	bool            are_offsets_set             : 1;
@@ -244,6 +245,7 @@ struct TypeProc {
 	TYPE_KIND(Tuple, struct {                                 \
 	TYPE_KIND(Tuple, struct {                                 \
 		Slice<Entity *> variables; /* Entity_Variable */  \
 		Slice<Entity *> variables; /* Entity_Variable */  \
 		i64 *           offsets;                          \
 		i64 *           offsets;                          \
+		BlockingMutex   mutex; /* for settings offsets */ \
 		bool            are_offsets_being_processed;      \
 		bool            are_offsets_being_processed;      \
 		bool            are_offsets_set;                  \
 		bool            are_offsets_set;                  \
 		bool            is_packed;                        \
 		bool            is_packed;                        \
@@ -265,8 +267,8 @@ struct TypeProc {
 		Type *pointer_type;                               \
 		Type *pointer_type;                               \
 		Type *base_integer;                               \
 		Type *base_integer;                               \
 	})                                                        \
 	})                                                        \
-	TYPE_KIND(RelativeSlice, struct {                         \
-		Type *slice_type;                                 \
+	TYPE_KIND(RelativeMultiPointer, struct {                  \
+		Type *pointer_type;                               \
 		Type *base_integer;                               \
 		Type *base_integer;                               \
 	})                                                        \
 	})                                                        \
 	TYPE_KIND(Matrix, struct {                                \
 	TYPE_KIND(Matrix, struct {                                \
@@ -347,7 +349,7 @@ enum Typeid_Kind : u8 {
 	Typeid_Bit_Set,
 	Typeid_Bit_Set,
 	Typeid_Simd_Vector,
 	Typeid_Simd_Vector,
 	Typeid_Relative_Pointer,
 	Typeid_Relative_Pointer,
-	Typeid_Relative_Slice,
+	Typeid_Relative_Multi_Pointer,
 	Typeid_Matrix,
 	Typeid_Matrix,
 	Typeid_SoaPointer,
 	Typeid_SoaPointer,
 };
 };
@@ -594,6 +596,7 @@ gb_global Type *t_untyped_uninit     = &basic_types[Basic_UntypedUninit];
 
 
 
 
 gb_global Type *t_u8_ptr       = nullptr;
 gb_global Type *t_u8_ptr       = nullptr;
+gb_global Type *t_u8_multi_ptr = nullptr;
 gb_global Type *t_int_ptr      = nullptr;
 gb_global Type *t_int_ptr      = nullptr;
 gb_global Type *t_i64_ptr      = nullptr;
 gb_global Type *t_i64_ptr      = nullptr;
 gb_global Type *t_f64_ptr      = nullptr;
 gb_global Type *t_f64_ptr      = nullptr;
@@ -632,7 +635,7 @@ gb_global Type *t_type_info_map                  = nullptr;
 gb_global Type *t_type_info_bit_set              = nullptr;
 gb_global Type *t_type_info_bit_set              = nullptr;
 gb_global Type *t_type_info_simd_vector          = nullptr;
 gb_global Type *t_type_info_simd_vector          = nullptr;
 gb_global Type *t_type_info_relative_pointer     = nullptr;
 gb_global Type *t_type_info_relative_pointer     = nullptr;
-gb_global Type *t_type_info_relative_slice       = 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_matrix               = nullptr;
 gb_global Type *t_type_info_soa_pointer          = nullptr;
 gb_global Type *t_type_info_soa_pointer          = nullptr;
 
 
@@ -661,7 +664,7 @@ gb_global Type *t_type_info_map_ptr              = nullptr;
 gb_global Type *t_type_info_bit_set_ptr          = nullptr;
 gb_global Type *t_type_info_bit_set_ptr          = nullptr;
 gb_global Type *t_type_info_simd_vector_ptr      = nullptr;
 gb_global Type *t_type_info_simd_vector_ptr      = nullptr;
 gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
 gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
-gb_global Type *t_type_info_relative_slice_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_matrix_ptr           = nullptr;
 gb_global Type *t_type_info_soa_pointer_ptr      = nullptr;
 gb_global Type *t_type_info_soa_pointer_ptr      = nullptr;
 
 
@@ -725,7 +728,7 @@ struct TypePath;
 
 
 gb_internal i64      type_size_of   (Type *t);
 gb_internal i64      type_size_of   (Type *t);
 gb_internal i64      type_align_of  (Type *t);
 gb_internal i64      type_align_of  (Type *t);
-gb_internal i64      type_offset_of (Type *t, i32 index);
+gb_internal i64      type_offset_of (Type *t, i64 index, Type **field_type_=nullptr);
 gb_internal gbString type_to_string (Type *type, bool shorthand=true);
 gb_internal gbString type_to_string (Type *type, bool shorthand=true);
 gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true);
 gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true);
 gb_internal i64      type_size_of_internal(Type *t, TypePath *path);
 gb_internal i64      type_size_of_internal(Type *t, TypePath *path);
@@ -734,6 +737,7 @@ gb_internal Type *   bit_set_to_int(Type *t);
 gb_internal bool     are_types_identical(Type *x, Type *y);
 gb_internal bool     are_types_identical(Type *x, Type *y);
 
 
 gb_internal bool  is_type_pointer(Type *t);
 gb_internal bool  is_type_pointer(Type *t);
+gb_internal bool  is_type_multi_pointer(Type *t);
 gb_internal bool  is_type_soa_pointer(Type *t);
 gb_internal bool  is_type_soa_pointer(Type *t);
 gb_internal bool  is_type_proc(Type *t);
 gb_internal bool  is_type_proc(Type *t);
 gb_internal bool  is_type_slice(Type *t);
 gb_internal bool  is_type_slice(Type *t);
@@ -821,6 +825,9 @@ gb_internal void type_path_pop(TypePath *tp) {
 #define FAILURE_ALIGNMENT 0
 #define FAILURE_ALIGNMENT 0
 
 
 gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
 gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
+	if (t == nullptr) {
+		return true;
+	}
 	if (ptr_set_exists(s, t)) {
 	if (ptr_set_exists(s, t)) {
 		return true;
 		return true;
 	}
 	}
@@ -829,6 +836,10 @@ gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
 }
 }
 
 
 gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
 gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
+	if (t == nullptr) {
+		return true;
+	}
+
 	if (ptr_set_exists(s, t)) {
 	if (ptr_set_exists(s, t)) {
 		return true;
 		return true;
 	}
 	}
@@ -988,7 +999,7 @@ gb_internal Type *alloc_type_enumerated_array(Type *elem, Type *index, ExactValu
 
 
 gb_internal Type *alloc_type_slice(Type *elem) {
 gb_internal Type *alloc_type_slice(Type *elem) {
 	Type *t = alloc_type(Type_Slice);
 	Type *t = alloc_type(Type_Slice);
-	t->Array.elem = elem;
+	t->Slice.elem = elem;
 	return t;
 	return t;
 }
 }
 
 
@@ -1025,12 +1036,12 @@ gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_int
 	return t;
 	return t;
 }
 }
 
 
-gb_internal Type *alloc_type_relative_slice(Type *slice_type, Type *base_integer) {
-	GB_ASSERT(is_type_slice(slice_type));
+gb_internal Type *alloc_type_relative_multi_pointer(Type *pointer_type, Type *base_integer) {
+	GB_ASSERT(is_type_multi_pointer(pointer_type));
 	GB_ASSERT(is_type_integer(base_integer));
 	GB_ASSERT(is_type_integer(base_integer));
-	Type *t = alloc_type(Type_RelativeSlice);
-	t->RelativeSlice.slice_type   = slice_type;
-	t->RelativeSlice.base_integer = base_integer;
+	Type *t = alloc_type(Type_RelativeMultiPointer);
+	t->RelativeMultiPointer.pointer_type = pointer_type;
+	t->RelativeMultiPointer.base_integer = base_integer;
 	return t;
 	return t;
 }
 }
 
 
@@ -1541,9 +1552,9 @@ gb_internal bool is_type_relative_pointer(Type *t) {
 	t = base_type(t);
 	t = base_type(t);
 	return t->kind == Type_RelativePointer;
 	return t->kind == Type_RelativePointer;
 }
 }
-gb_internal bool is_type_relative_slice(Type *t) {
+gb_internal bool is_type_relative_multi_pointer(Type *t) {
 	t = base_type(t);
 	t = base_type(t);
-	return t->kind == Type_RelativeSlice;
+	return t->kind == Type_RelativeMultiPointer;
 }
 }
 
 
 gb_internal bool is_type_u8_slice(Type *t) {
 gb_internal bool is_type_u8_slice(Type *t) {
@@ -1960,7 +1971,7 @@ gb_internal bool is_type_indexable(Type *t) {
 		return true;
 		return true;
 	case Type_EnumeratedArray:
 	case Type_EnumeratedArray:
 		return true;
 		return true;
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		return true;
 		return true;
 	case Type_Matrix:
 	case Type_Matrix:
 		return true;
 		return true;
@@ -1979,7 +1990,7 @@ gb_internal bool is_type_sliceable(Type *t) {
 		return true;
 		return true;
 	case Type_EnumeratedArray:
 	case Type_EnumeratedArray:
 		return false;
 		return false;
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		return true;
 		return true;
 	case Type_Matrix:
 	case Type_Matrix:
 		return false;
 		return false;
@@ -2185,12 +2196,12 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
 		}
 		}
 		break;
 		break;
 
 
-	case Type_RelativeSlice:
-		if (is_type_polymorphic(t->RelativeSlice.slice_type, or_specialized)) {
+	case Type_RelativeMultiPointer:
+		if (is_type_polymorphic(t->RelativeMultiPointer.pointer_type, or_specialized)) {
 			return true;
 			return true;
 		}
 		}
-		if (t->RelativeSlice.base_integer != nullptr &&
-		    is_type_polymorphic(t->RelativeSlice.base_integer, or_specialized)) {
+		if (t->RelativeMultiPointer.base_integer != nullptr &&
+		    is_type_polymorphic(t->RelativeMultiPointer.base_integer, or_specialized)) {
 			return true;
 			return true;
 		}
 		}
 		break;
 		break;
@@ -2248,7 +2259,7 @@ gb_internal bool type_has_nil(Type *t) {
 		return false;
 		return false;
 
 
 	case Type_RelativePointer:
 	case Type_RelativePointer:
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		return true;
 		return true;
 	}
 	}
 	return false;
 	return false;
@@ -2415,7 +2426,7 @@ gb_internal bool is_type_load_safe(Type *type) {
 		return true;
 		return true;
 
 
 	case Type_RelativePointer:
 	case Type_RelativePointer:
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		return true;
 		return true;
 
 
 	case Type_Pointer:
 	case Type_Pointer:
@@ -2666,7 +2677,6 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
 		    x->Struct.soa_kind == y->Struct.soa_kind &&
 		    x->Struct.soa_kind == y->Struct.soa_kind &&
 		    x->Struct.soa_count == y->Struct.soa_count &&
 		    x->Struct.soa_count == y->Struct.soa_count &&
 		    are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
 		    are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
-			// TODO(bill); Fix the custom alignment rule
 			for_array(i, x->Struct.fields) {
 			for_array(i, x->Struct.fields) {
 				Entity *xf = x->Struct.fields[i];
 				Entity *xf = x->Struct.fields[i];
 				Entity *yf = y->Struct.fields[i];
 				Entity *yf = y->Struct.fields[i];
@@ -2807,7 +2817,6 @@ gb_internal i64 union_tag_size(Type *u) {
 		return 0;
 		return 0;
 	}
 	}
 
 
-	// TODO(bill): Is this an okay approach?
 	i64 max_align = 1;
 	i64 max_align = 1;
 
 
 	if (u->Union.variants.count < 1ull<<8) {
 	if (u->Union.variants.count < 1ull<<8) {
@@ -2817,7 +2826,7 @@ gb_internal i64 union_tag_size(Type *u) {
 	} else if (u->Union.variants.count < 1ull<<32) {
 	} else if (u->Union.variants.count < 1ull<<32) {
 		max_align = 4;
 		max_align = 4;
 	} else {
 	} else {
-		GB_PANIC("how many variants do you have?!");
+		compiler_error("how many variants do you have?! %lld", cast(long long)u->Union.variants.count);
 	}
 	}
 
 
 	for_array(i, u->Union.variants) {
 	for_array(i, u->Union.variants) {
@@ -3081,7 +3090,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
 				mutex_lock(md->mutex);
 				mutex_lock(md->mutex);
 				defer (mutex_unlock(md->mutex));
 				defer (mutex_unlock(md->mutex));
 				for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
 				for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
-					GB_ASSERT(entry.entity->kind == Entity_Procedure);
+					GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
 					if (entry.name == field_name) {
 					if (entry.name == field_name) {
 						sel.entity = entry.entity;
 						sel.entity = entry.entity;
 						sel.pseudo_field = true;
 						sel.pseudo_field = true;
@@ -3136,8 +3145,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
 		switch (type->Basic.kind) {
 		switch (type->Basic.kind) {
 		case Basic_any: {
 		case Basic_any: {
 		#if 1
 		#if 1
-			// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
-			// `Raw_Any` type?
 			String data_str = str_lit("data");
 			String data_str = str_lit("data");
 			String id_str = str_lit("id");
 			String id_str = str_lit("id");
 			gb_local_persist Entity *entity__any_data = alloc_entity_field(nullptr, make_token_ident(data_str), t_rawptr, false, 0);
 			gb_local_persist Entity *entity__any_data = alloc_entity_field(nullptr, make_token_ident(data_str), t_rawptr, false, 0);
@@ -3623,8 +3630,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
 
 
 	case Type_RelativePointer:
 	case Type_RelativePointer:
 		return type_align_of_internal(t->RelativePointer.base_integer, path);
 		return type_align_of_internal(t->RelativePointer.base_integer, path);
-	case Type_RelativeSlice:
-		return type_align_of_internal(t->RelativeSlice.base_integer, path);
+	case Type_RelativeMultiPointer:
+		return type_align_of_internal(t->RelativeMultiPointer.base_integer, path);
 
 
 	case Type_SoaPointer:
 	case Type_SoaPointer:
 		return build_context.int_size;
 		return build_context.int_size;
@@ -3645,28 +3652,35 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
 		}
 		}
 	} else if (is_packed) {
 	} else if (is_packed) {
 		for_array(i, fields) {
 		for_array(i, fields) {
-			i64 size = type_size_of(fields[i]->type);
-			offsets[i] = curr_offset;
-			curr_offset += size;
+			if (fields[i]->kind != Entity_Variable) {
+				offsets[i] = -1;
+			} else {
+				i64 size = type_size_of(fields[i]->type);
+				offsets[i] = curr_offset;
+				curr_offset += size;
+			}
 		}
 		}
 	} else {
 	} else {
 		for_array(i, fields) {
 		for_array(i, fields) {
-			Type *t = fields[i]->type;
-			i64 align = gb_max(type_align_of(t), 1);
-			i64 size  = gb_max(type_size_of( t), 0);
-			curr_offset = align_formula(curr_offset, align);
-			offsets[i] = curr_offset;
-			curr_offset += size;
+			if (fields[i]->kind != Entity_Variable) {
+				offsets[i] = -1;
+			} else {
+				Type *t = fields[i]->type;
+				i64 align = gb_max(type_align_of(t), 1);
+				i64 size  = gb_max(type_size_of( t), 0);
+				curr_offset = align_formula(curr_offset, align);
+				offsets[i] = curr_offset;
+				curr_offset += size;
+			}
 		}
 		}
 	}
 	}
 	return offsets;
 	return offsets;
 }
 }
 
 
 gb_internal bool type_set_offsets(Type *t) {
 gb_internal bool type_set_offsets(Type *t) {
-	MUTEX_GUARD(&g_type_mutex); // TODO(bill): only per struct
-
 	t = base_type(t);
 	t = base_type(t);
 	if (t->kind == Type_Struct) {
 	if (t->kind == Type_Struct) {
+		MUTEX_GUARD(&t->Struct.mutex);
 		if (!t->Struct.are_offsets_set) {
 		if (!t->Struct.are_offsets_set) {
 			t->Struct.are_offsets_being_processed = true;
 			t->Struct.are_offsets_being_processed = true;
 			t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union);
 			t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union);
@@ -3675,6 +3689,7 @@ gb_internal bool type_set_offsets(Type *t) {
 			return true;
 			return true;
 		}
 		}
 	} else if (is_type_tuple(t)) {
 	} else if (is_type_tuple(t)) {
+		MUTEX_GUARD(&t->Tuple.mutex);
 		if (!t->Tuple.are_offsets_set) {
 		if (!t->Tuple.are_offsets_set) {
 			t->Tuple.are_offsets_being_processed = true;
 			t->Tuple.are_offsets_being_processed = true;
 			t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false);
 			t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false);
@@ -3849,7 +3864,6 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
 					max = size;
 					max = size;
 				}
 				}
 			}
 			}
-			// TODO(bill): Is this how it should work?
 			return align_formula(max, align);
 			return align_formula(max, align);
 		} else {
 		} else {
 			i64 count = 0, size = 0, align = 0;
 			i64 count = 0, size = 0, align = 0;
@@ -3899,59 +3913,100 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
 
 
 	case Type_RelativePointer:
 	case Type_RelativePointer:
 		return type_size_of_internal(t->RelativePointer.base_integer, path);
 		return type_size_of_internal(t->RelativePointer.base_integer, path);
-	case Type_RelativeSlice:
-		return 2*type_size_of_internal(t->RelativeSlice.base_integer, path);
+	case Type_RelativeMultiPointer:
+		return type_size_of_internal(t->RelativeMultiPointer.base_integer, path);
 	}
 	}
 
 
 	// Catch all
 	// Catch all
 	return build_context.ptr_size;
 	return build_context.ptr_size;
 }
 }
 
 
-gb_internal i64 type_offset_of(Type *t, i32 index) {
+gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) {
 	t = base_type(t);
 	t = base_type(t);
-	if (t->kind == Type_Struct) {
+	switch (t->kind) {
+	case Type_Struct:
 		type_set_offsets(t);
 		type_set_offsets(t);
 		if (gb_is_between(index, 0, t->Struct.fields.count-1)) {
 		if (gb_is_between(index, 0, t->Struct.fields.count-1)) {
 			GB_ASSERT(t->Struct.offsets != nullptr);
 			GB_ASSERT(t->Struct.offsets != nullptr);
+			if (field_type_) *field_type_ = t->Struct.fields[index]->type;
 			return t->Struct.offsets[index];
 			return t->Struct.offsets[index];
 		}
 		}
-	} else if (t->kind == Type_Tuple) {
+		break;
+	case Type_Tuple:
 		type_set_offsets(t);
 		type_set_offsets(t);
 		if (gb_is_between(index, 0, t->Tuple.variables.count-1)) {
 		if (gb_is_between(index, 0, t->Tuple.variables.count-1)) {
 			GB_ASSERT(t->Tuple.offsets != nullptr);
 			GB_ASSERT(t->Tuple.offsets != nullptr);
-			return t->Tuple.offsets[index];
+			if (field_type_) *field_type_ = t->Tuple.variables[index]->type;
+			i64 offset = t->Tuple.offsets[index];
+			GB_ASSERT(offset >= 0);
+			return offset;
 		}
 		}
-	}  else if (t->kind == Type_Basic) {
+		break;
+
+	case Type_Array:
+		GB_ASSERT(0 <= index && index < t->Array.count);
+		return index * type_size_of(t->Array.elem);
+
+	case Type_Basic:
 		if (t->Basic.kind == Basic_string) {
 		if (t->Basic.kind == Basic_string) {
 			switch (index) {
 			switch (index) {
-			case 0: return 0;                      // data
-			case 1: return build_context.int_size; // len
+			case 0:
+				if (field_type_) *field_type_ = t_u8_ptr;
+				return 0;                      // data
+			case 1:
+				if (field_type_) *field_type_ = t_int;
+				return build_context.int_size; // len
 			}
 			}
 		} else if (t->Basic.kind == Basic_any) {
 		} else if (t->Basic.kind == Basic_any) {
 			switch (index) {
 			switch (index) {
-			case 0: return 0;                      // type_info
-			case 1: return build_context.ptr_size; // data
+			case 0:
+				if (field_type_) *field_type_ = t_rawptr;
+				return 0;                      // data
+			case 1:
+				if (field_type_) *field_type_ = t_typeid;
+				return build_context.ptr_size; // id
 			}
 			}
 		}
 		}
-	} else if (t->kind == Type_Slice) {
+		break;
+	case Type_Slice:
 		switch (index) {
 		switch (index) {
-		case 0: return 0;                        // data
-		case 1: return 1*build_context.int_size; // len
-		case 2: return 2*build_context.int_size; // cap
+		case 0:
+			if (field_type_) *field_type_ = alloc_type_multi_pointer(t->Slice.elem);
+			return 0;                        // data
+		case 1:
+			if (field_type_) *field_type_ = t_int;
+			return 1*build_context.int_size; // len
 		}
 		}
-	} else if (t->kind == Type_DynamicArray) {
+		break;
+	case Type_DynamicArray:
 		switch (index) {
 		switch (index) {
-		case 0: return 0;                        // data
-		case 1: return 1*build_context.int_size; // len
-		case 2: return 2*build_context.int_size; // cap
-		case 3: return 3*build_context.int_size; // allocator
+		case 0:
+			if (field_type_) *field_type_ = alloc_type_multi_pointer(t->DynamicArray.elem);
+			return 0;                        // data
+		case 1:
+			if (field_type_) *field_type_ = t_int;
+			return 1*build_context.int_size; // len
+		case 2:
+			if (field_type_) *field_type_ = t_int;
+			return 2*build_context.int_size; // cap
+		case 3:
+			if (field_type_) *field_type_ = t_allocator;
+			return 3*build_context.int_size; // allocator
 		}
 		}
-	} else if (t->kind == Type_Union) {
-		/* i64 s = */ type_size_of(t);
-		switch (index) {
-		case -1: return align_formula(t->Union.variant_block_size, build_context.ptr_size); // __type_info
+		break;
+	case Type_Union:
+		if (!is_type_union_maybe_pointer(t)) {
+			/* i64 s = */ type_size_of(t);
+			switch (index) {
+			case -1:
+				if (field_type_) *field_type_ = union_tag_type(t);
+				union_tag_size(t);
+				return t->Union.variant_block_size;
+			}
 		}
 		}
+		break;
 	}
 	}
+	GB_ASSERT(index == 0);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3965,8 +4020,10 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
 		i32 index = sel.index[i];
 		i32 index = sel.index[i];
 		t = base_type(t);
 		t = base_type(t);
 		offset += type_offset_of(t, index);
 		offset += type_offset_of(t, index);
-		if (t->kind == Type_Struct && !t->Struct.is_raw_union) {
+		if (t->kind == Type_Struct) {
 			t = t->Struct.fields[index]->type;
 			t = t->Struct.fields[index]->type;
+		} else if (t->kind == Type_Array) {
+			t = t->Array.elem;
 		} else {
 		} else {
 			// NOTE(bill): No need to worry about custom types, just need the alignment
 			// NOTE(bill): No need to worry about custom types, just need the alignment
 			switch (t->kind) {
 			switch (t->kind) {
@@ -4410,11 +4467,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
 		str = gb_string_append_fmt(str, ") ");
 		str = gb_string_append_fmt(str, ") ");
 		str = write_type_to_string(str, type->RelativePointer.pointer_type);
 		str = write_type_to_string(str, type->RelativePointer.pointer_type);
 		break;
 		break;
-	case Type_RelativeSlice:
+	case Type_RelativeMultiPointer:
 		str = gb_string_append_fmt(str, "#relative(");
 		str = gb_string_append_fmt(str, "#relative(");
-		str = write_type_to_string(str, type->RelativeSlice.base_integer);
+		str = write_type_to_string(str, type->RelativePointer.base_integer);
 		str = gb_string_append_fmt(str, ") ");
 		str = gb_string_append_fmt(str, ") ");
-		str = write_type_to_string(str, type->RelativeSlice.slice_type);
+		str = write_type_to_string(str, type->RelativePointer.pointer_type);
 		break;
 		break;
 		
 		
 	case Type_Matrix:
 	case Type_Matrix:

+ 14 - 20
tests/core/encoding/hxa/test_core_hxa.odin

@@ -21,16 +21,13 @@ main :: proc() {
 
 
 @test
 @test
 test_read :: proc(t: ^testing.T) {
 test_read :: proc(t: ^testing.T) {
-
-	using hxa
-
 	filename := tc.get_data_path(t, TEAPOT_PATH)
 	filename := tc.get_data_path(t, TEAPOT_PATH)
 	defer delete(filename)
 	defer delete(filename)
 
 
-	file, err := read_from_file(filename)
+	file, err := hxa.read_from_file(filename)
 	e :: hxa.Read_Error.None
 	e :: hxa.Read_Error.None
 	tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e))
 	tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e))
-	defer file_destroy(file)
+	defer hxa.file_destroy(file)
 
 
 	/* Header */
 	/* Header */
 	tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v",
 	tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v",
@@ -134,38 +131,35 @@ test_read :: proc(t: ^testing.T) {
 
 
 @test
 @test
 test_write :: proc(t: ^testing.T) {
 test_write :: proc(t: ^testing.T) {
-
-	using hxa
-
-	n1 :Node
+	n1: hxa.Node
 
 
 	n1_m1_value := []f64le{0.4, -1.23, 2341.6, -333.333}
 	n1_m1_value := []f64le{0.4, -1.23, 2341.6, -333.333}
-	n1_m1 := Meta{"m1", n1_m1_value}
+	n1_m1 := hxa.Meta{"m1", n1_m1_value}
 
 
-	n1.meta_data = []Meta{n1_m1}
+	n1.meta_data = []hxa.Meta{n1_m1}
 
 
-	n1_l1 := Layer{"l1", 2, []f32le{32.1, -41.3}}
-	n1_l2 := Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}}
+	n1_l1 := hxa.Layer{"l1", 2, []f32le{32.1, -41.3}}
+	n1_l2 := hxa.Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}}
 
 
-	n1_content := Node_Image{Image_Type.Image_1D, [3]u32le{1, 1, 2}, Layer_Stack{n1_l1, n1_l2}} 
+	n1_content := hxa.Node_Image{.Image_1D, [3]u32le{1, 1, 2}, hxa.Layer_Stack{n1_l1, n1_l2}}
 
 
 	n1.content = n1_content
 	n1.content = n1_content
 
 
-	w_file :File
-	w_file.nodes = []Node{n1}
+	w_file: hxa.File
+	w_file.nodes = []hxa.Node{n1}
 
 
-	required_size := required_write_size(w_file)
+	required_size := hxa.required_write_size(w_file)
 	buf := make([]u8, required_size)
 	buf := make([]u8, required_size)
 
 
-	n, write_err := write(buf, w_file)
+	n, write_err := hxa.write(buf, w_file)
 	write_e :: hxa.Write_Error.None
 	write_e :: hxa.Write_Error.None
 	tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e))
 	tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e))
 	tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size))
 	tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size))
 
 
-	file, read_err := read(buf)
+	file, read_err := hxa.read(buf)
 	read_e :: hxa.Read_Error.None
 	read_e :: hxa.Read_Error.None
 	tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e))
 	tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e))
-	defer file_destroy(file)
+	defer hxa.file_destroy(file)
 
 
 	delete(buf)
 	delete(buf)
 
 

+ 33 - 36
tests/core/encoding/xml/test_core_xml.odin

@@ -47,7 +47,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "恥ずべきフクロウ",
 			expected_doctype = "恥ずべきフクロウ",
 		},
 		},
-		crc32     = 0x30d82264,
+		crc32     = 0xe9b62f03,
 	},
 	},
 
 
 	{
 	{
@@ -62,7 +62,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "恥ずべきフクロウ",
 			expected_doctype = "恥ずべきフクロウ",
 		},
 		},
-		crc32     = 0xad31d8e8,
+		crc32     = 0x9c2643ed,
 	},
 	},
 
 
 	{
 	{
@@ -77,7 +77,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "TS",
 			expected_doctype = "TS",
 		},
 		},
-		crc32     = 0x7bce2630,
+		crc32     = 0x859b7443,
 	},
 	},
 
 
 	{
 	{
@@ -92,7 +92,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "xliff",
 			expected_doctype = "xliff",
 		},
 		},
-		crc32     = 0x43f19d61,
+		crc32     = 0x3deaf329,
 	},
 	},
 
 
 	{
 	{
@@ -107,7 +107,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "xliff",
 			expected_doctype = "xliff",
 		},
 		},
-		crc32     = 0x961e7635,
+		crc32     = 0x0c55e287,
 	},
 	},
 
 
 	{
 	{
@@ -118,7 +118,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "html",
 			expected_doctype = "html",
 		},
 		},
-		crc32     = 0x573c1033,
+		crc32     = 0x05373317,
 	},
 	},
 
 
 	{
 	{
@@ -129,7 +129,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "html",
 			expected_doctype = "html",
 		},
 		},
-		crc32     = 0x82588917,
+		crc32     = 0x3b6d4a90,
 	},
 	},
 
 
 	{
 	{
@@ -140,7 +140,7 @@ TESTS :: []TEST{
 			},
 			},
 			expected_doctype = "html",
 			expected_doctype = "html",
 		},
 		},
-		crc32     = 0x5e74d8a6,
+		crc32     = 0x5be2ffdc,
 	},
 	},
 
 
 	/*
 	/*
@@ -170,7 +170,7 @@ TESTS :: []TEST{
 			expected_doctype = "",
 			expected_doctype = "",
 		},
 		},
 		err       = .None,
 		err       = .None,
-		crc32     = 0xcaa042b9,
+		crc32     = 0x420dbac5,
 	},
 	},
 }
 }
 
 
@@ -214,43 +214,40 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
 	*/
 	*/
 	print :: proc(writer: io.Writer, doc: ^xml.Document) -> (written: int, err: io.Error) {
 	print :: proc(writer: io.Writer, doc: ^xml.Document) -> (written: int, err: io.Error) {
 		if doc == nil { return }
 		if doc == nil { return }
-		using fmt
 
 
-		written += wprintf(writer, "[XML Prolog]\n")
+		written += fmt.wprintf(writer, "[XML Prolog]\n")
 
 
 		for attr in doc.prologue {
 		for attr in doc.prologue {
-			written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
+			written += fmt.wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
 		}
 		}
 
 
-		written += wprintf(writer, "[Encoding] %v\n", doc.encoding)
+		written += fmt.wprintf(writer, "[Encoding] %v\n", doc.encoding)
 
 
 		if len(doc.doctype.ident) > 0 {
 		if len(doc.doctype.ident) > 0 {
-			written += wprintf(writer, "[DOCTYPE]  %v\n", doc.doctype.ident)
+			written += fmt.wprintf(writer, "[DOCTYPE]  %v\n", doc.doctype.ident)
 
 
 			if len(doc.doctype.rest) > 0 {
 			if len(doc.doctype.rest) > 0 {
-			 	wprintf(writer, "\t%v\n", doc.doctype.rest)
+			 	fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
 			}
 			}
 		}
 		}
 
 
 		for comment in doc.comments {
 		for comment in doc.comments {
-			written += wprintf(writer, "[Pre-root comment]  %v\n", comment)
+			written += fmt.wprintf(writer, "[Pre-root comment]  %v\n", comment)
 		}
 		}
 
 
 		if doc.element_count > 0 {
 		if doc.element_count > 0 {
-		 	wprintln(writer, " --- ")
+		 	fmt.wprintln(writer, " --- ")
 		 	print_element(writer, doc, 0)
 		 	print_element(writer, doc, 0)
-		 	wprintln(writer, " --- ")
+		 	fmt.wprintln(writer, " --- ")
 		 }
 		 }
 
 
 		return written, .None
 		return written, .None
 	}
 	}
 
 
 	print_element :: proc(writer: io.Writer, doc: ^xml.Document, element_id: xml.Element_ID, indent := 0) -> (written: int, err: io.Error) {
 	print_element :: proc(writer: io.Writer, doc: ^xml.Document, element_id: xml.Element_ID, indent := 0) -> (written: int, err: io.Error) {
-		using fmt
-
 		tab :: proc(writer: io.Writer, indent: int) {
 		tab :: proc(writer: io.Writer, indent: int) {
 			for _ in 0..=indent {
 			for _ in 0..=indent {
-				wprintf(writer, "\t")
+				fmt.wprintf(writer, "\t")
 			}
 			}
 		}
 		}
 
 
@@ -259,22 +256,24 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
 		element := doc.elements[element_id]
 		element := doc.elements[element_id]
 
 
 		if element.kind == .Element {
 		if element.kind == .Element {
-			wprintf(writer, "<%v>\n", element.ident)
-			if len(element.value) > 0 {
-				tab(writer, indent + 1)
-				wprintf(writer, "[Value] %v\n", element.value)
+			fmt.wprintf(writer, "<%v>\n", element.ident)
+
+			for value in element.value {
+				switch v in value {
+				case string:
+					tab(writer, indent + 1)
+					fmt.wprintf(writer, "[Value] %v\n", v)
+				case xml.Element_ID:
+					print_element(writer, doc, v, indent + 1)
+				}
 			}
 			}
 
 
 			for attr in element.attribs {
 			for attr in element.attribs {
 				tab(writer, indent + 1)
 				tab(writer, indent + 1)
-				wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
-			}
-
-			for child in element.children {
-				print_element(writer, doc, child, indent + 1)
+				fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
 			}
 			}
 		} else if element.kind == .Comment {
 		} else if element.kind == .Comment {
-			wprintf(writer, "[COMMENT] %v\n", element.value)
+			fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
 		}
 		}
 
 
 		return written, .None
 		return written, .None
@@ -289,8 +288,6 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
 
 
 @test
 @test
 run_tests :: proc(t: ^testing.T) {
 run_tests :: proc(t: ^testing.T) {
-	using fmt
-
 	for test in TESTS {
 	for test in TESTS {
 		path := test_file_path(test.filename)
 		path := test_file_path(test.filename)
 		log(t, fmt.tprintf("Trying to parse %v", path))
 		log(t, fmt.tprintf("Trying to parse %v", path))
@@ -305,11 +302,11 @@ run_tests :: proc(t: ^testing.T) {
 		crc32 := hash.crc32(tree_bytes)
 		crc32 := hash.crc32(tree_bytes)
 
 
 		failed := err != test.err
 		failed := err != test.err
-		err_msg := tprintf("Expected return value %v, got %v", test.err, err)
+		err_msg := fmt.tprintf("Expected return value %v, got %v", test.err, err)
 		expect(t, err == test.err, err_msg)
 		expect(t, err == test.err, err_msg)
 
 
 		failed |= crc32 != test.crc32
 		failed |= crc32 != test.crc32
-		err_msg  = tprintf("Expected CRC 0x%08x, got 0x%08x, with options %v", test.crc32, crc32, test.options)
+		err_msg  = fmt.tprintf("Expected CRC 0x%08x, got 0x%08x, with options %v", test.crc32, crc32, test.options)
 		expect(t, crc32 == test.crc32, err_msg)
 		expect(t, crc32 == test.crc32, err_msg)
 
 
 		if failed {
 		if failed {
@@ -317,7 +314,7 @@ run_tests :: proc(t: ^testing.T) {
 				Don't fully print big trees.
 				Don't fully print big trees.
 			*/
 			*/
 			tree_string = tree_string[:min(2_048, len(tree_string))]
 			tree_string = tree_string[:min(2_048, len(tree_string))]
-			println(tree_string)
+			fmt.println(tree_string)
 		}
 		}
 	}
 	}
 }
 }

+ 4 - 10
tests/core/math/linalg/glsl/test_linalg_glsl_math.odin

@@ -22,9 +22,6 @@ main :: proc() {
 
 
 @test
 @test
 test_fract_f32 :: proc(t: ^testing.T) {
 test_fract_f32 :: proc(t: ^testing.T) {
-
-	using math
-
 	r: f32
 	r: f32
 
 
 	Datum :: struct {
 	Datum :: struct {
@@ -35,8 +32,8 @@ test_fract_f32 :: proc(t: ^testing.T) {
 	@static data := []Datum{
 	@static data := []Datum{
 		{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
 		{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
 		{ 1, -10.5, -0.5 },
 		{ 1, -10.5, -0.5 },
-		{ 2, F32_MIN, F32_MIN }, // 0x1p-126
-		{ 3, -F32_MIN, -F32_MIN },
+		{ 2, math.F32_MIN, math.F32_MIN }, // 0x1p-126
+		{ 3, -math.F32_MIN, -math.F32_MIN },
 		{ 4, 0.0, 0.0 },
 		{ 4, 0.0, 0.0 },
 		{ 5, -0.0, -0.0 },
 		{ 5, -0.0, -0.0 },
 		{ 6, 1, 0.0 },
 		{ 6, 1, 0.0 },
@@ -54,9 +51,6 @@ test_fract_f32 :: proc(t: ^testing.T) {
 
 
 @test
 @test
 test_fract_f64 :: proc(t: ^testing.T) {
 test_fract_f64 :: proc(t: ^testing.T) {
-
-	using math
-
 	r: f64
 	r: f64
 
 
 	Datum :: struct {
 	Datum :: struct {
@@ -67,8 +61,8 @@ test_fract_f64 :: proc(t: ^testing.T) {
 	@static data := []Datum{
 	@static data := []Datum{
 		{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
 		{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
 		{ 1, -10.5, -0.5 },
 		{ 1, -10.5, -0.5 },
-		{ 2, F64_MIN, F64_MIN }, // 0x1p-1022
-		{ 3, -F64_MIN, -F64_MIN },
+		{ 2, math.F64_MIN, math.F64_MIN }, // 0x1p-1022
+		{ 3, -math.F64_MIN, -math.F64_MIN },
 		{ 4, 0.0, 0.0 },
 		{ 4, 0.0, 0.0 },
 		{ 5, -0.0, -0.0 },
 		{ 5, -0.0, -0.0 },
 		{ 6, 1, 0.0 },
 		{ 6, 1, 0.0 },

+ 73 - 94
tests/core/math/test_core_math.odin

@@ -43,11 +43,7 @@ main :: proc() {
 
 
 @test
 @test
 test_classify_f16 :: proc(t: ^testing.T) {
 test_classify_f16 :: proc(t: ^testing.T) {
-
-	using math
-	using Float_Class
-
-	r: Float_Class
+	r: math.Float_Class
 
 
 	Datum :: struct {
 	Datum :: struct {
 		i: int,
 		i: int,
@@ -55,38 +51,34 @@ test_classify_f16 :: proc(t: ^testing.T) {
 		e: math.Float_Class,
 		e: math.Float_Class,
 	}
 	}
 	@static data := []Datum{
 	@static data := []Datum{
-		{ 0, 1.2, Normal },
-		{ 1, 0h0001, Subnormal },
-		{ 2, 0.0, Zero },
-		{ 3, -0.0, Neg_Zero },
-		{ 4, SNAN_F16, NaN },
-		{ 5, QNAN_F16, NaN },
-		{ 6, INF_F16, Inf },
-		{ 7, NEG_INF_F16, Neg_Inf },
+		{ 0, 1.2, .Normal },
+		{ 1, 0h0001, .Subnormal },
+		{ 2, 0.0, .Zero },
+		{ 3, -0.0, .Neg_Zero },
+		{ 4, math.SNAN_F16, .NaN },
+		{ 5, math.QNAN_F16, .NaN },
+		{ 6, math.INF_F16, .Inf },
+		{ 7, math.NEG_INF_F16, .Neg_Inf },
 	}
 	}
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i)
 		assert(i == d.i)
-		r = classify_f16(d.v)
+		r = math.classify_f16(d.v)
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
 	}
 	}
 
 
 	/* Check all subnormals (exponent 0, 10-bit significand non-zero) */
 	/* Check all subnormals (exponent 0, 10-bit significand non-zero) */
-	for i :u16 = 1; i < 0x400; i += 1 {
-		v :f16 = transmute(f16)i
-		r = classify_f16(v)
-		e :Float_Class: Subnormal
+	for i in u16(1)..<0x400 {
+		v := transmute(f16)i
+		r = math.classify_f16(v)
+		e :: math.Float_Class.Subnormal
 		tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e))
 		tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e))
 	}
 	}
 }
 }
 
 
 @test
 @test
 test_classify_f32 :: proc(t: ^testing.T) {
 test_classify_f32 :: proc(t: ^testing.T) {
-
-	using math
-	using Float_Class
-
-	r: Float_Class
+	r: math.Float_Class
 
 
 	Datum :: struct {
 	Datum :: struct {
 		i: int,
 		i: int,
@@ -94,30 +86,26 @@ test_classify_f32 :: proc(t: ^testing.T) {
 		e: math.Float_Class,
 		e: math.Float_Class,
 	}
 	}
 	@static data := []Datum{
 	@static data := []Datum{
-		{ 0, 1.2, Normal },
-		{ 1, 0h0000_0001, Subnormal },
-		{ 2, 0.0, Zero },
-		{ 3, -0.0, Neg_Zero },
-		{ 4, SNAN_F32, NaN },
-		{ 5, QNAN_F32, NaN },
-		{ 6, INF_F32, Inf },
-		{ 7, NEG_INF_F32, Neg_Inf },
+		{ 0, 1.2, .Normal },
+		{ 1, 0h0000_0001, .Subnormal },
+		{ 2, 0.0, .Zero },
+		{ 3, -0.0, .Neg_Zero },
+		{ 4, math.SNAN_F32, .NaN },
+		{ 5, math.QNAN_F32, .NaN },
+		{ 6, math.INF_F32, .Inf },
+		{ 7, math.NEG_INF_F32, .Neg_Inf },
 	}
 	}
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i)
 		assert(i == d.i)
-		r = classify_f32(d.v)
+		r = math.classify_f32(d.v)
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
 	}
 	}
 }
 }
 
 
 @test
 @test
 test_classify_f64 :: proc(t: ^testing.T) {
 test_classify_f64 :: proc(t: ^testing.T) {
-
-	using math
-	using Float_Class
-
-	r: Float_Class
+	r: math.Float_Class
 
 
 	Datum :: struct {
 	Datum :: struct {
 		i: int,
 		i: int,
@@ -125,28 +113,25 @@ test_classify_f64 :: proc(t: ^testing.T) {
 		e: math.Float_Class,
 		e: math.Float_Class,
 	}
 	}
 	@static data := []Datum{
 	@static data := []Datum{
-		{ 0, 1.2, Normal },
-		{ 1, 0h0000_0000_0000_0001, Subnormal },
-		{ 2, 0.0, Zero },
-		{ 3, -0.0, Neg_Zero },
-		{ 4, SNAN_F64, NaN },
-		{ 5, QNAN_F64, NaN },
-		{ 6, INF_F64, Inf },
-		{ 7, NEG_INF_F64, Neg_Inf },
+		{ 0, 1.2, .Normal },
+		{ 1, 0h0000_0000_0000_0001, .Subnormal },
+		{ 2, 0.0, .Zero },
+		{ 3, -0.0, .Neg_Zero },
+		{ 4, math.SNAN_F64, .NaN },
+		{ 5, math.QNAN_F64, .NaN },
+		{ 6, math.INF_F64, .Inf },
+		{ 7, math.NEG_INF_F64, .Neg_Inf },
 	}
 	}
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i)
 		assert(i == d.i)
-		r = classify_f64(d.v)
+		r = math.classify_f64(d.v)
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
 	}
 	}
 }
 }
 
 
 @test
 @test
 test_trunc_f16 :: proc(t: ^testing.T) {
 test_trunc_f16 :: proc(t: ^testing.T) {
-
-	using math
-
 	r, v: f16
 	r, v: f16
 
 
 	Datum :: struct {
 	Datum :: struct {
@@ -158,16 +143,16 @@ test_trunc_f16 :: proc(t: ^testing.T) {
 		{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
 		{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
 		{ 1, -10.5, -10 },
 		{ 1, -10.5, -10 },
 
 
-		{ 2, F16_MAX, F16_MAX },
-		{ 3, -F16_MAX, -F16_MAX },
-		{ 4, F16_MIN, 0.0 },
-		{ 5, -F16_MIN, -0.0 },
+		{ 2, math.F16_MAX, math.F16_MAX },
+		{ 3, -math.F16_MAX, -math.F16_MAX },
+		{ 4, math.F16_MIN, 0.0 },
+		{ 5, -math.F16_MIN, -0.0 },
 		{ 6, 0.0, 0.0 },
 		{ 6, 0.0, 0.0 },
 		{ 7, -0.0, -0.0 },
 		{ 7, -0.0, -0.0 },
 		{ 8, 1, 1 },
 		{ 8, 1, 1 },
 		{ 9, -1, -1 },
 		{ 9, -1, -1 },
-		{ 10, INF_F16, INF_F16 },
-		{ 11, NEG_INF_F16, NEG_INF_F16 },
+		{ 10, math.INF_F16, math.INF_F16 },
+		{ 11, math.NEG_INF_F16, math.NEG_INF_F16 },
 
 
 		/* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */
 		/* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */
 		{ 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1)
 		{ 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1)
@@ -185,24 +170,21 @@ test_trunc_f16 :: proc(t: ^testing.T) {
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i)
 		assert(i == d.i)
-		r = trunc_f16(d.v)
+		r = math.trunc_f16(d.v)
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
 	}
 	}
 
 
-	v = SNAN_F16
-	r = trunc_f16(v)
-	tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+	v = math.SNAN_F16
+	r = math.trunc_f16(v)
+	tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 
 
-	v = QNAN_F16
-	r = trunc_f16(v)
-	tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+	v = math.QNAN_F16
+	r = math.trunc_f16(v)
+	tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 }
 }
 
 
 @test
 @test
 test_trunc_f32 :: proc(t: ^testing.T) {
 test_trunc_f32 :: proc(t: ^testing.T) {
-
-	using math
-
 	r, v: f32
 	r, v: f32
 
 
 	Datum :: struct {
 	Datum :: struct {
@@ -214,16 +196,16 @@ test_trunc_f32 :: proc(t: ^testing.T) {
 		{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
 		{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
 		{ 1, -10.5, -10 },
 		{ 1, -10.5, -10 },
 
 
-		{ 2, F32_MAX, F32_MAX },
-		{ 3, -F32_MAX, -F32_MAX },
-		{ 4, F32_MIN, 0.0 },
-		{ 5, -F32_MIN, -0.0 },
+		{ 2, math.F32_MAX, math.F32_MAX },
+		{ 3, -math.F32_MAX, -math.F32_MAX },
+		{ 4, math.F32_MIN, 0.0 },
+		{ 5, -math.F32_MIN, -0.0 },
 		{ 6, 0.0, 0.0 },
 		{ 6, 0.0, 0.0 },
 		{ 7, -0.0, -0.0 },
 		{ 7, -0.0, -0.0 },
 		{ 8, 1, 1 },
 		{ 8, 1, 1 },
 		{ 9, -1, -1 },
 		{ 9, -1, -1 },
-		{ 10, INF_F32, INF_F32 },
-		{ 11, NEG_INF_F32, NEG_INF_F32 },
+		{ 10, math.INF_F32, math.INF_F32 },
+		{ 11, math.NEG_INF_F32, math.NEG_INF_F32 },
 
 
 		/* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */
 		/* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */
 		{ 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1)
 		{ 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1)
@@ -250,24 +232,21 @@ test_trunc_f32 :: proc(t: ^testing.T) {
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i)
 		assert(i == d.i)
-		r = trunc_f32(d.v)
+		r = math.trunc_f32(d.v)
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
 	}
 	}
 
 
-	v = SNAN_F32
-	r = trunc_f32(v)
-	tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+	v = math.SNAN_F32
+	r = math.trunc_f32(v)
+	tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 
 
-	v = QNAN_F32
-	r = trunc_f32(v)
-	tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+	v = math.QNAN_F32
+	r = math.trunc_f32(v)
+	tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 }
 }
 
 
 @test
 @test
 test_trunc_f64 :: proc(t: ^testing.T) {
 test_trunc_f64 :: proc(t: ^testing.T) {
-
-	using math
-
 	r, v: f64
 	r, v: f64
 
 
 	Datum :: struct {
 	Datum :: struct {
@@ -279,16 +258,16 @@ test_trunc_f64 :: proc(t: ^testing.T) {
 		{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
 		{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
 		{ 1, -10.5, -10 },
 		{ 1, -10.5, -10 },
 
 
-		{ 2, F64_MAX, F64_MAX },
-		{ 3, -F64_MAX, -F64_MAX },
-		{ 4, F64_MIN, 0.0 },
-		{ 5, -F64_MIN, -0.0 },
+		{ 2, math.F64_MAX, math.F64_MAX },
+		{ 3, -math.F64_MAX, -math.F64_MAX },
+		{ 4, math.F64_MIN, 0.0 },
+		{ 5, -math.F64_MIN, -0.0 },
 		{ 6, 0.0, 0.0 },
 		{ 6, 0.0, 0.0 },
 		{ 7, -0.0, -0.0 },
 		{ 7, -0.0, -0.0 },
 		{ 8, 1, 1 },
 		{ 8, 1, 1 },
 		{ 9, -1, -1 },
 		{ 9, -1, -1 },
-		{ 10, INF_F64, INF_F64 },
-		{ 11, NEG_INF_F64, NEG_INF_F64 },
+		{ 10, math.INF_F64, math.INF_F64 },
+		{ 11, math.NEG_INF_F64, math.NEG_INF_F64 },
 
 
 		/* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */
 		/* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */
 		{ 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1)
 		{ 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1)
@@ -315,17 +294,17 @@ test_trunc_f64 :: proc(t: ^testing.T) {
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i)
 		assert(i == d.i)
-		r = trunc_f64(d.v)
+		r = math.trunc_f64(d.v)
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
 		tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
 	}
 	}
 
 
-	v = SNAN_F64
-	r = trunc_f64(v)
-	tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+	v = math.SNAN_F64
+	r = math.trunc_f64(v)
+	tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 
 
-	v = QNAN_F64
-	r = trunc_f64(v)
-	tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+	v = math.QNAN_F64
+	r = math.trunc_f64(v)
+	tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 }
 }
 
 
 
 

+ 6 - 12
tests/core/path/filepath/test_core_filepath.odin

@@ -22,9 +22,6 @@ main :: proc() {
 
 
 @test
 @test
 test_split_list_windows :: proc(t: ^testing.T) {
 test_split_list_windows :: proc(t: ^testing.T) {
-
-	using filepath
-
 	Datum :: struct {
 	Datum :: struct {
 		i: int,
 		i: int,
 		v: string,
 		v: string,
@@ -43,7 +40,7 @@ test_split_list_windows :: proc(t: ^testing.T) {
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
 		assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
-		r := split_list(d.v)
+		r := filepath.split_list(d.v)
 		defer delete(r)
 		defer delete(r)
 		tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
 		tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
 													 i, #procedure, d.v, len(r), len(d.e)))
 													 i, #procedure, d.v, len(r), len(d.e)))
@@ -57,12 +54,12 @@ test_split_list_windows :: proc(t: ^testing.T) {
 
 
 	{
 	{
 		v := ""
 		v := ""
-		r := split_list(v)
+		r := filepath.split_list(v)
 		tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
 		tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
 	}
 	}
 	{
 	{
 		v := "a"
 		v := "a"
-		r := split_list(v)
+		r := filepath.split_list(v)
 		defer delete(r)
 		defer delete(r)
 		tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
 		tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
 		if len(r) == 1 {
 		if len(r) == 1 {
@@ -73,9 +70,6 @@ test_split_list_windows :: proc(t: ^testing.T) {
 
 
 @test
 @test
 test_split_list_unix :: proc(t: ^testing.T) {
 test_split_list_unix :: proc(t: ^testing.T) {
-
-	using filepath
-
 	Datum :: struct {
 	Datum :: struct {
 		i: int,
 		i: int,
 		v: string,
 		v: string,
@@ -94,7 +88,7 @@ test_split_list_unix :: proc(t: ^testing.T) {
 
 
 	for d, i in data {
 	for d, i in data {
 		assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
 		assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
-		r := split_list(d.v)
+		r := filepath.split_list(d.v)
 		defer delete(r)
 		defer delete(r)
 		tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
 		tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
 													 i, #procedure, d.v, len(r), len(d.e)))
 													 i, #procedure, d.v, len(r), len(d.e)))
@@ -108,12 +102,12 @@ test_split_list_unix :: proc(t: ^testing.T) {
 
 
 	{
 	{
 		v := ""
 		v := ""
-		r := split_list(v)
+		r := filepath.split_list(v)
 		tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
 		tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
 	}
 	}
 	{
 	{
 		v := "a"
 		v := "a"
-		r := split_list(v)
+		r := filepath.split_list(v)
 		defer delete(r)
 		defer delete(r)
 		tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
 		tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
 		if len(r) == 1 {
 		if len(r) == 1 {

+ 16 - 20
tests/core/reflect/test_core_reflect.odin

@@ -19,8 +19,6 @@ main :: proc() {
 
 
 @test
 @test
 test_as_u64 :: proc(t: ^testing.T) {
 test_as_u64 :: proc(t: ^testing.T) {
-	using reflect
-
 	{
 	{
 		/* i8 */
 		/* i8 */
 		Datum :: struct { i: int, v: i8, e: u64 }
 		Datum :: struct { i: int, v: i8, e: u64 }
@@ -32,7 +30,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n",
 												i, #procedure, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, r, r, d.e, d.e))
@@ -49,7 +47,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n",
 												i, #procedure, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, r, r, d.e, d.e))
@@ -66,7 +64,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n",
 												i, #procedure, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, r, r, d.e, d.e))
@@ -83,7 +81,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n",
 												i, #procedure, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, r, r, d.e, d.e))
@@ -103,7 +101,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n",
 												i, #procedure, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, r, r, d.e, d.e))
@@ -119,7 +117,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -133,7 +131,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -147,7 +145,7 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_u64(d.v)
+			r, valid := reflect.as_u64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -156,8 +154,6 @@ test_as_u64 :: proc(t: ^testing.T) {
 
 
 @test
 @test
 test_as_f64 :: proc(t: ^testing.T) {
 test_as_f64 :: proc(t: ^testing.T) {
-	using reflect
-
 	{
 	{
 		/* i8 */
 		/* i8 */
 		Datum :: struct { i: int, v: i8, e: f64 }
 		Datum :: struct { i: int, v: i8, e: f64 }
@@ -169,7 +165,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -185,7 +181,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -201,7 +197,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -217,7 +213,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}
@@ -234,7 +230,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n",
 												i, #procedure, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, r, r, d.e, d.e))
@@ -250,7 +246,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n",
 												i, #procedure, d.v, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, d.v, r, r, d.e, d.e))
@@ -265,7 +261,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n",
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n",
 												i, #procedure, d.v, d.v, r, r, d.e, d.e))
 												i, #procedure, d.v, d.v, r, r, d.e, d.e))
@@ -280,7 +276,7 @@ test_as_f64 :: proc(t: ^testing.T) {
 
 
 		for d, i in data {
 		for d, i in data {
 			assert(i == d.i)
 			assert(i == d.i)
-			r, valid := as_f64(d.v)
+			r, valid := reflect.as_f64(d.v)
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 			tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
 		}
 		}

+ 2 - 6
tests/core/text/i18n/test_core_text_i18n.odin

@@ -118,8 +118,6 @@ TESTS := []Test_Suite{
 
 
 @test
 @test
 tests :: proc(t: ^testing.T) {
 tests :: proc(t: ^testing.T) {
-	using fmt
-
 	cat: ^i18n.Translation
 	cat: ^i18n.Translation
 	err: i18n.Error
 	err: i18n.Error
 
 
@@ -142,8 +140,6 @@ tests :: proc(t: ^testing.T) {
 }
 }
 
 
 main :: proc() {
 main :: proc() {
-	using fmt
-
 	track: mem.Tracking_Allocator
 	track: mem.Tracking_Allocator
 	mem.tracking_allocator_init(&track, context.allocator)
 	mem.tracking_allocator_init(&track, context.allocator)
 	context.allocator = mem.tracking_allocator(&track)
 	context.allocator = mem.tracking_allocator(&track)
@@ -157,9 +153,9 @@ main :: proc() {
 	}
 	}
 
 
 	if len(track.allocation_map) > 0 {
 	if len(track.allocation_map) > 0 {
-		println()
+		fmt.println()
 		for _, v in track.allocation_map {
 		for _, v in track.allocation_map {
-			printf("%v Leaked %v bytes.\n", v.location, v.size)
+			fmt.printf("%v Leaked %v bytes.\n", v.location, v.size)
 		}
 		}
 	}
 	}
 }
 }

+ 1 - 0
tests/issues/run.bat

@@ -15,6 +15,7 @@ set COMMON=-collection:tests=..\..
 ..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b
 ..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b
 ..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b
 ..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b
 ..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b
 ..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b
+..\..\..\odin test ..\test_issue_2666.odin %COMMON% -file || exit /b
 
 
 @echo off
 @echo off
 
 

+ 1 - 0
tests/issues/run.sh

@@ -18,6 +18,7 @@ $ODIN build ../test_issue_2113.odin $COMMON -file -debug
 $ODIN test ../test_issue_2466.odin $COMMON -file
 $ODIN test ../test_issue_2466.odin $COMMON -file
 $ODIN test ../test_issue_2615.odin $COMMON -file
 $ODIN test ../test_issue_2615.odin $COMMON -file
 $ODIN test ../test_issue_2637.odin $COMMON -file
 $ODIN test ../test_issue_2637.odin $COMMON -file
+$ODIN test ../test_issue_2666.odin $COMMON -file
 if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
 if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
 	echo "SUCCESSFUL 1/1"
 	echo "SUCCESSFUL 1/1"
 else
 else

+ 26 - 0
tests/issues/test_issue_2666.odin

@@ -0,0 +1,26 @@
+// Tests issue https://github.com/odin-lang/Odin/issues/2666
+// @(disabled=<boolean>) does not work with polymorphic procs
+package test_issues
+
+import "core:testing"
+
+@(test)
+test_disabled_parapoly :: proc(t: ^testing.T) {
+	disabled_parapoly(t, 1)
+	disabled_parapoly_constant(t, 1)
+}
+
+@(private="file")
+@(disabled = true)
+disabled_parapoly :: proc(t: ^testing.T, num: $T) {
+	testing.error(t, "disabled_parapoly should be disabled")
+}
+
+@(private="file")
+DISABLE :: true
+
+@(disabled = DISABLE)
+@(private = "file")
+disabled_parapoly_constant :: proc(t: ^testing.T, num: $T) {
+	testing.error(t, "disabled_parapoly_constant should be disabled")
+}

+ 2 - 2
vendor/OpenGL/helpers.odin

@@ -5,7 +5,9 @@ package vendor_gl
 import "core:os"
 import "core:os"
 import "core:fmt"
 import "core:fmt"
 import "core:strings"
 import "core:strings"
+import "core:runtime"
 _ :: fmt
 _ :: fmt
+_ :: runtime
 
 
 Shader_Type :: enum i32 {
 Shader_Type :: enum i32 {
 	NONE = 0x0000,
 	NONE = 0x0000,
@@ -47,8 +49,6 @@ get_last_error_message :: proc() -> (compile_message: string, compile_type: Shad
 // except for calling differently named GL functions
 // except for calling differently named GL functions
 // it's a bit ugly looking, but meh
 // it's a bit ugly looking, but meh
 when GL_DEBUG {
 when GL_DEBUG {
-	import "core:runtime"
-	
 	@private
 	@private
 	check_error :: proc(
 	check_error :: proc(
 		id: u32, type: Shader_Type, status: u32,
 		id: u32, type: Shader_Type, status: u32,

+ 5 - 3
vendor/OpenGL/wrappers.odin

@@ -2,6 +2,11 @@ package vendor_gl
 
 
 #assert(size_of(bool) == size_of(u8))
 #assert(size_of(bool) == size_of(u8))
 
 
+import "core:runtime"
+import "core:fmt"
+_ :: runtime
+_ :: fmt
+
 when !GL_DEBUG {
 when !GL_DEBUG {
 	// VERSION_1_0
 	// VERSION_1_0
 	CullFace               :: proc "c" (mode: u32)                                                                                         {        impl_CullFace(mode)                                                                         }
 	CullFace               :: proc "c" (mode: u32)                                                                                         {        impl_CullFace(mode)                                                                         }
@@ -756,9 +761,6 @@ when !GL_DEBUG {
 	MultiDrawElementsIndirectCount :: proc "c" (mode: i32, type: i32, indirect: [^]DrawElementsIndirectCommand, drawcount: i32, maxdrawcount, stride: i32)      { impl_MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride)             }
 	MultiDrawElementsIndirectCount :: proc "c" (mode: i32, type: i32, indirect: [^]DrawElementsIndirectCommand, drawcount: i32, maxdrawcount, stride: i32)      { impl_MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride)             }
 	PolygonOffsetClamp             :: proc "c" (factor, units, clamp: f32)                                                                                      { impl_PolygonOffsetClamp(factor, units, clamp)                                                          }
 	PolygonOffsetClamp             :: proc "c" (factor, units, clamp: f32)                                                                                      { impl_PolygonOffsetClamp(factor, units, clamp)                                                          }
 } else {
 } else {
-	import "core:runtime"
-	import "core:fmt"
-
 	debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, args: ..any, loc := #caller_location) {
 	debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, args: ..any, loc := #caller_location) {
 		context = runtime.default_context()
 		context = runtime.default_context()
 
 

+ 2 - 3
vendor/darwin/Foundation/NSNumber.odin

@@ -1,9 +1,8 @@
 package objc_Foundation
 package objc_Foundation
 
 
+import "core:c"
+_ :: c
 when ODIN_OS == .Darwin {
 when ODIN_OS == .Darwin {
-	import "core:c"
-	_ :: c
-
 	#assert(size_of(c.long)  == size_of(int))
 	#assert(size_of(c.long)  == size_of(int))
 	#assert(size_of(c.ulong) == size_of(uint))
 	#assert(size_of(c.ulong) == size_of(uint))
 }
 }

+ 14 - 0
vendor/directx/d3d11/d3d11.odin

@@ -3551,6 +3551,8 @@ PARAMETER_DESC :: struct {
 	FirstOutComponent: u32,
 	FirstOutComponent: u32,
 }
 }
 
 
+ID3D11ShaderReflectionType_UUID_STRING :: "6E6FFA6A-9BAE-4613-A51E-91652D508C21"
+ID3D11ShaderReflectionType_UUID := &IID{0x6E6FFA6A, 0x9BAE, 0x4613, {0xA5, 0x1E, 0x91, 0x65, 0x2D, 0x50, 0x8C, 0x21}}
 IShaderReflectionType :: struct {
 IShaderReflectionType :: struct {
 	using vtable: ^IShaderReflectionType_VTable,
 	using vtable: ^IShaderReflectionType_VTable,
 }
 }
@@ -3568,6 +3570,8 @@ IShaderReflectionType_VTable :: struct {
 	ImplementsInterface:  proc "stdcall" (this: ^IShaderReflectionType, pBase: ^IShaderReflectionType) -> HRESULT,
 	ImplementsInterface:  proc "stdcall" (this: ^IShaderReflectionType, pBase: ^IShaderReflectionType) -> HRESULT,
 }
 }
 
 
+ID3D11ShaderReflectionVariable_UUID_STRING :: "51F23923-F3E5-4BD1-91CB-606177D8DB4C"
+ID3D11ShaderReflectionVariable_UUID := &IID{0x51F23923, 0xF3E5, 0x4BD1, {0x91, 0xCB, 0x60, 0x61, 0x77, 0xD8, 0xDB, 0x4C}}
 IShaderReflectionVariable :: struct {
 IShaderReflectionVariable :: struct {
 	using vtable: ^IShaderReflectionVariable_VTable,
 	using vtable: ^IShaderReflectionVariable_VTable,
 }
 }
@@ -3578,6 +3582,8 @@ IShaderReflectionVariable_VTable :: struct {
 	GetInterfaceSlot: proc "stdcall" (this: ^IShaderReflectionVariable, uArrayIndex: u32) -> u32,
 	GetInterfaceSlot: proc "stdcall" (this: ^IShaderReflectionVariable, uArrayIndex: u32) -> u32,
 }
 }
 
 
+ID3D11ShaderReflectionConstantBuffer_UUID_STRING :: "EB62D63D-93DD-4318-8AE8-C6F83AD371B8"
+ID3D11ShaderReflectionConstantBuffer_UUID := &IID{0xEB62D63D, 0x93DD, 0x4318, {0x8A, 0xE8, 0xC6, 0xF8, 0x3A, 0xD3, 0x71, 0xB8}}
 IShaderReflectionConstantBuffer :: struct {
 IShaderReflectionConstantBuffer :: struct {
 	using vtable: ^IShaderReflectionConstantBuffer_VTable,
 	using vtable: ^IShaderReflectionConstantBuffer_VTable,
 }
 }
@@ -3588,6 +3594,8 @@ IShaderReflectionConstantBuffer_VTable :: struct {
 }
 }
 
 
 
 
+ID3D11ShaderReflection_UUID_STRING :: "8D536CA1-0CCA-4956-A837-786963755584"
+ID3D11ShaderReflection_UUID := &IID{0x8D536CA1, 0x0CCA, 0x4956, {0xA8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84}}
 IShaderReflection :: struct #raw_union {
 IShaderReflection :: struct #raw_union {
 	#subtype iunknown: IUnknown,
 	#subtype iunknown: IUnknown,
 	using id3d11shaderreflection_vtable: ^IShaderReflection_VTable,
 	using id3d11shaderreflection_vtable: ^IShaderReflection_VTable,
@@ -3616,6 +3624,8 @@ IShaderReflection_VTable :: struct {
 }
 }
 
 
 
 
+ID3D11LibraryReflection_UUID_STRING :: "54384F1B-5B3E-4BB7-AE01-60BA3097CBB6"
+ID3D11LibraryReflection_UUID := &IID{0x54384F1B, 0x5B3E, 0x4BB7, {0xAE, 0x1, 0x60, 0xBA, 0x30, 0x97, 0xCD, 0xB6}}
 ILibraryReflection :: struct #raw_union {
 ILibraryReflection :: struct #raw_union {
 	#subtype iunknown: IUnknown,
 	#subtype iunknown: IUnknown,
 	using id3d11libraryreflection_vtable: ^ILibraryReflection_VTable,
 	using id3d11libraryreflection_vtable: ^ILibraryReflection_VTable,
@@ -3626,6 +3636,8 @@ ILibraryReflection_VTable :: struct {
 	GetFunctionByIndex: proc "stdcall" (this: ^ILibraryReflection, FunctionIndex: i32) -> ^IFunctionReflection,
 	GetFunctionByIndex: proc "stdcall" (this: ^ILibraryReflection, FunctionIndex: i32) -> ^IFunctionReflection,
 }
 }
 
 
+ID3D11FunctionReflection_UUID_STRING :: "207BCECB-D683-4A06-A8A3-9B149B9F73A4"
+ID3D11FunctionReflection_UUID := &IID{0x207BCECB, 0xD683, 0x4A06, {0xA8, 0xA3, 0x9B, 0x14, 0x9B, 0x9F, 0x73, 0xA4}}
 IFunctionReflection :: struct {
 IFunctionReflection :: struct {
 	using vtable: ^IFunctionReflection_VTable,
 	using vtable: ^IFunctionReflection_VTable,
 }
 }
@@ -3639,6 +3651,8 @@ IFunctionReflection_VTable :: struct {
 	GetFunctionParameter:         proc "stdcall" (this: ^IFunctionReflection, ParameterIndex: i32) -> ^IFunctionParameterReflection,
 	GetFunctionParameter:         proc "stdcall" (this: ^IFunctionReflection, ParameterIndex: i32) -> ^IFunctionParameterReflection,
 }
 }
 
 
+ID3D11FunctionParameterReflection_UUID_STRING :: "42757488-334F-47FE-982E-1A65D08CC462"
+ID3D11FunctionParameterReflection_UUID := &IID{0x42757488, 0x334f, 0x47FE, {0x98, 0x2E, 0x1A, 0x65, 0xD0, 0x8C, 0xC4, 0x62}}
 IFunctionParameterReflection :: struct {
 IFunctionParameterReflection :: struct {
 	using vtable: ^IFunctionParameterReflection_VTable,
 	using vtable: ^IFunctionParameterReflection_VTable,
 }
 }

+ 3 - 0
vendor/directx/d3d_compiler/d3d_compiler.odin

@@ -149,6 +149,9 @@ ID3DInclude_VTable :: struct {
 	Close: proc "stdcall" (this: ^ID3DInclude, pData: rawptr) -> HRESULT,
 	Close: proc "stdcall" (this: ^ID3DInclude, pData: rawptr) -> HRESULT,
 }
 }
 
 
+// Default file includer
+D3DCOMPILE_STANDARD_FILE_INCLUDE :: (^ID3DInclude)(uintptr(1))
+
 
 
 ID3D11Module :: struct #raw_union {
 ID3D11Module :: struct #raw_union {
 	#subtype iunknown: IUnknown,
 	#subtype iunknown: IUnknown,

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