Browse Source

enum_to_string

Ginger Bill 9 years ago
parent
commit
9561dc33ce
17 changed files with 661 additions and 215 deletions
  1. 2 3
      build.bat
  2. 187 14
      code/demo.odin
  3. 6 6
      code/punity.odin
  4. 1 1
      code/test.odin
  5. 60 38
      core/fmt.odin
  6. 5 4
      core/os.odin
  7. 29 8
      core/runtime.odin
  8. 25 7
      src/checker/checker.cpp
  9. 64 10
      src/checker/expr.cpp
  10. 32 12
      src/checker/stmt.cpp
  11. 8 10
      src/checker/type.cpp
  12. 116 21
      src/codegen/codegen.cpp
  13. 18 28
      src/codegen/print_llvm.cpp
  14. 48 27
      src/codegen/ssa.cpp
  15. 3 3
      src/exact_value.cpp
  16. 5 4
      src/main.cpp
  17. 52 19
      src/parser.cpp

+ 2 - 3
build.bat

@@ -6,7 +6,7 @@ set exe_name=odin.exe
 :: Debug = 0, Release = 1
 set release_mode=0
 
-set compiler_flags= -nologo -Oi -TP -W4 -fp:fast -fp:except- -Gm- -MP -FC -Z7 -GS- -EHsc- -GR-
+set compiler_flags= -nologo -Oi -TP -W4 -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
 
 if %release_mode% EQU 0 ( rem Debug
 	set compiler_flags=%compiler_flags% -Od -MDd -Z7
@@ -28,12 +28,11 @@ set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib
 
 set linker_flags= -incremental:no -opt:ref -subsystem:console
 
-
 if %release_mode% EQU 0 ( rem Debug
 	set linker_flags=%linker_flags% -debug
 	set libs=%libs% src\utf8proc\utf8proc_debug.lib
 ) else ( rem Release
-	set linker_flags=%linker_flags%
+	set linker_flags=%linker_flags% -debug
 	set libs=%libs% src\utf8proc\utf8proc.lib
 )
 

+ 187 - 14
code/demo.odin

@@ -1,19 +1,192 @@
-#import "punity.odin"   as pn
-#import "test.odin"     as t1
-#import "sub/test.odin" as t2
+#import "fmt.odin" as fmt
 
-main :: proc() {
-	t1.thing()
-	t2.thing()
+// #foreign_system_library "Ws2_32"
+
+// WSADESCRIPTION_LEN :: 256
+// WSASYS_STATUS_LEN  :: 128
+// WSADATA :: struct #ordered {
+// 	version:       i16
+// 	high_version:  i16
+
+
+// 	max_sockets:   u16
+// 	max_udp_dg:    u16
+// 	vendor_info:   ^byte
+// 	description:   [WSADESCRIPTION_LEN+1]byte
+// 	system_status: [WSASYS_STATUS_LEN+1]byte
+// }
+
+// addrinfo :: struct #ordered {
+// 	flags:     i32
+// 	family:    i32
+// 	socktype:  i32
+// 	protocol:  i32
+// 	addrlen:   uint
+// 	canonname: ^u8
+// 	addr:      ^sockaddr
+// 	next:      ^addrinfo
+// }
+
+// sockaddr :: struct #ordered {
+// 	family: u16
+// 	data:   [14]byte
+// }
+
+
+// SOCKET :: type uint
+// INVALID_SOCKET :: ~(0 as SOCKET)
+
+// AF_UNSPEC      :: 0       // unspecified
+// AF_UNIX        :: 1       // local to host (pipes, portals)
+// AF_INET        :: 2       // internetwork: UDP, TCP, etc.
+// AF_IMPLINK     :: 3       // arpanet imp addresses
+// AF_PUP         :: 4       // pup protocols: e.g. BSP
+// AF_CHAOS       :: 5       // mit CHAOS protocols
+// AF_NS          :: 6       // XEROX NS protocols
+// AF_ISO         :: 7       // ISO protocols
+// AF_OSI         :: AF_ISO  // OSI is ISO
+// AF_ECMA        :: 8       // european computer manufacturers
+// AF_DATAKIT     :: 9       // datakit protocols
+// AF_CCITT       :: 10      // CCITT protocols, X.25 etc
+// AF_SNA         :: 11      // IBM SNA
+// AF_DECnet      :: 12      // DECnet
+// AF_DLI         :: 13      // Direct data link interface
+// AF_LAT         :: 14      // LAT
+// AF_HYLINK      :: 15      // NSC Hyperchannel
+// AF_APPLETALK   :: 16      // AppleTalk
+// AF_ROUTE       :: 17      // Internal Routing Protocol
+// AF_LINK        :: 18      // Link layer interface
+// pseudo_AF_XTP  :: 19      // eXpress Transfer Protocol (no AF)
+// AF_COIP        :: 20      // connection-oriented IP, aka ST II
+// AF_CNT         :: 21      // Computer Network Technology
+// pseudo_AF_RTIP :: 22      // Help Identify RTIP packets
+// AF_IPX         :: 23      // Novell Internet Protocol
+// AF_SIP         :: 24      // Simple Internet Protocol
+// pseudo_AF_PIP  :: 25      // Help Identify PIP packets
+// AF_MAX         :: 26
+
+// SOCK_STREAM  :: 1
+// SOCKET_ERROR :: -1
+// IPPROTO_TCP  :: 6
+// AI_PASSIVE   :: 0x0020
+// SOMAXCONN    :: 128
+
+// SD_RECEIVE :: 0
+// SD_SEND    :: 1
+// SD_BOTH    :: 2
+
+// WSAStartup      :: proc(version_requested: i16, data: ^WSADATA) -> i32                             #foreign #dll_import
+// WSACleanup      :: proc() -> i32                                                                   #foreign #dll_import
+// getaddrinfo     :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign #dll_import
+// freeaddrinfo    :: proc(ai: ^addrinfo)                                                             #foreign #dll_import
+// socket          :: proc(af, type_, protocol: i32) -> SOCKET                                        #foreign #dll_import
+// closesocket     :: proc(s: SOCKET) -> i32                                                          #foreign #dll_import
+// bind            :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32                          #foreign #dll_import
+// listen          :: proc(s: SOCKET, back_log: i32) -> i32                                           #foreign #dll_import
+// accept          :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET                       #foreign #dll_import
+// recv            :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32                        #foreign #dll_import
+// send            :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32                        #foreign #dll_import
+// shutdown        :: proc(s: SOCKET, how: i32) -> i32                                                #foreign #dll_import
+// WSAGetLastError :: proc() -> i32                                                                   #foreign #dll_import
+
+// to_c_string :: proc(s: string) -> ^byte {
+// 	c_str := new_slice(byte, s.count+1)
+// 	assert(c_str.data != null)
+// 	copy(c_str, s as []byte)
+// 	c_str[s.count] = 0
+// 	return c_str.data
+// }
 
-	// init :: proc(c: ^pn.Core) {
-	// }
+// main :: proc() {
+// 	wsa: WSADATA
+// 	res:  ^addrinfo = null
+// 	hints: addrinfo
+// 	s, client: SOCKET
+
+// 	if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
+// 		fmt.println("WSAStartup failed: ", WSAGetLastError())
+// 		return
+// 	}
+// 	defer WSACleanup()
+
+// 	hints.family   = AF_INET
+// 	hints.socktype = SOCK_STREAM
+// 	hints.protocol = IPPROTO_TCP
+// 	hints.flags    = AI_PASSIVE
+
+// 	if getaddrinfo(null, to_c_string("8080"), ^hints, ^res) != 0 {
+// 		fmt.println("getaddrinfo failed: ", WSAGetLastError())
+// 		return
+// 	}
+// 	defer freeaddrinfo(res)
+
+// 	s = socket(res.family, res.socktype, res.protocol)
+// 	if s == INVALID_SOCKET {
+// 		fmt.println("socket failed: ", WSAGetLastError())
+// 		return
+// 	}
+// 	defer closesocket(s)
+
+// 	bind(s, res.addr, res.addrlen as i32)
+// 	listen(s, SOMAXCONN)
+
+// 	client = accept(s, null, null)
+// 	if client == INVALID_SOCKET {
+// 		fmt.println("socket failed: ", WSAGetLastError())
+// 		return
+// 	}
+// 	defer closesocket(client)
+
+// 	html :=
+// `HTTP/1.1 200 OK
+// Connection: close
+// Content-type: text/html
+
+// <html>
+// <head>
+// 	<title>Demo Title</title>
+// </head>
+// <body>
+// 	<h1 style="color: red;">Odin Server Demo</h1>
+// </body>
+// </html>
+// `
+
+// 	buf: [1024]byte
+// 	for {
+// 		bytes := recv(client, ^buf[0], buf.count as i32, 0)
+// 		if bytes > 0 {
+// 			// fmt.println(buf[:bytes] as string)
+// 			bytes_sent := send(client, html.data, (html.count-1) as i32, 0)
+// 			if bytes_sent == SOCKET_ERROR {
+// 				fmt.println("send failed: ", WSAGetLastError())
+// 				return
+// 			}
+// 			break
+// 		} else if bytes == 0 {
+// 			fmt.println("Connection closing...")
+// 			break
+// 		} else {
+// 			fmt.println("recv failed: ", WSAGetLastError())
+// 			return
+// 		}
+// 	}
+
+// 	shutdown(client, SD_SEND)
+// }
+
+main :: proc() {
+	Fruit :: enum {
+		APPLE,
+		BANANA,
+		GRAPE,
+		MELON,
+	}
 
-	// step :: proc(c: ^pn.Core) {
-	// 	if pn.key_down(pn.Key.ESCAPE) {
-	// 		c.running = false
-	// 	}
-	// }
+	s1 := enum_to_string(Fruit.APPLE)
+	e := Fruit.APPLE
+	s2 := enum_to_string(e)
 
-	// pn.run(init, step)
+	fmt.println(s1)
+	fmt.println(s2)
 }

+ 6 - 6
code/punity.odin

@@ -1,5 +1,6 @@
 #import "win32.odin" as win32
-#import "fmt.odin" as fmt
+#import "fmt.odin"   as fmt
+#import "os.odin"    as os
 
 CANVAS_WIDTH  :: 128
 CANVAS_HEIGHT :: 128
@@ -357,7 +358,7 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
 	}
 
 	if RegisterClassExA(^window_class) == 0 {
-		fmt.println_err("RegisterClassExA failed")
+		fmt.fprintln(os.stderr, "RegisterClassExA failed")
 		return
 	}
 
@@ -377,8 +378,7 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
 
 	win32_window := CreateWindowExA(0,
 	                                window_class.class_name,
-	                                WINDOW_TITLE.data,
-	                                // wt.data,
+	                                wt.data,
 	                                style,
 	                                rc.left, rc.top,
 	                                rc.right-rc.left, rc.bottom-rc.top,
@@ -386,7 +386,7 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
 	                                null);
 
 	if win32_window == null {
-		fmt.println_err("CreateWindowExA failed")
+		fmt.fprintln(os.stderr, "CreateWindowExA failed")
 		return
 	}
 
@@ -434,7 +434,7 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
 		{
 			data: [128]byte
 			buf := data[:0]
-			fmt.print_to_buffer(^buf, "Punity: % ms\x00", dt*1000)
+			fmt.printf_to_buffer(^buf, "Punity: % ms\x00", dt*1000)
 			win32.SetWindowTextA(win32_window, buf.data)
 		}
 

+ 1 - 1
code/test.odin

@@ -1,5 +1,5 @@
 #import "fmt.odin" as fmt
 
-thing :: proc() {
+thing :: proc() #link_name "frankerooney" {
 	fmt.println("Hello!")
 }

+ 60 - 38
core/fmt.odin

@@ -1,5 +1,60 @@
 #import "os.odin" as os
 
+PRINT_BUF_SIZE :: 1<<12
+
+fprint :: proc(f: ^os.File, args: ..any) {
+	data: [PRINT_BUF_SIZE]byte
+	buf := data[:0]
+	prev_string := false
+
+	for i := 0; i < args.count; i++ {
+		arg := args[i]
+		is_string := arg.data != null && type_info_is_string(arg.type_info)
+		if i > 0 && is_string && !prev_string {
+			print_space_to_buffer(^buf)
+		}
+		print_any_to_buffer(^buf, arg)
+		prev_string = is_string;
+	}
+
+	os.write(f, buf)
+}
+
+fprintln :: proc(f: ^os.File, args: ..any) {
+	data: [PRINT_BUF_SIZE]byte
+	buf := data[:0]
+
+	for i := 0; i < args.count; i++ {
+		if i > 0 {
+			append(^buf, #rune " ")
+		}
+		print_any_to_buffer(^buf, args[i])
+	}
+
+	print_nl_to_buffer(^buf)
+	os.write(f, buf)
+}
+fprintf :: proc(f: ^os.File, fmt: string, args: ..any) {
+	data: [PRINT_BUF_SIZE]byte
+	buf := data[:0]
+	printf_to_buffer(^buf, fmt, ..args)
+	os.write(f, buf)
+}
+
+
+print :: proc(args: ..any) {
+	fprint(os.stdout, ..args)
+}
+println :: proc(args: ..any) {
+	fprintln(os.stdout, ..args)
+}
+printf :: proc(fmt: string, args: ..any) {
+	fprintf(os.stdout, fmt, ..args)
+}
+
+
+
+
 print_byte_buffer :: proc(buf: ^[]byte, b: []byte) {
 	if buf.count < buf.capacity {
 		n := min(buf.capacity-buf.count, b.count)
@@ -68,7 +123,7 @@ print_nl_to_buffer    :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune "\
 print_int_to_buffer :: proc(buf: ^[]byte, i: int) {
 	print_int_base_to_buffer(buf, i, 10);
 }
-PRINT__NUM_TO_CHAR_TABLE :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"
+__NUM_TO_CHAR_TABLE :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"
 print_int_base_to_buffer :: proc(buffer: ^[]byte, i, base: int) {
 
 	buf: [65]byte
@@ -83,7 +138,7 @@ print_int_base_to_buffer :: proc(buffer: ^[]byte, i, base: int) {
 		len++
 	}
 	for i > 0 {
-		buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base]
+		buf[len] = __NUM_TO_CHAR_TABLE[i % base]
 		len++
 		i /= base
 	}
@@ -108,7 +163,7 @@ print_uint_base_to_buffer :: proc(buffer: ^[]byte, i, base: uint, min_width: int
 		len++
 	}
 	for i > 0 {
-		buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base]
+		buf[len] = __NUM_TO_CHAR_TABLE[i % base]
 		len++
 		i /= base
 	}
@@ -149,7 +204,7 @@ print__f64 :: proc(buffer: ^[]byte, f: f64, decimal_places: int) {
 			len++
 		}
 		for i > 0 {
-			buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % 10]
+			buf[len] = __NUM_TO_CHAR_TABLE[i % 10]
 			len++
 			i /= 10
 		}
@@ -334,7 +389,6 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any)  {
 				case 2:  i = (arg.data as ^i16)^  as int
 				case 4:  i = (arg.data as ^i32)^  as int
 				case 8:  i = (arg.data as ^i64)^  as int
-				case 16: i = (arg.data as ^i128)^ as int
 				}
 			}
 			print_int_to_buffer(buf, i)
@@ -346,7 +400,6 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any)  {
 				case 2:  i = (arg.data as ^u16)^  as uint
 				case 4:  i = (arg.data as ^u32)^  as uint
 				case 8:  i = (arg.data as ^u64)^  as uint
-				case 16: i = (arg.data as ^u128)^ as uint
 				}
 			}
 			print_uint_to_buffer(buf, i)
@@ -488,7 +541,7 @@ type_info_is_string :: proc(info: ^Type_Info) -> bool {
 }
 
 
-print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) {
+printf_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) {
 	is_digit :: proc(r: rune) -> bool #inline {
 		return r >= #rune "0" && r <= #rune "9"
 	}
@@ -550,34 +603,3 @@ print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) {
 
 	print_string_to_buffer(buf, fmt[prev:])
 }
-
-PRINT_BUF_SIZE :: 1<<12
-
-print_to_file :: proc(f: ^os.File, fmt: string, args: ..any) {
-	data: [PRINT_BUF_SIZE]byte
-	buf := data[:0]
-	print_to_buffer(^buf, fmt, ..args)
-	os.write(f, buf)
-}
-
-println_to_file :: proc(f: ^os.File, fmt: string, args: ..any) {
-	data: [PRINT_BUF_SIZE]byte
-	buf := data[:0]
-	print_to_buffer(^buf, fmt, ..args)
-	print_nl_to_buffer(^buf)
-	os.write(f, buf)
-}
-
-
-print :: proc(fmt: string, args: ..any) {
-	print_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args)
-}
-print_err :: proc(fmt: string, args: ..any) {
-	print_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args)
-}
-println :: proc(fmt: string, args: ..any) {
-	println_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args)
-}
-println_err :: proc(fmt: string, args: ..any) {
-	println_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args)
-}

+ 5 - 4
core/os.odin

@@ -46,6 +46,11 @@ File_Standard :: type enum {
 
 __std_files := __set_file_standards();
 
+stdin  := ^__std_files[File_Standard.INPUT]
+stdout := ^__std_files[File_Standard.OUTPUT]
+stderr := ^__std_files[File_Standard.ERROR]
+
+
 __set_file_standards :: proc() -> [File_Standard.COUNT as int]File {
 	return [File_Standard.COUNT as int]File{
 		File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
@@ -54,10 +59,6 @@ __set_file_standards :: proc() -> [File_Standard.COUNT as int]File {
 	}
 }
 
-get_standard_file :: proc(std: File_Standard) -> ^File {
-	return ^__std_files[std]
-}
-
 
 read_entire_file :: proc(name: string) -> (string, bool) {
 	buf: [300]byte

+ 29 - 8
core/runtime.odin

@@ -17,7 +17,6 @@ Type_Info :: union {
 		ordered: bool
 	}
 
-
 	Named: struct #ordered {
 		name: string
 		base: ^Type_Info
@@ -59,6 +58,8 @@ Type_Info :: union {
 	Raw_Union: Record
 	Enum: struct #ordered {
 		base: ^Type_Info
+		values: []i64
+		names:  []string
 	}
 }
 
@@ -170,7 +171,7 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >
 
 
 __assert :: proc(msg: string) {
-	fmt.print_err("%", msg)
+	fmt.fprintln(os.stderr, msg)
 	__debug_trap()
 }
 
@@ -179,8 +180,8 @@ __bounds_check_error :: proc(file: string, line, column: int,
 	if 0 <= index && index < count {
 		return
 	}
-	fmt.println_err("%(%:%) Index % is out of bounds range [0, %)",
-	                file, line, column, index, count)
+	fmt.fprintf(os.stderr, "%(%:%) Index % is out of bounds range [0, %)\n",
+	            file, line, column, index, count)
 	__debug_trap()
 }
 
@@ -189,8 +190,8 @@ __slice_expr_error :: proc(file: string, line, column: int,
 	if 0 <= low && low <= high && high <= max {
 		return
 	}
-	fmt.println_err("%(%:%) Invalid slice indices: [%:%:%]",
-	                file, line, column, low, high, max)
+	fmt.fprintf(os.stderr, "%(%:%) Invalid slice indices: [%:%:%]\n",
+	            file, line, column, low, high, max)
 	__debug_trap()
 }
 __substring_expr_error :: proc(file: string, line, column: int,
@@ -198,8 +199,8 @@ __substring_expr_error :: proc(file: string, line, column: int,
 	if 0 <= low && low <= high {
 		return
 	}
-	fmt.println_err("%(%:%) Invalid substring indices: [%:%:%]",
-	                file, line, column, low, high)
+	fmt.fprintf(os.stderr, "%(%:%) Invalid substring indices: [%:%:%]\n",
+	            file, line, column, low, high)
 	__debug_trap()
 }
 
@@ -341,5 +342,25 @@ __default_allocator :: proc() -> Allocator {
 }
 
 
+__enum_to_string :: proc(info: ^Type_Info, value: i64) -> string {
+	for {
+		match type i : info {
+		case Type_Info.Named:
+			info = i.base
+			continue
+		}
+		break
+	}
 
+	match type ti : info {
+	case Type_Info.Enum:
+		fmt.println("Here: ", ti.values.count)
+		for i := 0; i < ti.values.count; i++ {
+			if ti.values[i] == value {
+				return ti.names[i]
+			}
+		}
+	}
+	return ""
+}
 

+ 25 - 7
src/checker/checker.cpp

@@ -159,6 +159,8 @@ enum BuiltinProcId {
 	BuiltinProc_max,
 	BuiltinProc_abs,
 
+	BuiltinProc_enum_to_string,
+
 
 	BuiltinProc_Count,
 };
@@ -200,6 +202,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("max"),              2, false, Expr_Expr},
 	{STR_LIT("abs"),              1, false, Expr_Expr},
 
+	{STR_LIT("enum_to_string"),   1, false, Expr_Expr},
+
 };
 
 struct CheckerContext {
@@ -358,6 +362,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
 						// Do not return imported entities even #load ones
 						continue;
 					}
+					if (!is_entity_exported(e)) {
+						continue;
+					}
 					if (entity_) *entity_ = e;
 					if (scope_) *scope_ = shared;
 					return;
@@ -409,7 +416,7 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) {
 
 void check_scope_usage(Checker *c, Scope *scope) {
 	// TODO(bill): Use this?
-#if 0
+#if 1
 	gb_for_array(i, scope->elements.entries) {
 		auto *entry = scope->elements.entries + i;
 		Entity *e = entry->value;
@@ -445,7 +452,7 @@ void add_global_entity(Entity *entity) {
 		return; // NOTE(bill): `untyped thing`
 	}
 	if (scope_insert_entity(universal_scope, entity)) {
-		GB_PANIC("Compiler error: double declaration");
+		compiler_error("double declaration");
 	}
 }
 
@@ -492,10 +499,6 @@ void init_universal_scope(void) {
 		entity->Builtin.id = id;
 		add_global_entity(entity);
 	}
-
-// Custom Runtime Types
-	{
-	}
 }
 
 
@@ -1112,12 +1115,27 @@ void check_parsed_files(Checker *c) {
 		ExpressionInfo *info = &entry->value;
 		if (info != NULL && expr != NULL) {
 			if (is_type_typed(info->type)) {
-				GB_PANIC("%s (type %s) is typed!", expr_to_string(expr), info->type);
+				compiler_error("%s (type %s) is typed!", expr_to_string(expr), info->type);
 			}
 			add_type_and_value(&c->info, expr, info->mode, info->type, info->value);
 		}
 	}
 #endif
+
+	gb_for_array(i, c->parser->files) {
+		AstFile *f = &c->parser->files[i];
+		Scope *scope = f->scope;
+		gb_for_array(j, scope->elements.entries) {
+			Entity *e = scope->elements.entries[j].value;
+			switch (e->kind) {
+			case Entity_ImportName: {
+				if (!e->ImportName.used) {
+					warning(e->token, "Unused import name: %.*s", LIT(e->ImportName.name));
+				}
+			} break;
+			}
+		}
+	}
 }
 
 

+ 64 - 10
src/checker/expr.cpp

@@ -385,25 +385,34 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 
 
 // TODO(bill): Cleanup struct field reordering
+// TODO(bill): Inline sorting procedure?
 gb_global BaseTypeSizes __checker_sizes = {};
 gb_global gbAllocator   __checker_allocator = {};
 
 GB_COMPARE_PROC(cmp_struct_entity_size) {
-	// Rule: Biggest to smallest
-	// if same size, order by order in source
+	// Rule:
+	// Biggest to smallest alignment
+	// if same alignment: biggest to smallest size
+	// if same size: order by source order
 	Entity *x = *(Entity **)a;
 	Entity *y = *(Entity **)b;
 	GB_ASSERT(x != NULL);
 	GB_ASSERT(y != NULL);
 	GB_ASSERT(x->kind == Entity_Variable);
 	GB_ASSERT(y->kind == Entity_Variable);
+	i64 xa = type_align_of(__checker_sizes, __checker_allocator, x->type);
+	i64 ya = type_align_of(__checker_sizes, __checker_allocator, y->type);
 	i64 xs = type_size_of(__checker_sizes, __checker_allocator, x->type);
 	i64 ys = type_size_of(__checker_sizes, __checker_allocator, y->type);
-	if (xs == ys) {
-		i32 diff = x->Variable.field_index - y->Variable.field_index;
-		return diff < 0 ? -1 : diff > 0;
+
+	if (xa == ya) {
+		if (xs == ys) {
+			i32 diff = x->Variable.field_index - y->Variable.field_index;
+			return diff < 0 ? -1 : diff > 0;
+		}
+		return xs > ys ? -1 : xs < ys;
 	}
-	return xs > ys ? -1 : xs < ys;
+	return xa > ya ? -1 : xa < ya;
 }
 
 void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) {
@@ -746,6 +755,11 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 			error(n->Ident,
 			      "Undeclared name: %.*s", LIT(n->Ident.string));
 		}
+		o->type = t_invalid;
+		o->mode = Addressing_Invalid;
+		if (named_type != NULL) {
+			set_base_type(named_type, t_invalid);
+		}
 		return;
 	}
 	add_entity_use(&c->info, n, e);
@@ -761,7 +775,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 	check_entity_decl(c, e, NULL, named_type, cycle_checker);
 
 	if (e->type == NULL) {
-		GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string));
+		compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string));
 		return;
 	}
 
@@ -820,7 +834,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 		return;
 
 	default:
-		GB_PANIC("Compiler error: Unknown EntityKind");
+		compiler_error("Compiler error: Unknown EntityKind");
 		break;
 	}
 
@@ -2785,6 +2799,44 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 		operand->type = type;
 	} break;
+
+	case BuiltinProc_enum_to_string: {
+		Type *type = get_base_type(operand->type);
+		if (!is_type_enum(type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(ast_node_token(call),
+			      "Expected an enum to `enum_to_string`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		if (operand->mode == Addressing_Constant) {
+			ExactValue value = make_exact_value_string(make_string(""));
+			if (operand->value.kind == ExactValue_Integer) {
+				i64 index = operand->value.value_integer;
+				for (isize i = 0; i < type->Record.other_field_count; i++) {
+					Entity *f = type->Record.other_fields[i];
+					if (f->kind == Entity_Constant && f->Constant.value.kind == ExactValue_Integer) {
+						i64 fv = f->Constant.value.value_integer;
+						if (index == fv) {
+							value = make_exact_value_string(f->token.string);
+							break;
+						}
+					}
+				}
+			}
+
+			operand->value = value;
+			operand->type = t_string;
+			return true;
+		}
+
+		add_type_info_type(c, operand->type);
+
+		operand->mode = Addressing_Value;
+		operand->type = t_string;
+	} break;
 	}
 
 	return true;
@@ -2832,8 +2884,10 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 		Entity **sig_params = proc_type->Proc.params->Tuple.variables;
 		gb_for_array(arg_index, ce->args) {
 			check_multi_expr(c, operand, ce->args[arg_index]);
-			if (operand->mode == Addressing_Invalid)
+			if (operand->mode == Addressing_Invalid) {
+				param_index++;
 				continue;
+			}
 			if (operand->type->kind != Type_Tuple) {
 				check_not_tuple(c, operand);
 				isize index = param_index;
@@ -3083,7 +3137,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		case Type_Record: {
 			if (!is_type_struct(t))
 				break;
-			if (gb_array_count(cl->elems) == 0) {
+			if (cl->elems == NULL || gb_array_count(cl->elems) == 0) {
 				break; // NOTE(bill): No need to init
 			}
 			{ // Checker values

+ 32 - 12
src/checker/stmt.cpp

@@ -8,9 +8,12 @@ enum StmtFlag : u32 {
 
 
 void check_stmt(Checker *c, AstNode *node, u32 flags);
-void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later);
+void check_proc_decl(Checker *c, Entity *e, DeclInfo *d);
 
 void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
+	// TODO(bill): Allow declaration (expect variable) in any order
+	// even within a procedure
+
 	b32 ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
 	u32 f = flags & (~Stmt_FallthroughAllowed);
 
@@ -321,7 +324,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
 	}
 	if (!is_type_constant_type(operand->type)) {
 		// NOTE(bill): no need to free string as it's panicking
-		GB_PANIC("Compiler error: Type `%s` not constant!!!", type_to_string(operand->type));
+		compiler_error("Type `%s` not constant!!!", type_to_string(operand->type));
 	}
 
 	if (e->type == NULL) // NOTE(bill): type inference
@@ -482,7 +485,7 @@ b32 are_signatures_similar_enough(Type *a_, Type *b_) {
 	return true;
 }
 
-void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
+void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	GB_ASSERT(e->type == NULL);
 
 	Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
@@ -494,6 +497,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 
 
 	b32 is_foreign   = (pd->tags & ProcTag_foreign)   != 0;
+	b32 is_link_name = (pd->tags & ProcTag_link_name) != 0;
 	b32 is_inline    = (pd->tags & ProcTag_inline)    != 0;
 	b32 is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
 
@@ -527,11 +531,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 		d->scope = c->context.scope;
 
 		GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
-		if (check_body_later) {
-			check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body);
-		} else {
-			check_proc_body(c, e->token, d, proc_type, pd->body);
-		}
+		check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body);
 	}
 
 	if (is_foreign) {
@@ -557,6 +557,23 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 		} else {
 			map_set(fp, key, e);
 		}
+	} else if (is_link_name) {
+		auto *fp = &c->info.foreign_procs;
+		auto *proc_decl = &d->proc_decl->ProcDecl;
+		String name = proc_decl->link_name;
+
+		HashKey key = hash_string(name);
+		auto *found = map_get(fp, key);
+		if (found) {
+			Entity *f = *found;
+			TokenPos pos = f->token.pos;
+			error(ast_node_token(d->proc_decl),
+			      "Non unique #link_name for procedure `%.*s`\n"
+			      "\tother at %.*s(%td:%td)",
+			      LIT(name), LIT(pos.file), pos.line, pos.column);
+		} else {
+			map_set(fp, key, e);
+		}
 	}
 
 }
@@ -610,7 +627,9 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
 		if (found) {
 			d = *found;
 		} else {
-			GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
+			e->type = t_invalid;
+			return;
+			// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
 		}
 	}
 
@@ -620,7 +639,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
 	// defer (c->context.scope = prev);
 
 	if (e->kind == Entity_Procedure) {
-		check_proc_decl(c, e, d, true);
+		check_proc_decl(c, e, d);
 		return;
 	}
 
@@ -644,7 +663,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
 	} break;
 
 	case Entity_Procedure: {
-		check_proc_decl(c, e, d, true);
+		check_proc_decl(c, e, d);
 	} break;
 
 
@@ -1261,6 +1280,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				// NOTE(bill): Dummy type
 				Type *tag_ptr_type = make_type_pointer(c->allocator, tag_type);
 				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tag_ptr_type);
+				tag_var->Variable.used = true;
 				add_entity(c, c->context.scope, ms->var, tag_var);
 				add_entity_use(&c->info, ms->var, tag_var);
 			}
@@ -1481,7 +1501,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 		DeclInfo *decl = make_declaration_info(c->allocator, e->scope);
 		decl->proc_decl = node;
-		check_proc_decl(c, e, decl, false);
+		check_proc_decl(c, e, decl);
 	case_end;
 
 	case_ast_node(td, TypeDecl, node);

+ 8 - 10
src/checker/type.cpp

@@ -7,12 +7,10 @@ enum BasicKind {
 	Basic_i16,
 	Basic_i32,
 	Basic_i64,
-	Basic_i128,
 	Basic_u8,
 	Basic_u16,
 	Basic_u32,
 	Basic_u64,
-	Basic_u128,
 	Basic_f32,
 	Basic_f64,
 	Basic_int,
@@ -296,12 +294,10 @@ gb_global Type basic_types[] = {
 	{0, Type_Basic, {Basic_i16,            BasicFlag_Integer,                      STR_LIT("i16")}},
 	{0, Type_Basic, {Basic_i32,            BasicFlag_Integer,                      STR_LIT("i32")}},
 	{0, Type_Basic, {Basic_i64,            BasicFlag_Integer,                      STR_LIT("i64")}},
-	{0, Type_Basic, {Basic_i128,           BasicFlag_Integer,                      STR_LIT("i128")}},
 	{0, Type_Basic, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u8")}},
 	{0, Type_Basic, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u16")}},
 	{0, Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u32")}},
 	{0, Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u64")}},
-	{0, Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u128")}},
 	{0, Type_Basic, {Basic_f32,            BasicFlag_Float,                        STR_LIT("f32")}},
 	{0, Type_Basic, {Basic_f64,            BasicFlag_Float,                        STR_LIT("f64")}},
 	{0, Type_Basic, {Basic_int,            BasicFlag_Integer,                      STR_LIT("int")}},
@@ -328,12 +324,10 @@ gb_global Type *t_i8              = &basic_types[Basic_i8];
 gb_global Type *t_i16             = &basic_types[Basic_i16];
 gb_global Type *t_i32             = &basic_types[Basic_i32];
 gb_global Type *t_i64             = &basic_types[Basic_i64];
-gb_global Type *t_i128            = &basic_types[Basic_i128];
 gb_global Type *t_u8              = &basic_types[Basic_u8];
 gb_global Type *t_u16             = &basic_types[Basic_u16];
 gb_global Type *t_u32             = &basic_types[Basic_u32];
 gb_global Type *t_u64             = &basic_types[Basic_u64];
-gb_global Type *t_u128            = &basic_types[Basic_u128];
 gb_global Type *t_f32             = &basic_types[Basic_f32];
 gb_global Type *t_f64             = &basic_types[Basic_f64];
 gb_global Type *t_int             = &basic_types[Basic_int];
@@ -602,7 +596,12 @@ b32 are_types_identical(Type *x, Type *y) {
 					break;
 
 				case TypeRecord_Enum:
-					return are_types_identical(x->Record.enum_base, y->Record.enum_base);
+					if (are_types_identical(x->Record.enum_base, y->Record.enum_base)) {
+						if (x->Record.field_count == y->Record.field_count) {
+							return x->Record.fields == y->Record.fields;
+						}
+					}
+					return false;
 				}
 			}
 		}
@@ -679,12 +678,10 @@ gb_global i64 basic_type_sizes[] = {
 	2,  // Basic_i16
 	4,  // Basic_i32
 	8,  // Basic_i64
-	16, // Basic_i128
 	1,  // Basic_u8
 	2,  // Basic_u16
 	4,  // Basic_u32
 	8,  // Basic_u64
-	16, // Basic_u128
 	4,  // Basic_f32
 	8,  // Basic_f64
 };
@@ -954,7 +951,8 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 	} break;
 	}
 
-	return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align);
+	// return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align);
+	return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.word_size);
 }
 
 i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields, isize field_count, b32 is_packed) {

+ 116 - 21
src/codegen/codegen.cpp

@@ -67,16 +67,6 @@ String ssa_mangle_name(ssaGen *s, String path, String name) {
 }
 
 void ssa_gen_tree(ssaGen *s) {
-	if (v_zero == NULL) {
-		v_zero   = ssa_make_const_int (gb_heap_allocator(), 0);
-		v_one    = ssa_make_const_int (gb_heap_allocator(), 1);
-		v_zero32 = ssa_make_const_i32 (gb_heap_allocator(), 0);
-		v_one32  = ssa_make_const_i32 (gb_heap_allocator(), 1);
-		v_two32  = ssa_make_const_i32 (gb_heap_allocator(), 2);
-		v_false  = ssa_make_const_bool(gb_heap_allocator(), false);
-		v_true   = ssa_make_const_bool(gb_heap_allocator(), true);
-	}
-
 	struct ssaGlobalVariable {
 		ssaValue *var, *init;
 		DeclInfo *decl;
@@ -85,9 +75,35 @@ void ssa_gen_tree(ssaGen *s) {
 	ssaModule *m = &s->module;
 	CheckerInfo *info = m->info;
 	gbAllocator a = m->allocator;
+
+	if (v_zero == NULL) {
+		v_zero   = ssa_make_const_int (m->allocator, 0);
+		v_one    = ssa_make_const_int (m->allocator, 1);
+		v_zero32 = ssa_make_const_i32 (m->allocator, 0);
+		v_one32  = ssa_make_const_i32 (m->allocator, 1);
+		v_two32  = ssa_make_const_i32 (m->allocator, 2);
+		v_false  = ssa_make_const_bool(m->allocator, false);
+		v_true   = ssa_make_const_bool(m->allocator, true);
+	}
+
+	isize global_variable_max_count = 0;
+
+	gb_for_array(i, info->entities.entries) {
+		auto *entry = &info->entities.entries[i];
+		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
+		if (e->kind == Entity_Variable) {
+			global_variable_max_count++;
+		}
+	}
+
+
+	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+	defer (gb_temp_arena_memory_end(tmp));
+
 	gbArray(ssaGlobalVariable) global_variables;
-	gb_array_init(global_variables, gb_heap_allocator());
-	defer (gb_array_free(global_variables));
+	gb_array_init_reserve(global_variables, m->tmp_allocator, global_variable_max_count);
+
+
 
 	gb_for_array(i, info->entities.entries) {
 		auto *entry = &info->entities.entries[i];
@@ -123,7 +139,7 @@ void ssa_gen_tree(ssaGen *s) {
 					ExactValue v = tav->value;
 					if (v.kind == ExactValue_String) {
 						// NOTE(bill): The printer will fix the value correctly
-						g->Global.value = ssa_add_global_string_array(m, v.value_string);
+						// g->Global.value = ssa_add_global_string_array(m, v.value_string);
 					} else {
 						g->Global.value = ssa_make_value_constant(a, tav->type, v);
 					}
@@ -147,6 +163,8 @@ void ssa_gen_tree(ssaGen *s) {
 			}
 			if (pd->foreign_name.len > 0) {
 				name = pd->foreign_name;
+			} else if (pd->link_name.len > 0) {
+				name = pd->link_name;
 			}
 
 			ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name);
@@ -171,7 +189,18 @@ void ssa_gen_tree(ssaGen *s) {
 	ssaDebugInfo *compile_unit = m->debug_info.entries[0].value;
 	GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit);
 	ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs);
-	gb_array_init(all_procs->AllProcs.procs, gb_heap_allocator());
+
+	isize all_proc_max_count = 0;
+	gb_for_array(i, m->debug_info.entries) {
+		auto *entry = &m->debug_info.entries[i];
+		ssaDebugInfo *di = entry->value;
+		di->id = i;
+		if (di->kind == ssaDebugInfo_Proc) {
+			all_proc_max_count++;
+		}
+	}
+
+	gb_array_init_reserve(all_procs->AllProcs.procs, m->allocator, all_proc_max_count);
 	map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped
 	compile_unit->CompileUnit.all_procs = all_procs;
 
@@ -250,10 +279,12 @@ void ssa_gen_tree(ssaGen *s) {
 
 			// Useful types
 			Type *t_int_ptr              = make_type_pointer(a, t_int);
+			Type *t_i64_ptr              = make_type_pointer(a, t_i64);
 			Type *t_bool_ptr             = make_type_pointer(a, t_bool);
 			Type *t_string_ptr           = make_type_pointer(a, t_string);
 			Type *t_type_info_ptr_ptr    = make_type_pointer(a, t_type_info_ptr);
-
+			Type *t_i64_slice_ptr        = make_type_pointer(a, make_type_slice(a, t_i64));
+			Type *t_string_slice_ptr     = make_type_pointer(a, make_type_slice(a, t_string));
 
 			auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * {
 				return ssa_emit_struct_gep(proc, type_info_data,
@@ -302,12 +333,12 @@ void ssa_gen_tree(ssaGen *s) {
 					case Basic_i16:
 					case Basic_i32:
 					case Basic_i64:
-					case Basic_i128:
+					// case Basic_i128:
 					case Basic_u8:
 					case Basic_u16:
 					case Basic_u32:
 					case Basic_u64:
-					case Basic_u128:
+					// case Basic_u128:
 					case Basic_int:
 					case Basic_uint: {
 						tag = ssa_add_local_generated(proc, t_type_info_integer);
@@ -467,8 +498,72 @@ void ssa_gen_tree(ssaGen *s) {
 						if (enum_base == NULL) {
 							enum_base = t_int;
 						}
-						ssaValue *gep = get_type_info_ptr(proc, type_info_data, enum_base);
-						ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep);
+						ssaValue *base = ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr);
+						ssa_emit_store(proc, base, get_type_info_ptr(proc, type_info_data, enum_base));
+
+						if (t->Record.other_field_count > 0) {
+							Entity **fields = t->Record.other_fields;
+							isize count = t->Record.other_field_count;
+							ssaValue *value_array = NULL;
+							ssaValue *name_array = NULL;
+
+
+							{
+								Token token = {Token_Identifier};
+								i32 id = cast(i32)entry_index;
+								char name_base[] = "__$enum_values";
+								isize name_len = gb_size_of(name_base) + 10;
+								token.string.text = gb_alloc_array(a, u8, name_len);
+								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
+								                               "%s-%d", name_base, id)-1;
+								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count));
+								value_array = ssa_make_value_global(a, e, NULL);
+								value_array->Global.is_private = true;
+								ssa_module_add_value(m, e, value_array);
+								map_set(&m->members, hash_string(token.string), value_array);
+							}
+							{
+								Token token = {Token_Identifier};
+								i32 id = cast(i32)entry_index;
+								char name_base[] = "__$enum_names";
+								isize name_len = gb_size_of(name_base) + 10;
+								token.string.text = gb_alloc_array(a, u8, name_len);
+								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
+								                               "%s-%d", name_base, id)-1;
+								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count));
+								name_array = ssa_make_value_global(a, e, NULL);
+								name_array->Global.is_private = true;
+								ssa_module_add_value(m, e, name_array);
+								map_set(&m->members, hash_string(token.string), name_array);
+							}
+
+							for (isize i = 0; i < count; i++) {
+								ssaValue *value_gep = ssa_emit_struct_gep(proc, value_array, i, t_i64_ptr);
+								ssaValue *name_gep  = ssa_emit_struct_gep(proc, name_array, i, t_string_ptr);
+
+								ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer));
+								ssa_emit_store(proc, name_gep,  ssa_emit_global_string(proc, fields[i]->token.string));
+							}
+
+							ssaValue *v_count = ssa_make_const_int(a, count);
+
+
+							ssaValue *values = ssa_emit_struct_gep(proc, tag, v_one32, t_i64_slice_ptr);
+							ssaValue *names  = ssa_emit_struct_gep(proc, tag, v_two32, t_string_slice_ptr);
+							ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr));
+							ssaValue *name_slice  = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr));
+
+							ssa_emit_store(proc, ssa_emit_struct_gep(proc, value_slice, v_zero32, t_i64_ptr), ssa_array_elem(proc, value_array));
+							ssa_emit_store(proc, ssa_emit_struct_gep(proc, value_slice, v_one32, t_int_ptr), v_count);
+							ssa_emit_store(proc, ssa_emit_struct_gep(proc, value_slice, v_two32, t_int_ptr), v_count);
+
+							ssa_emit_store(proc, ssa_emit_struct_gep(proc, name_slice, v_zero32, t_string_ptr), ssa_array_elem(proc, name_array));
+							ssa_emit_store(proc, ssa_emit_struct_gep(proc, name_slice, v_one32, t_int_ptr), v_count);
+							ssa_emit_store(proc, ssa_emit_struct_gep(proc, name_slice, v_two32, t_int_ptr), v_count);
+
+							ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice));
+							ssa_emit_store(proc, names,  ssa_emit_load(proc, name_slice));
+						}
 					} break;
 					}
 				} break;
@@ -515,10 +610,10 @@ void ssa_gen_tree(ssaGen *s) {
 					ssaValue *variadic = ssa_emit_struct_gep(proc, tag, v_two32,  t_bool_ptr);
 
 					if (t->Proc.params) {
-						ssa_emit_store(proc, params,   get_type_info_ptr(proc, type_info_data, t->Proc.params));
+						ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params));
 					}
 					if (t->Proc.results) {
-						ssa_emit_store(proc, results,  get_type_info_ptr(proc, type_info_data, t->Proc.results));
+						ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results));
 					}
 					ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic));
 

+ 18 - 28
src/codegen/print_llvm.cpp

@@ -142,12 +142,12 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
 		case Basic_i16:    ssa_fprintf(f, "i16");                     break;
 		case Basic_i32:    ssa_fprintf(f, "i32");                     break;
 		case Basic_i64:    ssa_fprintf(f, "i64");                     break;
-		case Basic_i128:   ssa_fprintf(f, "i128");                    break;
+		// case Basic_i128:   ssa_fprintf(f, "i128");                    break;
 		case Basic_u8:     ssa_fprintf(f, "i8");                      break;
 		case Basic_u16:    ssa_fprintf(f, "i16");                     break;
 		case Basic_u32:    ssa_fprintf(f, "i32");                     break;
 		case Basic_u64:    ssa_fprintf(f, "i64");                     break;
-		case Basic_u128:   ssa_fprintf(f, "i128");                    break;
+		// case Basic_u128:   ssa_fprintf(f, "i128");                    break;
 		case Basic_f32:    ssa_fprintf(f, "float");                   break;
 		case Basic_f64:    ssa_fprintf(f, "double");                  break;
 		case Basic_rawptr: ssa_fprintf(f, "%%..rawptr");              break;
@@ -346,27 +346,27 @@ void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type
 		if (scope != NULL) {
 			in_global_scope = scope->is_global || scope->is_init;
 		}
-		if (type_hint != NULL && is_type_string(type_hint)) {
-			ssa_fprintf(f, "{i8* getelementptr inbounds (");
-			ssa_print_type(f, m, value->Global.entity->type);
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, value->Global.entity->type);
-			ssa_fprintf(f, "* ");
-			ssa_print_encoded_global(f, value->Global.entity->token.string, in_global_scope);
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " 0, i32 0), ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " %lld}", 0);
-		} else {
+		// if (type_hint != NULL && is_type_string(type_hint)) {
+		// 	ssa_fprintf(f, "{i8* getelementptr inbounds (");
+		// 	ssa_print_type(f, m, value->Global.entity->type);
+		// 	ssa_fprintf(f, ", ");
+		// 	ssa_print_type(f, m, value->Global.entity->type);
+		// 	ssa_fprintf(f, "* ");
+		// 	ssa_print_encoded_global(f, value->Global.entity->token.string, in_global_scope);
+		// 	ssa_fprintf(f, ", ");
+		// 	ssa_print_type(f, m, t_int);
+		// 	ssa_fprintf(f, " 0, i32 0), ");
+		// 	ssa_print_type(f, m, t_int);
+		// 	ssa_fprintf(f, " %lld}", 0);
+		// } else {
 			ssa_print_encoded_global(f, value->Global.entity->token.string, in_global_scope);
-		}
+		// }
 	} break;
 	case ssaValue_Param:
 		ssa_print_encoded_local(f, value->Param.entity->token.string);
 		break;
 	case ssaValue_Proc:
-		ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & ProcTag_foreign) != 0);
+		ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & (ProcTag_foreign|ProcTag_link_name)) != 0);
 		break;
 	case ssaValue_Instr:
 		ssa_fprintf(f, "%%%d", value->id);
@@ -788,11 +788,7 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
 	}
 
 	ssa_fprintf(f, " ");
-	if (are_strings_equal(proc->name, make_string("main"))) {
-		ssa_print_encoded_global(f, proc->name, true);
-	} else {
-		ssa_print_encoded_global(f, proc->name, (proc->tags & ProcTag_foreign) != 0);
-	}
+	ssa_print_encoded_global(f, proc->name, (proc->tags & (ProcTag_foreign|ProcTag_link_name)) != 0);
 	ssa_fprintf(f, "(");
 
 	if (proc_type->param_count > 0) {
@@ -818,12 +814,6 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
 		ssa_fprintf(f, "noinline ");
 	}
 
-	// if (proc->tags & ProcTag_stdcall) {
-	// 	ssa_fprintf(f, "\"cc\"=\"64\" ");
-	// }
-	// if (proc->tags & ProcTag_fastcall) {
-	// 	ssa_fprintf(f, "\"cc\"=\"65\" ");
-	// }
 
 	if (proc->module->generate_debug_info && proc->entity != NULL) {
 		ssaDebugInfo *di = *map_get(&proc->module->debug_info, hash_pointer(proc->entity));

+ 48 - 27
src/codegen/ssa.cpp

@@ -46,7 +46,9 @@ struct ssaModule {
 	CheckerInfo * info;
 	BaseTypeSizes sizes;
 	gbArena       arena;
+	gbArena       tmp_arena;
 	gbAllocator   allocator;
+	gbAllocator   tmp_allocator;
 	b32 generate_debug_info;
 
 	u32 stmt_state_flags;
@@ -364,7 +366,9 @@ void ssa_init_module(ssaModule *m, Checker *c) {
 	isize token_count = c->parser->total_token_count;
 	isize arena_size = 4 * token_count * gb_size_of(ssaValue);
 	gb_arena_init_from_allocator(&m->arena, gb_heap_allocator(), arena_size);
-	m->allocator = gb_arena_allocator(&m->arena);
+	gb_arena_init_from_allocator(&m->tmp_arena, gb_heap_allocator(), arena_size);
+	m->allocator     = gb_arena_allocator(&m->arena);
+	m->tmp_allocator = gb_arena_allocator(&m->tmp_arena);
 	m->info = &c->info;
 	m->sizes = c->sizes;
 
@@ -770,6 +774,9 @@ ssaValue *ssa_make_const_int(gbAllocator a, i64 i) {
 ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) {
 	return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i));
 }
+ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) {
+	return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i));
+}
 ssaValue *ssa_make_const_bool(gbAllocator a, b32 b) {
 	return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0));
 }
@@ -1066,8 +1073,10 @@ ssaValue *ssa_lvalue_load(ssaProcedure *proc, ssaAddr lval) {
 
 
 void ssa_begin_procedure_body(ssaProcedure *proc) {
-	gb_array_init(proc->blocks, gb_heap_allocator());
+	gb_array_init(proc->blocks,      gb_heap_allocator());
 	gb_array_init(proc->defer_stmts, gb_heap_allocator());
+	gb_array_init(proc->children,    gb_heap_allocator());
+
 	proc->decl_block  = ssa_add_block(proc, proc->type_expr, make_string("decls"));
 	proc->entry_block = ssa_add_block(proc, proc->type_expr, make_string("entry"));
 	proc->curr_block  = proc->entry_block;
@@ -1466,7 +1475,7 @@ ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *ba
 
 
 ssaValue *ssa_add_global_string_array(ssaModule *m, String string) {
-	gbAllocator a = gb_heap_allocator();
+	gbAllocator a = m->allocator;
 
 	isize max_len = 4+8+1;
 	u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
@@ -2005,9 +2014,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 	case_end;
 
 	case_ast_node(pl, ProcLit, expr);
-		if (proc->children == NULL) {
-			gb_array_init(proc->children, gb_heap_allocator());
-		}
 		// NOTE(bill): Generate a new name
 		// parent$count
 		isize name_len = proc->name.len + 1 + 8 + 1;
@@ -2456,6 +2462,20 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssaValue *cond = ssa_emit_comp(proc, lt, x, v_zero);
 					return ssa_emit_select(proc, cond, neg_x, x);
 				} break;
+
+
+				case BuiltinProc_enum_to_string: {
+					ssa_emit_comment(proc, make_string("enum_to_string"));
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					Type *t = ssa_type(x);
+					ssaValue *ti = ssa_type_info(proc, t);
+
+
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2);
+					args[0] = ti;
+					args[1] = ssa_emit_conv(proc, x, t_i64);
+					return ssa_emit_global_call(proc, "__enum_to_string", args, 2);
+				} break;
 				}
 			}
 		}
@@ -2673,7 +2693,6 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 		String selector = unparen_expr(se->selector)->Ident.string;
 		Type *type = get_base_type(type_of_expr(proc->module->info, se->expr));
 
-
 		if (type == t_invalid) {
 			// Imports
 			Entity *imp = entity_of_ident(proc->module->info, se->expr);
@@ -3039,13 +3058,15 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 	case_end;
 
 	case_ast_node(vd, VarDecl, node);
+		ssaModule *m = proc->module;
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+		defer (gb_temp_arena_memory_end(tmp));
+
 		if (gb_array_count(vd->names) == gb_array_count(vd->values)) { // 1:1 assigment
 			gbArray(ssaAddr)  lvals;
 			gbArray(ssaValue *) inits;
-			gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
-			gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
-			defer (gb_array_free(lvals));
-			defer (gb_array_free(inits));
+			gb_array_init_reserve(lvals, m->tmp_allocator, gb_array_count(vd->names));
+			gb_array_init_reserve(inits, m->tmp_allocator, gb_array_count(vd->names));
 
 			gb_for_array(i, vd->names) {
 				AstNode *name = vd->names[i];
@@ -3079,10 +3100,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		} else { // Tuple(s)
 			gbArray(ssaAddr)  lvals;
 			gbArray(ssaValue *) inits;
-			gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
-			gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
-			defer (gb_array_free(lvals));
-			defer (gb_array_free(inits));
+			gb_array_init_reserve(lvals, m->tmp_allocator, gb_array_count(vd->names));
+			gb_array_init_reserve(inits, m->tmp_allocator, gb_array_count(vd->names));
 
 			gb_for_array(i, vd->names) {
 				AstNode *name = vd->names[i];
@@ -3118,15 +3137,15 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 	case_end;
 
 	case_ast_node(pd, ProcDecl, node);
-		if (proc->children == NULL) {
-			gb_array_init(proc->children, gb_heap_allocator());
-		}
-
-
 		if (pd->body != NULL) {
 			// NOTE(bill): Generate a new name
 			// parent$name-guid
-			String pd_name = pd->name->Ident.string;
+			String original_name = pd->name->Ident.string;
+			String pd_name = original_name;
+			if (pd->link_name.len > 0) {
+				pd_name = pd->link_name;
+			}
+
 			isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1;
 			u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
 			i32 guid = cast(i32)gb_array_count(proc->children);
@@ -3205,11 +3224,15 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 	case_ast_node(as, AssignStmt, node);
 		ssa_emit_comment(proc, make_string("AssignStmt"));
+
+		ssaModule *m = proc->module;
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+		defer (gb_temp_arena_memory_end(tmp));
+
 		switch (as->op.kind) {
 		case Token_Eq: {
 			gbArray(ssaAddr) lvals;
-			gb_array_init(lvals, gb_heap_allocator());
-			defer (gb_array_free(lvals));
+			gb_array_init(lvals, m->tmp_allocator);
 
 			gb_for_array(i, as->lhs) {
 				AstNode *lhs = as->lhs[i];
@@ -3227,8 +3250,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 					ssa_lvalue_store(proc, lvals[0], init);
 				} else {
 					gbArray(ssaValue *) inits;
-					gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(lvals));
-					defer (gb_array_free(inits));
+					gb_array_init_reserve(inits, m->tmp_allocator, gb_array_count(lvals));
 
 					gb_for_array(i, as->rhs) {
 						ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
@@ -3241,8 +3263,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 				}
 			} else {
 				gbArray(ssaValue *) inits;
-				gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(lvals));
-				defer (gb_array_free(inits));
+				gb_array_init_reserve(inits, m->tmp_allocator, gb_array_count(lvals));
 
 				gb_for_array(i, as->rhs) {
 					ssaValue *init = ssa_build_expr(proc, as->rhs[i]);

+ 3 - 3
src/exact_value.cpp

@@ -190,7 +190,7 @@ ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
 	}
 
 failure:
-	GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op.kind]));
+	compiler_error("Invalid unary operation, %.*s", LIT(token_strings[op.kind]));
 
 	ExactValue error_value = {};
 	return error_value;
@@ -212,7 +212,7 @@ i32 exact_value_order(ExactValue v) {
 		return 4;
 
 	default:
-		GB_PANIC("How'd you get here? Invalid Value.kind");
+		compiler_error("How'd you get here? Invalid Value.kind");
 		return -1;
 	}
 }
@@ -249,7 +249,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
 		break;
 	}
 
-	GB_PANIC("How'd you get here? Invalid ExactValueKind");
+	compiler_error("How'd you get here? Invalid ExactValueKind");
 }
 
 // TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?

+ 5 - 4
src/main.cpp

@@ -40,6 +40,7 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 	}
 }
 
+
 #if defined(DISPLAY_TIMING)
 #define INIT_TIMER() f64 start_time = gb_time_now(), end_time = 0, total_time = 0
 #define PRINT_TIMER(section) do { \
@@ -82,7 +83,7 @@ int main(int argc, char **argv) {
 
 	if (!init_parser(&parser))
 		return 1;
-	defer (destroy_parser(&parser));
+	// defer (destroy_parser(&parser));
 
 	if (parse_files(&parser, init_filename) != ParseFile_None)
 		return 1;
@@ -99,7 +100,7 @@ int main(int argc, char **argv) {
 	sizes.max_align = 16;
 
 	init_checker(&checker, &parser, sizes);
-	defer (destroy_checker(&checker));
+	// defer (destroy_checker(&checker));
 
 	check_parsed_files(&checker);
 
@@ -110,7 +111,7 @@ int main(int argc, char **argv) {
 	ssaGen ssa = {};
 	if (!ssa_gen_init(&ssa, &checker))
 		return 1;
-	defer (ssa_gen_destroy(&ssa));
+	// defer (ssa_gen_destroy(&ssa));
 
 	ssa_gen_tree(&ssa);
 
@@ -146,7 +147,7 @@ int main(int argc, char **argv) {
 
 #if 1
 	gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib");
-	defer (gb_string_free(lib_str));
+	// defer (gb_string_free(lib_str));
 	char lib_str_buf[1024] = {};
 	gb_for_array(i, parser.system_libraries) {
 		String lib = parser.system_libraries[i];

+ 52 - 19
src/parser.cpp

@@ -63,14 +63,15 @@ enum ProcTag : u64 {
 	ProcTag_no_bounds_check = GB_BIT(1),
 
 	ProcTag_foreign         = GB_BIT(10),
-	ProcTag_inline          = GB_BIT(11),
-	ProcTag_no_inline       = GB_BIT(12),
-	ProcTag_dll_import      = GB_BIT(13),
-	ProcTag_dll_export      = GB_BIT(14),
-
-	ProcTag_stdcall         = GB_BIT(15),
-	ProcTag_fastcall        = GB_BIT(16),
-	// ProcTag_cdecl           = GB_BIT(17),
+	ProcTag_link_name       = GB_BIT(11),
+	ProcTag_inline          = GB_BIT(12),
+	ProcTag_no_inline       = GB_BIT(13),
+	ProcTag_dll_import      = GB_BIT(14),
+	ProcTag_dll_export      = GB_BIT(15),
+
+	ProcTag_stdcall         = GB_BIT(16),
+	ProcTag_fastcall        = GB_BIT(17),
+	// ProcTag_cdecl           = GB_BIT(18),
 };
 
 enum VarDeclTag {
@@ -237,6 +238,7 @@ AST_NODE_KIND(_DeclBegin,      "", struct{}) \
 			AstNode *body;        \
 			u64     tags;         \
 			String  foreign_name; \
+			String  link_name;    \
 	}) \
 	AST_NODE_KIND(TypeDecl,   "type declaration",   struct { Token token; AstNode *name, *type; }) \
 	AST_NODE_KIND(ImportDecl, "import declaration", struct { \
@@ -806,13 +808,14 @@ gb_inline AstNode *make_proc_type(AstFile *f, Token token, AstNodeArray params,
 	return result;
 }
 
-gb_inline AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name) {
+gb_inline AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name, String link_name) {
 	AstNode *result = make_node(f, AstNode_ProcDecl);
 	result->ProcDecl.name = name;
 	result->ProcDecl.type = proc_type;
 	result->ProcDecl.body = body;
 	result->ProcDecl.tags = tags;
 	result->ProcDecl.foreign_name = foreign_name;
+	result->ProcDecl.link_name = link_name;
 	return result;
 }
 
@@ -1165,10 +1168,10 @@ b32 is_foreign_name_valid(String name) {
 	return true;
 }
 
-void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
+void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_name) {
 	// TODO(bill): Add this to procedure literals too
-
-
+	GB_ASSERT(foreign_name != NULL);
+	GB_ASSERT(link_name    != NULL);
 
 	while (f->cursor[0].kind == Token_Hash) {
 		AstNode *tag_expr = parse_tag_expr(f, NULL);
@@ -1186,11 +1189,24 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
 				*foreign_name = f->cursor[0].string;
 				// TODO(bill): Check if valid string
 				if (!is_foreign_name_valid(*foreign_name)) {
-					syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name");
+					syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name: `%.*s`", LIT(*foreign_name));
 				}
 
 				next_token(f);
 			}
+		} else if (are_strings_equal(tag_name, make_string("link_name"))) {
+			check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name);
+			if (f->cursor[0].kind == Token_String) {
+				*link_name = f->cursor[0].string;
+				// TODO(bill): Check if valid string
+				if (!is_foreign_name_valid(*link_name)) {
+					syntax_error(ast_node_token(tag_expr), "Invalid alternative link procedure name `%.*s`", LIT(*link_name));
+				}
+
+				next_token(f);
+			} else {
+				expect_token(f, Token_String);
+			}
 		}
 		ELSE_IF_ADD_TAG(bounds_check)
 		ELSE_IF_ADD_TAG(no_bounds_check)
@@ -1208,6 +1224,10 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
 		#undef ELSE_IF_ADD_TAG
 	}
 
+	if ((*tags & ProcTag_foreign) && (*tags & ProcTag_link_name)) {
+		syntax_error(f->cursor[0], "You cannot apply both #foreign and #link_name to a procedure");
+	}
+
 	if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
 		syntax_error(f->cursor[0], "You cannot apply both #inline and #no_inline to a procedure");
 	}
@@ -1219,6 +1239,10 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
 	if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) {
 		syntax_error(f->cursor[0], "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body");
 	}
+
+	if ((*tags & ProcTag_stdcall) && (*tags & ProcTag_fastcall)) {
+		syntax_error(f->cursor[0], "You cannot apply one calling convention to a procedure");
+	}
 }
 
 AstNode *parse_operand(AstFile *f, b32 lhs) {
@@ -1292,10 +1316,14 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 
 		u64 tags = 0;
 		String foreign_name = {};
-		parse_proc_tags(f, &tags, &foreign_name);
+		String link_name = {};
+		parse_proc_tags(f, &tags, &foreign_name, &link_name);
 		if (tags & ProcTag_foreign) {
 			syntax_error(f->cursor[0], "#foreign cannot be applied to procedure literals");
 		}
+		if (tags & ProcTag_link_name) {
+			syntax_error(f->cursor[0], "#link_name cannot be applied to procedure literals");
+		}
 
 		if (f->cursor[0].kind != Token_OpenBrace) {
 			return type;
@@ -2082,8 +2110,9 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 	AstNode *body = NULL;
 	u64 tags = 0;
 	String foreign_name = {};
+	String link_name = {};
 
-	parse_proc_tags(f, &tags, &foreign_name);
+	parse_proc_tags(f, &tags, &foreign_name, &link_name);
 
 	AstNode *curr_proc = f->curr_proc;
 	f->curr_proc = proc_type;
@@ -2096,7 +2125,7 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 		body = parse_body(f);
 	}
 
-	return make_proc_decl(f, name, proc_type, body, tags, foreign_name);
+	return make_proc_decl(f, name, proc_type, body, tags, foreign_name, link_name);
 }
 
 AstNode *parse_decl(AstFile *f, AstNodeArray names) {
@@ -2794,7 +2823,7 @@ b32 try_add_foreign_system_library_path(Parser *p, String import_file) {
 }
 
 gb_global Rune illegal_import_runes[] = {
-	'"', '\'', '`', ' ',
+	'"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f',
 	'\\', // NOTE(bill): Disallow windows style filepaths
 	'!', '$', '%', '^', '&', '*', '(', ')', '=', '+',
 	'[', ']', '{', '}',
@@ -2862,10 +2891,12 @@ void parse_file(Parser *p, AstFile *f) {
 
 				if (!is_import_path_valid(file_str)) {
 					if (id->is_load) {
-						syntax_error(ast_node_token(node), "Invalid #load path");
+						syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str));
 					} else {
-						syntax_error(ast_node_token(node), "Invalid #import path");
+						syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str));
 					}
+					// NOTE(bill): It's a naughty name
+					f->decls[i] = make_bad_decl(f, id->token, id->token);
 					continue;
 				}
 
@@ -2888,6 +2919,8 @@ void parse_file(Parser *p, AstFile *f) {
 
 				if (!is_import_path_valid(file_str)) {
 					syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
+					// NOTE(bill): It's a naughty name
+					f->decls[i] = make_bad_decl(f, id->token, id->token);
 					continue;
 				}