Răsfoiți Sursa

Merge branch 'master' into parser-experiments

gingerBill 4 ani în urmă
părinte
comite
f0683c9102
86 a modificat fișierele cu 13186 adăugiri și 4449 ștergeri
  1. 12 3
      .github/workflows/nightly.yml
  2. 1 1
      Makefile
  3. 1 1
      PROPOSAL-PROCESS.md
  4. 1 1
      README.md
  5. 31 0
      build-m1.sh
  6. 68 0
      core/bufio/read_writer.odin
  7. 474 0
      core/bufio/reader.odin
  8. 255 0
      core/bufio/writer.odin
  9. 335 0
      core/bytes/buffer.odin
  10. 177 0
      core/bytes/reader.odin
  11. 1032 0
      core/bytes/strings.odin
  12. 1 1
      core/dynlib/lib.odin
  13. 7 8
      core/encoding/json/marshal.odin
  14. 241 202
      core/fmt/fmt.odin
  15. 5 1
      core/intrinsics/intrinsics.odin
  16. 200 0
      core/io/conv.odin
  17. 504 0
      core/io/io.odin
  18. 113 0
      core/io/multi.odin
  19. 192 0
      core/io/util.odin
  20. 24 0
      core/math/math.odin
  21. 11 4
      core/odin/ast/ast.odin
  22. 123 44
      core/odin/parser/parser.odin
  23. 13 12
      core/odin/tokenizer/token.odin
  24. 64 15
      core/odin/tokenizer/tokenizer.odin
  25. 69 0
      core/os/stream.odin
  26. 2 1
      core/path/filepath/path_windows.odin
  27. 0 2
      core/path/match.odin
  28. 79 0
      core/reflect/reflect.odin
  29. 161 132
      core/reflect/types.odin
  30. 32 1282
      core/runtime/core.odin
  31. 838 0
      core/runtime/core_builtin.odin
  32. 100 0
      core/runtime/dynamic_array_internal.odin
  33. 394 0
      core/runtime/dynamic_map_internal.odin
  34. 57 4
      core/runtime/error_checks.odin
  35. 24 17
      core/runtime/internal.odin
  36. 135 0
      core/runtime/internal_linux.odin
  37. 92 92
      core/runtime/internal_windows.odin
  38. 1 1
      core/runtime/print.odin
  39. 5 6
      core/runtime/procs_windows_amd64.odin
  40. 42 1
      core/slice/slice.odin
  41. 4 4
      core/slice/sort.odin
  42. 176 81
      core/strings/builder.odin
  43. 269 0
      core/strings/conversion.odin
  44. 177 0
      core/strings/reader.odin
  45. 114 268
      core/strings/strings.odin
  46. 69 42
      core/sync/channel.odin
  47. 35 0
      core/sys/cpu/cpu.odin
  48. 67 0
      core/sys/cpu/cpu_x86.odin
  49. 9 9
      core/sys/unix/pthread_darwin.odin
  50. 9 9
      core/sys/unix/pthread_freebsd.odin
  51. 9 9
      core/sys/unix/pthread_linux.odin
  52. 2 2
      core/sys/win32/kernel32.odin
  53. 585 0
      core/text/scanner/scanner.odin
  54. 107 7
      core/thread/thread.odin
  55. 3 2
      core/thread/thread_unix.odin
  56. 6 2
      core/thread/thread_windows.odin
  57. 42 996
      core/unicode/letter.odin
  58. 1272 0
      core/unicode/tables.odin
  59. 41 0
      core/unicode/utf8/utf8.odin
  60. 3 3
      examples/demo/demo.odin
  61. 87 2
      src/array.cpp
  62. 51 0
      src/build_settings.cpp
  63. 5 8
      src/check_decl.cpp
  64. 290 129
      src/check_expr.cpp
  65. 14 21
      src/check_stmt.cpp
  66. 196 84
      src/check_type.cpp
  67. 188 79
      src/checker.cpp
  68. 20 12
      src/checker.hpp
  69. 6 0
      src/checker_builtin_procs.hpp
  70. 132 9
      src/common.cpp
  71. 284 72
      src/docs.cpp
  72. 4 3
      src/entity.cpp
  73. 59 51
      src/exact_value.cpp
  74. 101 4
      src/gb/gb.h
  75. 413 107
      src/ir.cpp
  76. 45 20
      src/ir_print.cpp
  77. 959 0
      src/llvm_abi.cpp
  78. 473 166
      src/llvm_backend.cpp
  79. 31 2
      src/llvm_backend.hpp
  80. 371 160
      src/main.cpp
  81. 184 100
      src/parser.cpp
  82. 42 39
      src/parser.hpp
  83. 91 46
      src/ptr_set.cpp
  84. 1 0
      src/string.cpp
  85. 0 2
      src/tokenizer.cpp
  86. 224 68
      src/types.cpp

+ 12 - 3
.github/workflows/nightly.yml

@@ -1,6 +1,7 @@
 name:  Nightly
 
 on:
+  workflow_dispatch:
   schedule:
     - cron: 0 20 * * *
 
@@ -50,7 +51,7 @@ jobs:
       - name: (Linux) Download LLVM
         run: sudo apt-get install llvm
       - name: build odin
-        run: make release
+        run: make nightly
       - name: Odin run
         run: ./odin run examples/demo/demo.odin
       - name: Copy artifacts
@@ -76,7 +77,7 @@ jobs:
           TMP_PATH=$(xcrun --show-sdk-path)/user/include
           echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
       - name: build odin
-        run: make release
+        run: make nightly
       - name: Odin run
         run: ./odin run examples/demo/demo.odin
       - name: Copy artifacts
@@ -96,10 +97,18 @@ jobs:
     needs: [build_windows, build_macos, build_ubuntu]
     steps:
       - uses: actions/checkout@v1
+      - uses: actions/setup-python@v2
+        with:
+          python-version: '3.x'
       
       - name: Install B2 CLI
         shell: bash
-        run: sudo pip install --upgrade b2
+        run: |
+          python -m pip install --upgrade pip
+          pip install --upgrade b2
+      
+      - name: Display Python version
+        run: python -c "import sys; print(sys.version)"
       
       - name: Download Windows artifacts
         uses: actions/download-artifact@v1

+ 1 - 1
Makefile

@@ -22,7 +22,7 @@ release:
 	$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
 
 nightly:
-	$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin
+	$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
 
 
 

+ 1 - 1
PROPOSAL-PROCESS.md

@@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci
 	* Accept proposal
 	* Decline proposal
 
-After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
+After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project.
 
 ## Design Documents
 

+ 1 - 1
README.md

@@ -60,7 +60,7 @@ main :: proc() {
 
 #### [Getting Started](https://odin-lang.org/docs/install)
 
-Instructions for downloading and install the Odin compiler and libraries.
+Instructions for downloading and installing the Odin compiler and libraries.
 
 ### Learning Odin
 

+ 31 - 0
build-m1.sh

@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+release_mode=$1
+
+warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
+libraries="-pthread -ldl -lm -lstdc++"
+other_args="-DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM"
+compiler="clang"
+
+if [ -z "$release_mode" ]; then release_mode="0"; fi
+
+if [ "$release_mode" -eq "0" ]; then
+	other_args="${other_args} -g"
+fi
+if [ "$release_mode" -eq "1" ]; then
+	other_args="${other_args} -O3 -march=native"
+fi
+
+if [[ "$(uname)" == "Darwin" ]]; then
+
+	# Set compiler to clang on MacOS
+	# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
+	compiler="clang"
+
+	other_args="${other_args} -liconv"
+elif [[ "$(uname)" == "FreeBSD" ]]; then
+	compiler="clang"
+fi
+
+${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin \
+	&& ./odin run examples/demo/demo.odin -llvm-api

+ 68 - 0
core/bufio/read_writer.odin

@@ -0,0 +1,68 @@
+package bufio
+
+import "core:io"
+
+// Read_Writer stores pointers to a Reader and a Writer
+Read_Writer :: struct {
+	r: ^Reader,
+	w: ^Writer,
+}
+
+
+read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
+	rw.r, rw.w = r, w;
+}
+
+read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
+	s.stream_data = rw;
+	s.stream_vtable = _read_writer_vtable;
+	return;
+}
+
+@(private)
+_read_writer_vtable := &io.Stream_VTable{
+	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Read_Writer)(s.stream_data).r;
+		return reader_read(b, p);
+	},
+	impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
+		b := (^Read_Writer)(s.stream_data).r;
+		return reader_read_byte(b);
+	},
+	impl_unread_byte = proc(s: io.Stream) -> io.Error {
+		b := (^Read_Writer)(s.stream_data).r;
+		return reader_unread_byte(b);
+	},
+	impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+		b := (^Read_Writer)(s.stream_data).r;
+		return reader_read_rune(b);
+	},
+	impl_unread_rune = proc(s: io.Stream) -> io.Error {
+		b := (^Read_Writer)(s.stream_data).r;
+		return reader_unread_rune(b);
+	},
+	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+		b := (^Read_Writer)(s.stream_data).r;
+		return reader_write_to(b, w);
+	},
+	impl_flush = proc(s: io.Stream)  -> io.Error {
+		b := (^Read_Writer)(s.stream_data).w;
+		return writer_flush(b);
+	},
+	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Read_Writer)(s.stream_data).w;
+		return writer_write(b, p);
+	},
+	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+		b := (^Read_Writer)(s.stream_data).w;
+		return writer_write_byte(b, c);
+	},
+	impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+		b := (^Read_Writer)(s.stream_data).w;
+		return writer_write_rune(b, r);
+	},
+	impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
+		b := (^Read_Writer)(s.stream_data).w;
+		return writer_read_from(b, r);
+	},
+};

+ 474 - 0
core/bufio/reader.odin

@@ -0,0 +1,474 @@
+package bufio
+
+import "core:io"
+import "core:mem"
+import "core:unicode/utf8"
+import "core:bytes"
+
+// Reader is a buffered wrapper for an io.Reader
+Reader :: struct {
+	buf:            []byte,
+	buf_allocator:  mem.Allocator,
+
+	rd:             io.Reader, // reader
+	r, w:           int, // read and write positions for buf
+
+	err:            io.Error,
+
+	last_byte:      int, // last byte read, invalid is -1
+	last_rune_size: int, // size of last rune read, invalid is -1
+}
+
+
+DEFAULT_BUF_SIZE :: 4096;
+
+@(private)
+MIN_READ_BUFFER_SIZE :: 16;
+@(private)
+MAX_CONSECUTIVE_EMPTY_READS :: 128;
+
+reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
+	size := size;
+	size = max(size, MIN_READ_BUFFER_SIZE);
+	reader_reset(b, rd);
+	b.buf_allocator = allocator;
+	b.buf = make([]byte, size, allocator);
+}
+
+reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
+	reader_reset(b, rd);
+	b.buf_allocator = {};
+	b.buf = buf;
+}
+
+// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
+reader_destroy :: proc(b: ^Reader) {
+	delete(b.buf, b.buf_allocator);
+	b^ = {};
+}
+
+reader_size :: proc(b: ^Reader) -> int {
+	return len(b.buf);
+}
+
+reader_reset :: proc(b: ^Reader, r: io.Reader) {
+	b.rd = r;
+	b.r, b.w = 0, 0;
+	b.err = nil;
+	b.last_byte      = -1;
+	b.last_rune_size = -1;
+}
+
+@(private)
+_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
+	if b.r > 0 {
+		copy(b.buf, b.buf[b.r:b.w]);
+		b.w -= b.r;
+		b.r = 0;
+	}
+
+	if b.w >= len(b.buf) {
+		return .Buffer_Full;
+	}
+
+	// read new data, and try a limited number of times
+	for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
+		n, err := io.read(b.rd, b.buf[b.w:]);
+		if n < 0 {
+			return .Negative_Read;
+		}
+		b.w += n;
+		if err != nil {
+			b.err = err;
+			return nil;
+		}
+		if n > 0 {
+			return nil;
+		}
+	}
+	b.err = .No_Progress;
+	return nil;
+}
+
+@(private)
+_reader_consume_err :: proc(b: ^Reader) -> io.Error {
+	err := b.err;
+	b.err = nil;
+	return err;
+}
+
+// reader_peek returns the next n bytes without advancing the reader
+// The bytes stop being valid on the next read call
+// If reader_peek returns fewer than n bytes, it also return an error
+// explaining why the read is short
+// The error will be .Buffer_Full if n is larger than the internal buffer size
+reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
+	n := n;
+
+	if n < 0 {
+		return nil, .Negative_Count;
+	}
+	b.last_byte = -1;
+	b.last_rune_size = -1;
+
+	for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
+		if fill_err := _reader_read_new_chunk(b); fill_err != nil {
+			return nil, fill_err;
+		}
+	}
+
+	if n > len(b.buf) {
+		return b.buf[b.r : b.w], .Buffer_Full;
+	}
+
+	if available := b.w - b.r; available < n {
+		n = available;
+		err = _reader_consume_err(b);
+		if err == nil {
+			err = .Buffer_Full;
+		}
+	}
+
+	return b.buf[b.r : b.r+n], err;
+}
+
+// reader_buffered returns the number of bytes that can be read from the current buffer
+reader_buffered :: proc(b: ^Reader) -> int {
+	return b.w - b.r;
+}
+
+// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
+reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
+	if n < 0 {
+		return 0, .Negative_Count;
+	}
+	if n == 0 {
+		return;
+	}
+
+	remaining := n;
+	for {
+		skip := reader_buffered(b);
+		if skip == 0 {
+			if fill_err := _reader_read_new_chunk(b); fill_err != nil {
+				return 0, fill_err;
+			}
+			skip = reader_buffered(b);
+		}
+		skip = min(skip, remaining);
+		b.r += skip;
+		remaining -= skip;
+		if remaining == 0 {
+			return n, nil;
+		}
+		if b.err != nil {
+			return n - remaining, _reader_consume_err(b);
+		}
+	}
+
+	return;
+}
+
+// reader_read reads data into p
+// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
+reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+	n = len(p);
+	if n == 0 {
+		if reader_buffered(b) > 0 {
+			return 0, nil;
+		}
+		return 0, _reader_consume_err(b);
+	}
+	if b.r == b.w {
+		if b.err != nil {
+			return 0, _reader_consume_err(b);
+		}
+
+		if len(p) >= len(b.buf) {
+			n, b.err = io.read(b.rd, p);
+			if n < 0 {
+				return 0, .Negative_Read;
+			}
+
+			if n > 0 {
+				b.last_byte = int(p[n-1]);
+				b.last_rune_size = -1;
+			}
+			return n, _reader_consume_err(b);
+		}
+
+		b.r, b.w = 0, 0;
+		n, b.err = io.read(b.rd, b.buf);
+		if n < 0 {
+			return 0, .Negative_Read;
+		}
+		if n == 0 {
+			return 0, _reader_consume_err(b);
+		}
+		b.w += n;
+	}
+
+	n = copy(p, b.buf[b.r:b.w]);
+	b.r += n;
+	b.last_byte = int(b.buf[b.r-1]);
+	b.last_rune_size = -1;
+	return n, nil;
+}
+
+// reader_read_byte reads and returns a single byte
+// If no byte is available, it return an error
+reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
+	b.last_rune_size = -1;
+	for b.r == b.w {
+		if b.err != nil {
+			return 0, _reader_consume_err(b);
+		}
+		if err := _reader_read_new_chunk(b); err != nil {
+			return 0, err;
+		}
+	}
+	c := b.buf[b.r];
+	b.r += 1;
+	b.last_byte = int(c);
+	return c, nil;
+}
+
+// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
+reader_unread_byte :: proc(b: ^Reader) -> io.Error {
+	if b.last_byte < 0 || b.r == 0 && b.w > 0 {
+		return .Invalid_Unread;
+	}
+	if b.r > 0 {
+		b.r -= 1;
+	} else {
+		// b.r == 0 && b.w == 0
+		b.w = 1;
+	}
+	b.buf[b.r] = byte(b.last_byte);
+	b.last_byte = -1;
+	b.last_rune_size = -1;
+	return nil;
+}
+
+// reader_read_rune reads a single UTF-8 encoded unicode character
+// and returns the rune and its size in bytes
+// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1
+reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
+	for b.r+utf8.UTF_MAX > b.w &&
+	    !utf8.full_rune(b.buf[b.r:b.w]) &&
+	    b.err == nil &&
+	    b.w-b.w < len(b.buf) {
+		if err = _reader_read_new_chunk(b); err != nil {
+			return;
+		}
+	}
+
+	b.last_rune_size = -1;
+	if b.r == b.w {
+		err = _reader_consume_err(b);
+		return;
+	}
+	r, size = rune(b.buf[b.r]), 1;
+	if r >= utf8.RUNE_SELF {
+		r, size = utf8.decode_rune(b.buf[b.r : b.w]);
+	}
+	b.r += size;
+	b.last_byte = int(b.buf[b.r-1]);
+	b.last_rune_size = size;
+	return;
+}
+
+// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
+reader_unread_rune :: proc(b: ^Reader) -> io.Error {
+	if b.last_rune_size < 0 || b.r < b.last_rune_size {
+		return .Invalid_Unread;
+	}
+	b.r -= b.last_rune_size;
+	b.last_byte = -1;
+	b.last_rune_size = -1;
+	return nil;
+}
+
+reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
+	write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
+		n, err := io.write(w, b.buf[b.r:b.w]);
+		if n < 0 {
+			return 0, .Negative_Write;
+		}
+		b.r += n;
+		return i64(n), err;
+	}
+
+	n, err = write_buf(b, w);
+	if err != nil {
+		return;
+	}
+
+	m: i64;
+	if nr, cerr := io.to_writer_to(b.rd); cerr == nil {
+		m, err = io.write_to(nr, w);
+		n += m;
+		return n, err;
+	}
+
+	if nw, cerr := io.to_reader_from(w); cerr == nil {
+		m, err = io.read_from(nw, b.rd);
+		n += m;
+		return n, err;
+	}
+
+	if b.w-b.r < len(b.buf) {
+		if err = _reader_read_new_chunk(b); err != nil {
+			return;
+		}
+	}
+
+	for b.r < b.w {
+		m, err = write_buf(b, w);
+		n += m;
+		if err != nil {
+			return;
+		}
+		if err = _reader_read_new_chunk(b); err != nil {
+			return;
+		}
+	}
+
+	if b.err == .EOF {
+		b.err = nil;
+	}
+
+	err = _reader_consume_err(b);
+	return;
+}
+
+
+
+// reader_to_stream converts a Reader into an io.Stream
+reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
+	s.stream_data = b;
+	s.stream_vtable = _reader_vtable;
+	return;
+}
+
+
+
+@(private)
+_reader_vtable := &io.Stream_VTable{
+	impl_destroy = proc(s: io.Stream) -> io.Error {
+		b := (^Reader)(s.stream_data);
+		reader_destroy(b);
+		return nil;
+	},
+	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Reader)(s.stream_data);
+		return reader_read(b, p);
+	},
+	impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
+		b := (^Reader)(s.stream_data);
+		return reader_read_byte(b);
+	},
+	impl_unread_byte = proc(s: io.Stream) -> io.Error {
+		b := (^Reader)(s.stream_data);
+		return reader_unread_byte(b);
+	},
+	impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+		b := (^Reader)(s.stream_data);
+		return reader_read_rune(b);
+	},
+	impl_unread_rune = proc(s: io.Stream) -> io.Error {
+		b := (^Reader)(s.stream_data);
+		return reader_unread_rune(b);
+	},
+	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+		b := (^Reader)(s.stream_data);
+		return reader_write_to(b, w);
+	},
+};
+
+
+
+//
+// Utility procedures
+//
+
+
+// reader_read_slice reads until the first occurrence of delim from the reader
+// It returns a slice pointing at the bytes in the buffer
+// The bytes stop being valid at the next read
+// If reader_read_slice encounters an error before finding a delimiter
+// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
+// Because the data returned from reader_read_slice will be overwritten on the
+// next IO operation, reader_read_bytes or reader_read_string is usually preferred
+//
+// reader_read_slice returns err != nil if and only if line does not end in delim
+//
+reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
+	s := 0;
+	for {
+		if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
+			i += s;
+			line = b.buf[b.r:][:i+1];
+			b.r += i + 1;
+			break;
+		}
+
+		if b.err != nil {
+			line = b.buf[b.r : b.w];
+			b.r = b.w;
+			err = _reader_consume_err(b);
+			break;
+		}
+
+		if reader_buffered(b) >= len(b.buf) {
+			b.r = b.w;
+			line = b.buf;
+			err = .Buffer_Full;
+			break;
+		}
+
+		s = b.w - b.r;
+
+		if err = _reader_read_new_chunk(b); err != nil {
+			break;
+		}
+	}
+
+	if i := len(line)-1; i >= 0 {
+		b.last_byte = int(line[i]);
+		b.last_rune_size = -1;
+	}
+
+	return;
+}
+
+// reader_read_bytes reads until the first occurrence of delim from the Reader
+// It returns an allocated slice containing the data up to and including the delimiter
+reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
+	full: [dynamic]byte;
+	full.allocator = allocator;
+
+	frag: []byte;
+	for {
+		e: io.Error;
+		frag, e = reader_read_slice(b, delim);
+		if e == nil {
+			break;
+		}
+		if e != .Buffer_Full {
+			err = e;
+			break;
+		}
+
+		append(&full, ..frag);
+	}
+	append(&full, ..frag);
+	return full[:], err;
+}
+
+// reader_read_string reads until the first occurrence of delim from the Reader
+// It returns an allocated string containing the data up to and including the delimiter
+reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
+	buf, err := reader_read_bytes(b, delim, allocator);
+	return string(buf), err;
+}

+ 255 - 0
core/bufio/writer.odin

@@ -0,0 +1,255 @@
+package bufio
+
+import "core:io"
+import "core:mem"
+import "core:unicode/utf8"
+// import "core:bytes"
+
+// Writer is a buffered wrapper for an io.Writer
+Writer :: struct {
+	buf:            []byte,
+	buf_allocator:  mem.Allocator,
+
+	wr: io.Writer,
+	n: int,
+
+	err: io.Error,
+
+}
+
+writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
+	size := size;
+	size = max(size, MIN_READ_BUFFER_SIZE);
+	writer_reset(b, wr);
+	b.buf_allocator = allocator;
+	b.buf = make([]byte, size, allocator);
+}
+
+writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
+	writer_reset(b, wr);
+	b.buf_allocator = {};
+	b.buf = buf;
+}
+
+// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
+writer_destroy :: proc(b: ^Writer) {
+	delete(b.buf, b.buf_allocator);
+	b^ = {};
+}
+
+// writer_size returns the size of underlying buffer in bytes
+writer_size :: proc(b: ^Writer) -> int {
+	return len(b.buf);
+}
+
+writer_reset :: proc(b: ^Writer, w: io.Writer) {
+	b.wr = w;
+	b.n = 0;
+	b.err = nil;
+}
+
+
+// writer_flush writes any buffered data into the underlying io.Writer
+writer_flush :: proc(b: ^Writer) -> io.Error {
+	if b.err != nil {
+		return b.err;
+	}
+	if b.n == 0 {
+		return nil;
+	}
+
+	n, err := io.write(b.wr, b.buf[0:b.n]);
+	if n < b.n && err == nil {
+		err = .Short_Write;
+	}
+	if err != nil {
+		if n > 0 && n < b.n {
+			copy(b.buf[:b.n-n], b.buf[n : b.n]);
+		}
+		b.n -= n;
+		b.err = err;
+		return err;
+	}
+	b.n = 0;
+	return nil;
+}
+
+// writer_available returns how many bytes are unused in the buffer
+writer_available :: proc(b: ^Writer) -> int {
+	return len(b.buf) - b.n;
+}
+
+// writer_buffered returns the number of bytes that have been writted into the current buffer
+writer_buffered :: proc(b: ^Writer) -> int {
+	return b.n;
+}
+
+// writer_write writes the contents of p into the buffer
+// It returns the number of bytes written
+// If n < len(p), it will return an error explaining why the write is short
+writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
+	p := p;
+	for len(p) > writer_available(b) && b.err == nil {
+		m: int;
+		if writer_buffered(b) == 0 {
+			m, b.err = io.write(b.wr, p);
+		} else {
+			m = copy(b.buf[b.n:], p);
+			b.n += m;
+			writer_flush(b);
+		}
+		n += m;
+		p = p[m:];
+	}
+	if b.err != nil {
+		return n, b.err;
+	}
+	m := copy(b.buf[b.n:], p);
+	b.n += m;
+	m += n;
+	return m, nil;
+}
+
+// writer_write_byte writes a single byte
+writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
+	if b.err != nil {
+		return b.err;
+	}
+	if writer_available(b) <= 0 && writer_flush(b) != nil {
+		return b.err;
+	}
+	b.buf[b.n] = c;
+	b.n += 1;
+	return nil;
+}
+
+// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
+writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
+	if r < utf8.RUNE_SELF {
+		err = writer_write_byte(b, byte(r));
+		size = 0 if err != nil else 1;
+		return;
+	}
+	if b.err != nil {
+		return 0, b.err;
+	}
+
+	buf: [4]u8;
+
+	n := writer_available(b);
+	if n < utf8.UTF_MAX {
+		writer_flush(b);
+		if b.err != nil {
+			return 0, b.err;
+		}
+		n = writer_available(b);
+		if n < utf8.UTF_MAX {
+			// this only happens if the buffer is very small
+			w: int;
+			buf, w = utf8.encode_rune(r);
+			return writer_write(b, buf[:w]);
+		}
+	}
+
+	buf, size = utf8.encode_rune(r);
+	copy(b.buf[b.n:], buf[:size]);
+	b.n += size;
+	return;
+}
+
+// writer_write writes a string into the buffer
+// It returns the number of bytes written
+// If n < len(p), it will return an error explaining why the write is short
+writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
+	return writer_write(b, transmute([]byte)s);
+}
+
+// writer_read_from is to support io.Reader_From types
+// If the underlying writer supports the io,read_from, and b has no buffered data yet,
+// this procedure calls the underlying read_from implementation without buffering
+writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
+	if b.err != nil {
+		return 0, b.err;
+	}
+	if writer_buffered(b) == 0 {
+		if w, cerr := io.to_reader_from(b.wr); cerr != nil {
+			n, err = io.read_from(w, r);
+			b.err = err;
+			return;
+		}
+	}
+
+	for {
+		if writer_available(b) == 0 {
+			if ferr := writer_flush(b); ferr != nil {
+				return n, ferr;
+			}
+		}
+		m: int;
+		nr := 0;
+		for nr < MAX_CONSECUTIVE_EMPTY_READS {
+			m, err = io.read(r, b.buf[b.n:]);
+			if m != 0 || err != nil {
+				break;
+			}
+			nr += 1;
+		}
+		if nr == MAX_CONSECUTIVE_EMPTY_READS {
+			return n, .No_Progress;
+		}
+		b.n += m;
+		n += i64(m);
+		if err != nil {
+			break;
+		}
+	}
+
+	if err == .EOF {
+		if writer_available(b) == 0 {
+			err = writer_flush(b);
+		} else {
+			err = nil;
+		}
+	}
+	return;
+}
+
+
+
+// writer_to_stream converts a Writer into an io.Stream
+writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
+	s.stream_data = b;
+	s.stream_vtable = _writer_vtable;
+	return;
+}
+
+
+
+@(private)
+_writer_vtable := &io.Stream_VTable{
+	impl_destroy = proc(s: io.Stream) -> io.Error {
+		b := (^Writer)(s.stream_data);
+		writer_destroy(b);
+		return nil;
+	},
+	impl_flush = proc(s: io.Stream)  -> io.Error {
+		b := (^Writer)(s.stream_data);
+		return writer_flush(b);
+	},
+	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Writer)(s.stream_data);
+		return writer_write(b, p);
+	},
+	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+		b := (^Writer)(s.stream_data);
+		return writer_write_byte(b, c);
+	},
+	impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+		b := (^Writer)(s.stream_data);
+		return writer_write_rune(b, r);
+	},
+	impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
+		b := (^Writer)(s.stream_data);
+		return writer_read_from(b, r);
+	},
+};

+ 335 - 0
core/bytes/buffer.odin

@@ -0,0 +1,335 @@
+package bytes
+
+import "core:io"
+import "core:unicode/utf8"
+
+MIN_READ :: 512;
+
+@(private)
+SMALL_BUFFER_SIZE :: 64;
+
+Buffer :: struct {
+	buf: [dynamic]byte,
+	off: int,
+	last_read: Read_Op,
+}
+
+@(private)
+Read_Op :: enum i8 {
+	Read       = -1,
+	Invalid    =  0,
+	Read_Rune1 =  1,
+	Read_Rune2 =  2,
+	Read_Rune3 =  3,
+	Read_Rune4 =  4,
+}
+
+
+buffer_init :: proc(b: ^Buffer, buf: []byte) {
+	resize(&b.buf, len(buf));
+	copy(b.buf[:], buf);
+}
+
+buffer_init_string :: proc(b: ^Buffer, s: string) {
+	resize(&b.buf, len(s));
+	copy(b.buf[:], s);
+}
+
+
+buffer_destroy :: proc(b: ^Buffer) {
+	delete(b.buf);
+	buffer_reset(b);
+}
+
+buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
+	return b.buf[b.off:];
+}
+
+buffer_to_string :: proc(b: ^Buffer) -> string {
+	if b == nil {
+		return "<nil>";
+	}
+	return string(b.buf[b.off:]);
+}
+
+buffer_is_empty :: proc(b: ^Buffer) -> bool {
+	return len(b.buf) <= b.off;
+}
+
+buffer_length :: proc(b: ^Buffer) -> int {
+	return len(b.buf) - b.off;
+}
+
+buffer_capacity :: proc(b: ^Buffer) -> int {
+	return cap(b.buf);
+}
+
+buffer_reset :: proc(b: ^Buffer) {
+	clear(&b.buf);
+	b.off = 0;
+	b.last_read = .Invalid;
+}
+
+
+buffer_truncate :: proc(b: ^Buffer, n: int) {
+	if n == 0 {
+		buffer_reset(b);
+		return;
+	}
+	b.last_read = .Invalid;
+	if n < 0 || n > buffer_length(b) {
+		panic("bytes.truncate: truncation out of range");
+	}
+	resize(&b.buf, b.off+n);
+}
+
+@(private)
+_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
+	if l := len(b.buf); n <= cap(b.buf)-l {
+		resize(&b.buf, l+n);
+		return l, true;
+	}
+	return 0, false;
+}
+
+@(private)
+_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
+	m := buffer_length(b);
+	if m == 0 && b.off != 0 {
+		buffer_reset(b);
+	}
+	if i, ok := _buffer_try_grow(b, n); ok {
+		return i;
+	}
+	if b.buf == nil && n <= SMALL_BUFFER_SIZE {
+		b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
+		return 0;
+	}
+
+	c := cap(b.buf);
+	if n <= c/2 - m {
+		copy(b.buf[:], b.buf[b.off:]);
+	} else if c > max(int) - c - n {
+		panic("bytes.Buffer: too large");
+	} else {
+		resize(&b.buf, 2*c + n);
+		copy(b.buf[:], b.buf[b.off:]);
+	}
+	b.off = 0;
+	resize(&b.buf, m+n);
+	return m;
+}
+
+buffer_grow :: proc(b: ^Buffer, n: int) {
+	if n < 0 {
+		panic("bytes.buffer_grow: negative count");
+	}
+	m := _buffer_grow(b, n);
+	resize(&b.buf, m);
+}
+
+
+buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
+	b.last_read = .Invalid;
+	m, ok := _buffer_try_grow(b, len(p));
+	if !ok {
+		m = _buffer_grow(b, len(p));
+	}
+	return copy(b.buf[m:], p), nil;
+}
+
+buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
+	b.last_read = .Invalid;
+	m, ok := _buffer_try_grow(b, len(s));
+	if !ok {
+		m = _buffer_grow(b, len(s));
+	}
+	return copy(b.buf[m:], s), nil;
+}
+
+buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
+	b.last_read = .Invalid;
+	m, ok := _buffer_try_grow(b, 1);
+	if !ok {
+		m = _buffer_grow(b, 1);
+	}
+	b.buf[m] = c;
+	return nil;
+}
+
+buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
+	if r < utf8.RUNE_SELF {
+		buffer_write_byte(b, byte(r));
+		return 1, nil;
+	}
+	b.last_read = .Invalid;
+	m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
+	if !ok {
+		m = _buffer_grow(b, utf8.UTF_MAX);
+	}
+	res: [4]byte;
+	res, n = utf8.encode_rune(r);
+	copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
+	resize(&b.buf, m+n);
+	return;
+}
+
+buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
+	n := n;
+	b.last_read = .Invalid;
+	m := buffer_length(b);
+	if n > m {
+		n = m;
+	}
+	data := b.buf[b.off : b.off + n];
+	b.off += n;
+	if n > 0 {
+		b.last_read = .Read;
+	}
+	return data;
+}
+
+buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
+	b.last_read = .Invalid;
+	if buffer_is_empty(b) {
+		buffer_reset(b);
+		if len(p) == 0 {
+			return 0, nil;
+		}
+		return 0, .EOF;
+	}
+	n = copy(p, b.buf[b.off:]);
+	b.off += n;
+	if n > 0 {
+		b.last_read = .Read;
+	}
+	return;
+}
+
+buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
+	if buffer_is_empty(b) {
+		buffer_reset(b);
+		return 0, .EOF;
+	}
+	c := b.buf[b.off];
+	b.off += 1;
+	b.last_read = .Read;
+	return c, nil;
+}
+
+buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
+	if buffer_is_empty(b) {
+		buffer_reset(b);
+		return 0, 0, .EOF;
+	}
+	c := b.buf[b.off];
+	if c < utf8.RUNE_SELF {
+		b.off += 1;
+		b.last_read = .Read_Rune1;
+		return rune(c), 1, nil;
+	}
+	r, size = utf8.decode_rune(b.buf[b.off:]);
+	b.off += size;
+	b.last_read = Read_Op(i8(size));
+	return;
+}
+
+buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
+	if b.last_read == .Invalid {
+		return .Invalid_Unread;
+	}
+	b.last_read = .Invalid;
+	if b.off > 0 {
+		b.off -= 1;
+	}
+	return nil;
+}
+
+buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
+	if b.last_read <= .Invalid {
+		return .Invalid_Unread;
+	}
+	if b.off >= int(b.last_read) {
+		b.off -= int(i8(b.last_read));
+	}
+	b.last_read = .Invalid;
+	return nil;
+}
+
+
+buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
+	i := index_byte(b.buf[b.off:], delim);
+	end := b.off + i + 1;
+	if i < 0 {
+		end = len(b.buf);
+		err = .EOF;
+	}
+	line = b.buf[b.off:end];
+	b.off = end;
+	b.last_read = .Read;
+	return;
+}
+
+buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
+	slice: []byte;
+	slice, err = buffer_read_bytes(b, delim);
+	return string(slice), err;
+}
+
+
+
+buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
+	s.stream_data = b;
+	s.stream_vtable = _buffer_vtable;
+	return;
+}
+
+@(private)
+_buffer_vtable := &io.Stream_VTable{
+	impl_size = proc(s: io.Stream) -> i64 {
+		b := (^Buffer)(s.stream_data);
+		return i64(buffer_capacity(b));
+	},
+	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Buffer)(s.stream_data);
+		return buffer_read(b, p);
+	},
+	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+		b := (^Buffer)(s.stream_data);
+		return buffer_read_byte(b);
+	},
+	impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+		b := (^Buffer)(s.stream_data);
+		return buffer_read_rune(b);
+	},
+	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Buffer)(s.stream_data);
+		return buffer_write(b, p);
+	},
+	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+		b := (^Buffer)(s.stream_data);
+		return buffer_write_byte(b, c);
+	},
+	impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+		b := (^Buffer)(s.stream_data);
+		return buffer_write_rune(b, r);
+	},
+	impl_unread_byte = proc(s: io.Stream) -> io.Error {
+		b := (^Buffer)(s.stream_data);
+		return buffer_unread_byte(b);
+	},
+	impl_unread_rune = proc(s: io.Stream) -> io.Error {
+		b := (^Buffer)(s.stream_data);
+		return buffer_unread_rune(b);
+	},
+	impl_destroy = proc(s: io.Stream) -> io.Error {
+		b := (^Buffer)(s.stream_data);
+		buffer_destroy(b);
+		return nil;
+	},
+
+	// TODO(bill): write_to and read_from
+	// impl_write_to = nil,
+	// impl_read_from = nil,
+};
+

+ 177 - 0
core/bytes/reader.odin

@@ -0,0 +1,177 @@
+package bytes
+
+import "core:io"
+import "core:unicode/utf8"
+
+Reader :: struct {
+	s:         []byte, // read-only buffer
+	i:         i64,    // current reading index
+	prev_rune: int,    // previous reading index of rune or < 0
+}
+
+reader_init :: proc(r: ^Reader, s: []byte) {
+	r.s = s;
+	r.i = 0;
+	r.prev_rune = -1;
+}
+
+reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
+	s.stream_data = r;
+	s.stream_vtable = _reader_vtable;
+	return;
+}
+
+reader_length :: proc(r: ^Reader) -> int {
+	if r.i >= i64(len(r.s)) {
+		return 0;
+	}
+	return int(i64(len(r.s)) - r.i);
+}
+
+reader_size :: proc(r: ^Reader) -> i64 {
+	return i64(len(r.s));
+}
+
+reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+	if r.i >= i64(len(r.s)) {
+		return 0, .EOF;
+	}
+	r.prev_rune = -1;
+	n = copy(p, r.s[r.i:]);
+	r.i += i64(n);
+	return;
+}
+reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
+	if off < 0 {
+		return 0, .Invalid_Offset;
+	}
+	if off >= i64(len(r.s)) {
+		return 0, .EOF;
+	}
+	n = copy(p, r.s[off:]);
+	if n < len(p) {
+		err = .EOF;
+	}
+	return;
+}
+reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
+	r.prev_rune = -1;
+	if r.i >= i64(len(r.s)) {
+		return 0, .EOF;
+	}
+	b := r.s[r.i];
+	r.i += 1;
+	return b, nil;
+}
+reader_unread_byte :: proc(r: ^Reader) -> io.Error {
+	if r.i <= 0 {
+		return .Invalid_Unread;
+	}
+	r.prev_rune = -1;
+	r.i -= 1;
+	return nil;
+}
+reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
+	if r.i >= i64(len(r.s)) {
+		r.prev_rune = -1;
+		return 0, 0, .EOF;
+	}
+	r.prev_rune = int(r.i);
+	if c := r.s[r.i]; c < utf8.RUNE_SELF {
+		r.i += 1;
+		return rune(c), 1, nil;
+	}
+	ch, size = utf8.decode_rune(r.s[r.i:]);
+	r.i += i64(size);
+	return;
+}
+reader_unread_rune :: proc(r: ^Reader) -> io.Error {
+	if r.i <= 0 {
+		return .Invalid_Unread;
+	}
+	if r.prev_rune < 0 {
+		return .Invalid_Unread;
+	}
+	r.i = i64(r.prev_rune);
+	r.prev_rune = -1;
+	return nil;
+}
+reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+	r.prev_rune = -1;
+	abs: i64;
+	switch whence {
+	case .Start:
+		abs = offset;
+	case .Current:
+		abs = r.i + offset;
+	case .End:
+		abs = i64(len(r.s)) + offset;
+	case:
+		return 0, .Invalid_Whence;
+	}
+
+	if abs < 0 {
+		return 0, .Invalid_Offset;
+	}
+	r.i = abs;
+	return abs, nil;
+}
+reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
+	r.prev_rune = -1;
+	if r.i >= i64(len(r.s)) {
+		return 0, nil;
+	}
+	s := r.s[r.i:];
+	m: int;
+	m, err = io.write(w, s);
+	if m > len(s) {
+		panic("bytes.Reader.write_to: invalid io.write_string count");
+	}
+	r.i += i64(m);
+	n = i64(m);
+	if m != len(s) && err == nil {
+		err = .Short_Write;
+	}
+	return;
+}
+
+
+@(private)
+_reader_vtable := &io.Stream_VTable{
+	impl_size = proc(s: io.Stream) -> i64 {
+		r := (^Reader)(s.stream_data);
+		return reader_size(r);
+	},
+	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read(r, p);
+	},
+	impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read_at(r, p, off);
+	},
+	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read_byte(r);
+	},
+	impl_unread_byte = proc(s: io.Stream) -> io.Error {
+		r := (^Reader)(s.stream_data);
+		return reader_unread_byte(r);
+	},
+	impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read_rune(r);
+	},
+	impl_unread_rune = proc(s: io.Stream) -> io.Error {
+		r := (^Reader)(s.stream_data);
+		return reader_unread_rune(r);
+	},
+	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_seek(r, offset, whence);
+	},
+	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_write_to(r, w);
+	},
+};

+ 1032 - 0
core/bytes/strings.odin

@@ -0,0 +1,1032 @@
+package bytes
+
+import "core:mem"
+import "core:unicode"
+import "core:unicode/utf8"
+
+clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
+	c := make([]byte, len(s)+1, allocator, loc);
+	copy(c, s);
+	c[len(s)] = 0;
+	return c[:len(s)];
+}
+
+ptr_from_slice :: proc(str: []byte) -> ^byte {
+	d := transmute(mem.Raw_String)str;
+	return d.data;
+}
+
+// Compares two strings, returning a value representing which one comes first lexiographically.
+// -1 for `a`; 1 for `b`, or 0 if they are equal.
+compare :: proc(lhs, rhs: []byte) -> int {
+	return mem.compare(lhs, rhs);
+}
+
+contains_rune :: proc(s: []byte, r: rune) -> int {
+	for c, offset in string(s) {
+		if c == r {
+			return offset;
+		}
+	}
+	return -1;
+}
+
+contains :: proc(s, substr: []byte) -> bool {
+	return index(s, substr) >= 0;
+}
+
+contains_any :: proc(s, chars: []byte) -> bool {
+	return index_any(s, chars) >= 0;
+}
+
+
+rune_count :: proc(s: []byte) -> int {
+	return utf8.rune_count(s);
+}
+
+
+equal :: proc(a, b: []byte) -> bool {
+	return string(a) == string(b);
+}
+
+equal_fold :: proc(u, v: []byte) -> bool {
+	s, t := string(u), string(v);
+	loop: for s != "" && t != "" {
+		sr, tr: rune;
+		if s[0] < utf8.RUNE_SELF {
+			sr, s = rune(s[0]), s[1:];
+		} else {
+			r, size := utf8.decode_rune_in_string(s);
+			sr, s = r, s[size:];
+		}
+		if t[0] < utf8.RUNE_SELF {
+			tr, t = rune(t[0]), t[1:];
+		} else {
+			r, size := utf8.decode_rune_in_string(t);
+			tr, t = r, t[size:];
+		}
+
+		if tr == sr { // easy case
+			continue loop;
+		}
+
+		if tr < sr {
+			tr, sr = sr, tr;
+		}
+
+		if tr < utf8.RUNE_SELF {
+			switch sr {
+			case 'A'..'Z':
+				if tr == (sr+'a')-'A' {
+					continue loop;
+				}
+			}
+			return false;
+		}
+
+		// TODO(bill): Unicode folding
+
+		return false;
+	}
+
+	return s == t;
+}
+
+has_prefix :: proc(s, prefix: []byte) -> bool {
+	return len(s) >= len(prefix) && string(s[0:len(prefix)]) == string(prefix);
+}
+
+has_suffix :: proc(s, suffix: []byte) -> bool {
+	return len(s) >= len(suffix) && string(s[len(s)-len(suffix):]) == string(suffix);
+}
+
+
+join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte {
+	if len(a) == 0 {
+		return nil;
+	}
+
+	n := len(sep) * (len(a) - 1);
+	for s in a {
+		n += len(s);
+	}
+
+	b := make([]byte, n, allocator);
+	i := copy(b, a[0]);
+	for s in a[1:] {
+		i += copy(b[i:], sep);
+		i += copy(b[i:], s);
+	}
+	return b;
+}
+
+concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
+	if len(a) == 0 {
+		return nil;
+	}
+
+	n := 0;
+	for s in a {
+		n += len(s);
+	}
+	b := make([]byte, n, allocator);
+	i := 0;
+	for s in a {
+		i += copy(b[i:], s);
+	}
+	return b;
+}
+
+@private
+_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
+	s, n := s, n;
+
+	if n == 0 {
+		return nil;
+	}
+
+	if sep == nil {
+		l := utf8.rune_count(s);
+		if n < 0 || n > l {
+			n = l;
+		}
+
+		res := make([dynamic][]byte, n, allocator);
+		for i := 0; i < n-1; i += 1 {
+			_, w := utf8.decode_rune(s);
+			res[i] = s[:w];
+			s = s[w:];
+		}
+		if n > 0 {
+			res[n-1] = s;
+		}
+		return res[:];
+	}
+
+	if n < 0 {
+		n = count(s, sep) + 1;
+	}
+
+	res := make([dynamic][]byte, n, allocator);
+
+	n -= 1;
+
+	i := 0;
+	for ; i < n; i += 1 {
+		m := index(s, sep);
+		if m < 0 {
+			break;
+		}
+		res[i] = s[:m+sep_save];
+		s = s[m+len(sep):];
+	}
+	res[i] = s;
+
+	return res[:i+1];
+}
+
+split :: inline proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
+	return _split(s, sep, 0, -1, allocator);
+}
+
+split_n :: inline proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
+	return _split(s, sep, 0, n, allocator);
+}
+
+split_after :: inline proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
+	return _split(s, sep, len(sep), -1, allocator);
+}
+
+split_after_n :: inline proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
+	return _split(s, sep, len(sep), n, allocator);
+}
+
+
+
+
+index_byte :: proc(s: []byte, c: byte) -> int {
+	for i := 0; i < len(s); i += 1 {
+		if s[i] == c {
+			return i;
+		}
+	}
+	return -1;
+}
+
+// Returns -1 if c is not present
+last_index_byte :: proc(s: []byte, c: byte) -> int {
+	for i := len(s)-1; i >= 0; i -= 1 {
+		if s[i] == c {
+			return i;
+		}
+	}
+	return -1;
+}
+
+
+
+@private PRIME_RABIN_KARP :: 16777619;
+
+index :: proc(s, substr: []byte) -> int {
+	hash_str_rabin_karp :: proc(s: []byte) -> (hash: u32 = 0, pow: u32 = 1) {
+		for i := 0; i < len(s); i += 1 {
+			hash = hash*PRIME_RABIN_KARP + u32(s[i]);
+		}
+		sq := u32(PRIME_RABIN_KARP);
+		for i := len(s); i > 0; i >>= 1 {
+			if (i & 1) != 0 {
+				pow *= sq;
+			}
+			sq *= sq;
+		}
+		return;
+	}
+
+	n := len(substr);
+	switch {
+	case n == 0:
+		return 0;
+	case n == 1:
+		return index_byte(s, substr[0]);
+	case n == len(s):
+		if string(s) == string(substr) {
+			return 0;
+		}
+		return -1;
+	case n > len(s):
+		return -1;
+	}
+
+	hash, pow := hash_str_rabin_karp(substr);
+	h: u32;
+	for i := 0; i < n; i += 1 {
+		h = h*PRIME_RABIN_KARP + u32(s[i]);
+	}
+	if h == hash && string(s[:n]) == string(substr) {
+		return 0;
+	}
+	for i := n; i < len(s); /**/ {
+		h *= PRIME_RABIN_KARP;
+		h += u32(s[i]);
+		h -= pow * u32(s[i-n]);
+		i += 1;
+		if h == hash && string(s[i-n:i]) == string(substr) {
+			return i - n;
+		}
+	}
+	return -1;
+}
+
+last_index :: proc(s, substr: []byte) -> int {
+	hash_str_rabin_karp_reverse :: proc(s: []byte) -> (hash: u32 = 0, pow: u32 = 1) {
+		for i := len(s) - 1; i >= 0; i -= 1 {
+			hash = hash*PRIME_RABIN_KARP + u32(s[i]);
+		}
+		sq := u32(PRIME_RABIN_KARP);
+		for i := len(s); i > 0; i >>= 1 {
+			if (i & 1) != 0 {
+				pow *= sq;
+			}
+			sq *= sq;
+		}
+		return;
+	}
+
+	n := len(substr);
+	switch {
+	case n == 0:
+		return len(s);
+	case n == 1:
+		return last_index_byte(s, substr[0]);
+	case n == len(s):
+		return 0 if string(substr) == string(s) else -1;
+	case n > len(s):
+		return -1;
+	}
+
+	hash, pow := hash_str_rabin_karp_reverse(substr);
+	last := len(s) - n;
+	h: u32;
+	for i := len(s)-1; i >= last; i -= 1 {
+		h = h*PRIME_RABIN_KARP + u32(s[i]);
+	}
+	if h == hash && string(s[last:]) == string(substr) {
+		return last;
+	}
+
+	for i := last-1; i >= 0; i -= 1 {
+		h *= PRIME_RABIN_KARP;
+		h += u32(s[i]);
+		h -= pow * u32(s[i+n]);
+		if h == hash && string(s[i:i+n]) == string(substr) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+index_any :: proc(s, chars: []byte) -> int {
+	if chars == nil {
+		return -1;
+	}
+
+	// TODO(bill): Optimize
+	for r, i in s {
+		for c in chars {
+			if r == c {
+				return i;
+			}
+		}
+	}
+	return -1;
+}
+
+last_index_any :: proc(s, chars: []byte) -> int {
+	if chars == nil {
+		return -1;
+	}
+
+	for i := len(s); i > 0;  {
+		r, w := utf8.decode_last_rune(s[:i]);
+		i -= w;
+		for c in string(chars) {
+			if r == c {
+				return i;
+			}
+		}
+	}
+	return -1;
+}
+
+count :: proc(s, substr: []byte) -> int {
+	if len(substr) == 0 { // special case
+		return rune_count(s) + 1;
+	}
+	if len(substr) == 1 {
+		c := substr[0];
+		switch len(s) {
+		case 0:
+			return 0;
+		case 1:
+			return int(s[0] == c);
+		}
+		n := 0;
+		for i := 0; i < len(s); i += 1 {
+			if s[i] == c {
+				n += 1;
+			}
+		}
+		return n;
+	}
+
+	// TODO(bill): Use a non-brute for approach
+	n := 0;
+	str := s;
+	for {
+		i := index(str, substr);
+		if i == -1 {
+			return n;
+		}
+		n += 1;
+		str = str[i+len(substr):];
+	}
+	return n;
+}
+
+
+repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte {
+	if count < 0 {
+		panic("bytes: negative repeat count");
+	} else if count > 0 && (len(s)*count)/count != len(s) {
+		panic("bytes: repeat count will cause an overflow");
+	}
+
+	b := make([]byte, len(s)*count, allocator);
+	i := copy(b, s);
+	for i < len(b) { // 2^N trick to reduce the need to copy
+		copy(b[i:], b[:i]);
+		i *= 2;
+	}
+	return b;
+}
+
+replace_all :: proc(s, old, new: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
+	return replace(s, old, new, -1, allocator);
+}
+
+// if n < 0, no limit on the number of replacements
+replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
+	if string(old) == string(new) || n == 0 {
+		was_allocation = false;
+		output = s;
+		return;
+	}
+	byte_count := n;
+	if m := count(s, old); m == 0 {
+		was_allocation = false;
+		output = s;
+		return;
+	} else if n < 0 || m < n {
+		byte_count = m;
+	}
+
+
+	t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator);
+	was_allocation = true;
+
+	w := 0;
+	start := 0;
+	for i := 0; i < byte_count; i += 1 {
+		j := start;
+		if len(old) == 0 {
+			if i > 0 {
+				_, width := utf8.decode_rune(s[start:]);
+				j += width;
+			}
+		} else {
+			j += index(s[start:], old);
+		}
+		w += copy(t[w:], s[start:j]);
+		w += copy(t[w:], new);
+		start = j + len(old);
+	}
+	w += copy(t[w:], s[start:]);
+	output = t[0:w];
+	return;
+}
+
+@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+
+
+is_ascii_space :: proc(r: rune) -> bool {
+	if r < utf8.RUNE_SELF {
+		return _ascii_space[u8(r)] != 0;
+	}
+	return false;
+}
+
+is_space :: proc(r: rune) -> bool {
+	if r < 0x2000 {
+		switch r {
+		case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xa0, 0x1680:
+			return true;
+		}
+	} else {
+		if r <= 0x200a {
+			return true;
+		}
+		switch r {
+		case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
+			return true;
+		}
+	}
+	return false;
+}
+
+is_null :: proc(r: rune) -> bool {
+	return r == 0x0000;
+}
+
+index_proc :: proc(s: []byte, p: proc(rune) -> bool, truth := true) -> int {
+	for r, i in string(s) {
+		if p(r) == truth {
+			return i;
+		}
+	}
+	return -1;
+}
+
+index_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
+	for r, i in string(s) {
+		if p(state, r) == truth {
+			return i;
+		}
+	}
+	return -1;
+}
+
+last_index_proc :: proc(s: []byte, p: proc(rune) -> bool, truth := true) -> int {
+	// TODO(bill): Probably use Rabin-Karp Search
+	for i := len(s); i > 0; {
+		r, size := utf8.decode_last_rune(s[:i]);
+		i -= size;
+		if p(r) == truth {
+			return i;
+		}
+	}
+	return -1;
+}
+
+last_index_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
+	// TODO(bill): Probably use Rabin-Karp Search
+	for i := len(s); i > 0; {
+		r, size := utf8.decode_last_rune(s[:i]);
+		i -= size;
+		if p(state, r) == truth {
+			return i;
+		}
+	}
+	return -1;
+}
+
+trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
+	i := index_proc(s, p, false);
+	if i == -1 {
+		return nil;
+	}
+	return s[i:];
+}
+
+
+index_rune :: proc(s: []byte, r: rune) -> int {
+	switch {
+	case 0 <= r && r < utf8.RUNE_SELF:
+		return index_byte(s, byte(r));
+
+	case r == utf8.RUNE_ERROR:
+		for c, i in string(s) {
+			if c == utf8.RUNE_ERROR {
+				return i;
+			}
+		}
+		return -1;
+
+	case !utf8.valid_rune(r):
+		return -1;
+	}
+
+	b, w := utf8.encode_rune(r);
+	return index(s, b[:w]);
+}
+
+
+trim_left_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr) -> []byte {
+	i := index_proc_with_state(s, p, state, false);
+	if i == -1 {
+		return nil;
+	}
+	return s[i:];
+}
+
+trim_right_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
+	i := last_index_proc(s, p, false);
+	if i >= 0 && s[i] >= utf8.RUNE_SELF {
+		_, w := utf8.decode_rune(s[i:]);
+		i += w;
+	} else {
+		i += 1;
+	}
+	return s[0:i];
+}
+
+trim_right_proc_with_state :: proc(s: []byte, p: proc(rawptr, rune) -> bool, state: rawptr) -> []byte {
+	i := last_index_proc_with_state(s, p, state, false);
+	if i >= 0 && s[i] >= utf8.RUNE_SELF {
+		_, w := utf8.decode_rune(s[i:]);
+		i += w;
+	} else {
+		i += 1;
+	}
+	return s[0:i];
+}
+
+
+is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
+	if state == nil {
+		return false;
+	}
+	cutset := (^string)(state)^;
+	for c in cutset {
+		if r == c {
+			return true;
+		}
+	}
+	return false;
+}
+
+
+trim_left :: proc(s: []byte, cutset: []byte) -> []byte {
+	if s == nil || cutset == nil {
+		return s;
+	}
+	state := cutset;
+	return trim_left_proc_with_state(s, is_in_cutset, &state);
+}
+
+trim_right :: proc(s: []byte, cutset: []byte) -> []byte {
+	if s == nil || cutset == nil {
+		return s;
+	}
+	state := cutset;
+	return trim_right_proc_with_state(s, is_in_cutset, &state);
+}
+
+trim :: proc(s: []byte, cutset: []byte) -> []byte {
+	return trim_right(trim_left(s, cutset), cutset);
+}
+
+trim_left_space :: proc(s: []byte) -> []byte {
+	return trim_left_proc(s, is_space);
+}
+
+trim_right_space :: proc(s: []byte) -> []byte {
+	return trim_right_proc(s, is_space);
+}
+
+trim_space :: proc(s: []byte) -> []byte {
+	return trim_right_space(trim_left_space(s));
+}
+
+
+trim_left_null :: proc(s: []byte) -> []byte {
+	return trim_left_proc(s, is_null);
+}
+
+trim_right_null :: proc(s: []byte) -> []byte {
+	return trim_right_proc(s, is_null);
+}
+
+trim_null :: proc(s: []byte) -> []byte {
+	return trim_right_null(trim_left_null(s));
+}
+
+trim_prefix :: proc(s, prefix: []byte) -> []byte {
+	if has_prefix(s, prefix) {
+		return s[len(prefix):];
+	}
+	return s;
+}
+
+trim_suffix :: proc(s, suffix: []byte) -> []byte {
+	if has_suffix(s, suffix) {
+		return s[:len(s)-len(suffix)];
+	}
+	return s;
+}
+
+split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator) -> [][]byte #no_bounds_check {
+	if s == nil || len(substrs) <= 0 {
+		return nil;
+	}
+
+	sublen := len(substrs[0]);
+
+	for substr in substrs[1:] {
+		sublen = min(sublen, len(substr));
+	}
+
+	shared := len(s) - sublen;
+
+	if shared <= 0 {
+		return nil;
+	}
+
+	// number, index, last
+	n, i, l := 0, 0, 0;
+
+	// count results
+	first_pass: for i <= shared {
+		for substr in substrs {
+			if string(s[i:i+sublen]) == string(substr) {
+				if !skip_empty || i - l > 0 {
+					n += 1;
+				}
+
+				i += sublen;
+				l  = i;
+
+				continue first_pass;
+			}
+		}
+
+		_, skip := utf8.decode_rune(s[i:]);
+		i += skip;
+	}
+
+	if !skip_empty || len(s) - l > 0 {
+		n += 1;
+	}
+
+	if n < 1 {
+		// no results
+		return nil;
+	}
+
+	buf := make([][]byte, n, allocator);
+
+	n, i, l = 0, 0, 0;
+
+	// slice results
+	second_pass: for i <= shared {
+		for substr in substrs {
+			if string(s[i:i+sublen]) == string(substr) {
+				if !skip_empty || i - l > 0 {
+					buf[n] = s[l:i];
+					n += 1;
+				}
+
+				i += sublen;
+				l  = i;
+
+				continue second_pass;
+			}
+		}
+
+		_, skip := utf8.decode_rune(s[i:]);
+		i += skip;
+	}
+
+	if !skip_empty || len(s) - l > 0 {
+		buf[n] = s[l:];
+	}
+
+	return buf;
+}
+
+/*
+// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
+// Adjacent invalid bytes are only replaced once
+scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
+	str := s;
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+
+	has_error := false;
+	cursor := 0;
+	origin := str;
+
+	for len(str) > 0 {
+		r, w := utf8.decode_rune(str);
+
+		if r == utf8.RUNE_ERROR {
+			if !has_error {
+				has_error = true;
+				write(&b, origin[:cursor]);
+			}
+		} else if has_error {
+			has_error = false;
+			write(&b, replacement);
+
+			origin = origin[cursor:];
+			cursor = 0;
+		}
+
+		cursor += w;
+		str = str[w:];
+	}
+
+	return to_string(b);
+}
+*/
+
+
+reverse :: proc(s: []byte, allocator := context.allocator) -> []byte {
+	str := s;
+	n := len(str);
+	buf := make([]byte, n);
+	i := n;
+
+	for len(str) > 0 {
+		_, w := utf8.decode_rune(str);
+		i -= w;
+		copy(buf[i:], str[:w]);
+		str = str[w:];
+	}
+	return buf;
+}
+
+/*
+expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
+	if tab_size <= 0 {
+		panic("tab size must be positive");
+	}
+
+
+	if s == nil {
+		return nil;
+	}
+
+	b: Builder;
+	init_builder(&b, allocator);
+	writer := to_writer(&b);
+	str := s;
+	column: int;
+
+	for len(str) > 0 {
+		r, w := utf8.decode_rune_in_string(str);
+
+		if r == '\t' {
+			expand := tab_size - column%tab_size;
+
+			for i := 0; i < expand; i += 1 {
+				io.write_byte(writer, ' ');
+			}
+
+			column += expand;
+		} else {
+			if r == '\n' {
+				column = 0;
+			} else {
+				column += w;
+			}
+
+			io.write_rune(writer, r);
+		}
+
+		str = str[w:];
+	}
+
+	return to_string(b);
+}
+*/
+
+partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) {
+	i := index(str, sep);
+	if i == -1 {
+		head = str;
+		return;
+	}
+
+	head = str[:i];
+	match = str[i:i+len(sep)];
+	tail = str[i+len(sep):];
+	return;
+}
+
+/*
+center_justify :: centre_justify; // NOTE(bill): Because Americans exist
+
+// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length
+centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+	n := rune_count(str);
+	if n >= length || pad == nil {
+		return clone(str, allocator);
+	}
+
+	remains := length-1;
+	pad_len := rune_count(pad);
+
+	b: Builder;
+	init_builder(&b, allocator);
+	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+	w := to_writer(&b);
+
+	write_pad_string(w, pad, pad_len, remains/2);
+	io.write_string(w, str);
+	write_pad_string(w, pad, pad_len, (remains+1)/2);
+
+	return to_string(b);
+}
+
+// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length
+left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+	n := rune_count(str);
+	if n >= length || pad == nil {
+		return clone(str, allocator);
+	}
+
+	remains := length-1;
+	pad_len := rune_count(pad);
+
+	b: Builder;
+	init_builder(&b, allocator);
+	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+	w := to_writer(&b);
+
+	io.write_string(w, str);
+	write_pad_string(w, pad, pad_len, remains);
+
+	return to_string(b);
+}
+
+// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length
+right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+	n := rune_count(str);
+	if n >= length || pad == nil {
+		return clone(str, allocator);
+	}
+
+	remains := length-1;
+	pad_len := rune_count(pad);
+
+	b: Builder;
+	init_builder(&b, allocator);
+	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+	w := to_writer(&b);
+
+	write_pad_string(w, pad, pad_len, remains);
+	io.write_string(w, str);
+
+	return to_string(b);
+}
+
+
+
+
+@private
+write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
+	repeats := remains / pad_len;
+
+	for i := 0; i < repeats; i += 1 {
+		io.write_string(w, pad);
+	}
+
+	n := remains % pad_len;
+	p := pad;
+
+	for i := 0; i < n; i += 1 {
+		r, width := utf8.decode_rune_in_string(p);
+		io.write_rune(w, r);
+		p = p[width:];
+	}
+}
+*/
+
+
+// fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space
+// returning a slice of subslices of s or an empty slice if s only contains white space
+fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds_check {
+	n := 0;
+	was_space := 1;
+	set_bits := u8(0);
+
+	// check to see
+	for i in 0..<len(s) {
+		r := s[i];
+		set_bits |= r;
+		is_space := int(_ascii_space[r]);
+		n += was_space & ~is_space;
+		was_space = is_space;
+	}
+
+	if set_bits >= utf8.RUNE_SELF {
+		return fields_proc(s, unicode.is_space, allocator);
+	}
+
+	if n == 0 {
+		return nil;
+	}
+
+	a := make([][]byte, n, allocator);
+	na := 0;
+	field_start := 0;
+	i := 0;
+	for i < len(s) && _ascii_space[s[i]] != 0 {
+		i += 1;
+	}
+	field_start = i;
+	for i < len(s) {
+		if _ascii_space[s[i]] == 0 {
+			i += 1;
+			continue;
+		}
+		a[na] = s[field_start : i];
+		na += 1;
+		i += 1;
+		for i < len(s) && _ascii_space[s[i]] != 0 {
+			i += 1;
+		}
+		field_start = i;
+	}
+	if field_start < len(s) {
+		a[na] = s[field_start:];
+	}
+	return a;
+}
+
+
+// fields_proc splits the byte slice s at each run of unicode code points `ch` satisfying f(ch)
+// returns a slice of subslices of s
+// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
+//
+// fields_proc makes no guarantee about the order in which it calls f(ch)
+// it assumes that `f` always returns the same value for a given ch
+fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator) -> [][]byte #no_bounds_check {
+	subslices := make([dynamic][]byte, 0, 32, allocator);
+
+	start, end := -1, -1;
+	for r, offset in string(s) {
+		end = offset;
+		if f(r) {
+			if start >= 0 {
+				append(&subslices, s[start : end]);
+				// -1 could be used, but just speed it up through bitwise not
+				// gotta love 2's complement
+				start = ~start;
+			}
+		} else {
+			if start < 0 {
+				start = end;
+			}
+		}
+	}
+
+	if start >= 0 {
+		append(&subslices, s[start : end]);
+	}
+
+	return subslices[:];
+}

+ 1 - 1
core/dynlib/lib.odin

@@ -1,3 +1,3 @@
 package dynlib
 
-Library :: opaque rawptr;
+Library :: #opaque rawptr;

+ 7 - 8
core/encoding/json/marshal.odin

@@ -14,7 +14,8 @@ Marshal_Error :: enum {
 }
 
 marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
-	b := strings.make_builder(allocator);
+	b: strings.Builder;
+	strings.init_builder(&b, allocator);
 
 	err := marshal_arg(&b, v);
 
@@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 		case b32:  val = bool(b);
 		case b64:  val = bool(b);
 		}
-		write_string(b, val ? "true" : "false");
+		write_string_builder(b, val ? "true" : "false");
 
 	case Type_Info_Any:
 		return .Unsupported_Type;
@@ -208,14 +209,12 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 				if i > 0 { write_string(b, ", "); }
 
 				data := uintptr(entries.data) + uintptr(i*entry_size);
-				header := cast(^Map_Entry_Header)data;
-
-				marshal_arg(b, any{rawptr(&header.key.key.val), info.key.id});
+				key   := rawptr(data + entry_type.offsets[2]);
+				value := rawptr(data + entry_type.offsets[3]);
 
+				marshal_arg(b, any{key, info.key.id});
 				write_string(b, ": ");
-
-				value := data + entry_type.offsets[2];
-				marshal_arg(b, any{rawptr(value), info.value.id});
+				marshal_arg(b, any{value, info.value.id});
 			}
 		}
 		write_byte(b, '}');

Fișier diff suprimat deoarece este prea mare
+ 241 - 202
core/fmt/fmt.odin


+ 5 - 1
core/intrinsics/intrinsics.odin

@@ -1,4 +1,5 @@
 // This is purely for documentation
+//+ignore
 package intrinsics
 
 // Types
@@ -114,7 +115,7 @@ type_is_ordered_numeric :: proc($T: typeid) -> bool ---
 type_is_indexable       :: proc($T: typeid) -> bool ---
 type_is_sliceable       :: proc($T: typeid) -> bool ---
 type_is_comparable      :: proc($T: typeid) -> bool ---
-type_is_simple_compare  :: proc($T: typeid) -> bool --- // easily compared using memcmp
+type_is_simple_compare  :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
 type_is_dereferenceable :: proc($T: typeid) -> bool ---
 type_is_valid_map_key   :: proc($T: typeid) -> bool ---
 
@@ -152,3 +153,6 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
 
 
 type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
+
+type_equal_proc  :: proc($T: typeid) -> (equal:  proc "contextless" (rawptr, rawptr) -> bool) ---
+type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---

+ 200 - 0
core/io/conv.odin

@@ -0,0 +1,200 @@
+package io
+
+Conversion_Error :: enum {
+	None,
+	Missing_Procedure,
+	Fallback_Possible,
+}
+
+to_reader :: proc(s: Stream) -> (r: Reader, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+
+to_closer :: proc(s: Stream) -> (c: Closer, err: Conversion_Error) {
+	c.stream = s;
+	if s.stream_vtable == nil || s.impl_close == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_flusher :: proc(s: Stream) -> (f: Flusher, err: Conversion_Error) {
+	f.stream = s;
+	if s.stream_vtable == nil || s.impl_flush == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) {
+	seeker.stream = s;
+	if s.stream_vtable == nil || s.impl_seek == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+
+to_read_writer :: proc(s: Stream) -> (r: Read_Writer, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_read_closer :: proc(s: Stream) -> (r: Read_Closer, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+
+to_reader_at :: proc(s: Stream) -> (r: Reader_At, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read_at == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_writer_at :: proc(s: Stream) -> (w: Writer_At, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write_at == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_reader_from :: proc(s: Stream) -> (r: Reader_From, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read_from == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write_to == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_write_closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) {
+	w.stream = s;
+	if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
+		err = .Missing_Procedure;
+	}
+	return;
+}
+
+
+to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) {
+	b.stream = s;
+	if s.stream_vtable == nil || s.impl_read_byte == nil {
+		err = .Missing_Procedure;
+		if s.stream_vtable != nil && s.impl_read != nil {
+			err = .Fallback_Possible;
+		}
+	}
+	return;
+}
+to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) {
+	b.stream = s;
+	if s.stream_vtable != nil {
+		if s.impl_unread_byte == nil {
+			err = .Missing_Procedure;
+			return;
+		}
+		if s.impl_read_byte != nil {
+			err = .None;
+		} else if s.impl_read != nil {
+			err = .Fallback_Possible;
+		} else {
+			err = .Missing_Procedure;
+		}
+	}
+	return;
+}
+to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) {
+	b.stream = s;
+	if s.stream_vtable == nil || s.impl_write_byte == nil {
+		err = .Missing_Procedure;
+		if s.stream_vtable != nil && s.impl_write != nil {
+			err = .Fallback_Possible;
+		}
+	}
+	return;
+}
+
+to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable == nil || s.impl_read_rune == nil {
+		err = .Missing_Procedure;
+		if s.stream_vtable != nil && s.impl_read != nil {
+			err = .Fallback_Possible;
+		}
+	}
+	return;
+
+}
+to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, err: Conversion_Error) {
+	r.stream = s;
+	if s.stream_vtable != nil {
+		if s.impl_unread_rune == nil {
+			err = .Missing_Procedure;
+			return;
+		}
+		if s.impl_read_rune != nil {
+			err = .None;
+		} else if s.impl_read != nil {
+			err = .Fallback_Possible;
+		} else {
+			err = .Missing_Procedure;
+		}
+	} else {
+		err = .Missing_Procedure;
+	}
+	return;
+}

+ 504 - 0
core/io/io.odin

@@ -0,0 +1,504 @@
+package io
+
+import "intrinsics"
+import "core:runtime"
+import "core:unicode/utf8"
+
+Seek_From :: enum {
+	Start   = 0, // seek relative to the origin of the file
+	Current = 1, // seek relative to the current offset
+	End     = 2, // seek relative to the end
+}
+
+Error :: enum i32 {
+	// No Error
+	None = 0,
+
+	// EOF is the error returned by `read` when no more input is available
+	EOF,
+
+	// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
+	Unexpected_EOF,
+
+	// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
+	Short_Write,
+
+	// Short_Buffer means that a read required a longer buffer than was provided
+	Short_Buffer,
+
+	// No_Progress is returned by some implementations of `io.Reader` when many calls
+	// to `read` have failed to return any data or error.
+	// This is usually a signed of a broken `io.Reader` implementation
+	No_Progress,
+
+	Invalid_Whence,
+	Invalid_Offset,
+	Invalid_Unread,
+
+	Negative_Read,
+	Negative_Write,
+	Negative_Count,
+	Buffer_Full,
+
+	// Empty is returned when a procedure has not been implemented for an io.Stream
+	Empty = -1,
+}
+
+Close_Proc       :: proc(using s: Stream) -> Error;
+Flush_Proc       :: proc(using s: Stream) -> Error;
+Seek_Proc        :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error);
+Size_Proc        :: proc(using s: Stream) -> i64;
+Read_Proc        :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
+Read_At_Proc     :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
+Read_From_Proc   :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error);
+Read_Byte_Proc   :: proc(using s: Stream) -> (byte, Error);
+Read_Rune_Proc   :: proc(using s: Stream) -> (ch: rune, size: int, err: Error);
+Unread_Byte_Proc :: proc(using s: Stream) -> Error;
+Unread_Rune_Proc :: proc(using s: Stream) -> Error;
+Write_Proc       :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
+Write_At_Proc    :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
+Write_To_Proc    :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error);
+Write_Byte_Proc  :: proc(using s: Stream, c: byte) -> Error;
+Write_Rune_Proc  :: proc(using s: Stream, r: rune) -> (size: int, err: Error);
+Destroy_Proc     :: proc(using s: Stream) -> Error;
+
+
+Stream :: struct {
+	using stream_vtable: ^Stream_VTable,
+	stream_data:         rawptr,
+}
+Stream_VTable :: struct {
+	impl_close: Close_Proc,
+	impl_flush: Flush_Proc,
+
+	impl_seek:  Seek_Proc,
+	impl_size:  Size_Proc,
+
+	impl_read:      Read_Proc,
+	impl_read_at:   Read_At_Proc,
+	impl_read_byte: Read_Byte_Proc,
+	impl_read_rune: Read_Rune_Proc,
+	impl_write_to:  Write_To_Proc,
+
+	impl_write:      Write_Proc,
+	impl_write_at:   Write_At_Proc,
+	impl_write_byte: Write_Byte_Proc,
+	impl_write_rune: Write_Rune_Proc,
+	impl_read_from:  Read_From_Proc,
+
+	impl_unread_byte: Unread_Byte_Proc,
+	impl_unread_rune: Unread_Rune_Proc,
+
+	impl_destroy: Destroy_Proc,
+}
+
+
+Reader             :: struct {using stream: Stream};
+Writer             :: struct {using stream: Stream};
+Closer             :: struct {using stream: Stream};
+Flusher            :: struct {using stream: Stream};
+Seeker             :: struct {using stream: Stream};
+
+Read_Writer        :: struct {using stream: Stream};
+Read_Closer        :: struct {using stream: Stream};
+Read_Write_Closer  :: struct {using stream: Stream};
+Read_Write_Seeker  :: struct {using stream: Stream};
+
+Write_Closer       :: struct {using stream: Stream};
+Write_Seeker       :: struct {using stream: Stream};
+Write_Flusher      :: struct {using stream: Stream};
+Write_Flush_Closer :: struct {using stream: Stream};
+
+Reader_At          :: struct {using stream: Stream};
+Writer_At          :: struct {using stream: Stream};
+Reader_From        :: struct {using stream: Stream};
+Writer_To          :: struct {using stream: Stream};
+
+Byte_Reader        :: struct {using stream: Stream};
+Byte_Scanner       :: struct {using stream: Stream};
+Byte_Writer        :: struct {using stream: Stream};
+
+Rune_Reader        :: struct {using stream: Stream};
+Rune_Scanner       :: struct {using stream: Stream};
+
+
+destroy :: proc(s: Stream) -> Error {
+	close_err := close({s});
+	if s.stream_vtable != nil && s.impl_destroy != nil {
+		return s->impl_destroy();
+	}
+	if close_err != .None {
+		return close_err;
+	}
+	return .Empty;
+}
+
+read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) {
+	if s.stream_vtable != nil && s.impl_read != nil {
+		return s->impl_read(p);
+	}
+	return 0, .Empty;
+}
+
+write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) {
+	if s.stream_vtable != nil && s.impl_write != nil {
+		return s->impl_write(p);
+	}
+	return 0, .Empty;
+}
+
+seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+	if s.stream_vtable != nil && s.impl_seek != nil {
+		return s->impl_seek(offset, whence);
+	}
+	return 0, .Empty;
+}
+
+close :: proc(s: Closer) -> Error {
+	if s.stream_vtable != nil && s.impl_close != nil {
+		return s->impl_close();
+	}
+	// Instead of .Empty, .None is fine in this case
+	return .None;
+}
+
+flush :: proc(s: Flusher) -> Error {
+	if s.stream_vtable != nil && s.impl_flush != nil {
+		return s->impl_flush();
+	}
+	// Instead of .Empty, .None is fine in this case
+	return .None;
+}
+
+size :: proc(s: Stream) -> i64 {
+	if s.stream_vtable == nil {
+		return 0;
+	}
+	if s.impl_size != nil {
+		return s->impl_size();
+	}
+	if s.impl_seek == nil {
+		return 0;
+	}
+
+	curr, end: i64;
+	err: Error;
+	if curr, err = s->impl_seek(0, .Current); err != nil {
+		return 0;
+	}
+
+	if end, err = s->impl_seek(0, .End); err != nil {
+		return 0;
+	}
+
+	if _, err = s->impl_seek(curr, .Start); err != nil {
+		return 0;
+	}
+
+	return end;
+}
+
+
+
+
+read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
+	if r.stream_vtable == nil {
+		return 0, .Empty;
+	}
+	if r.impl_read_at != nil {
+		return r->impl_read_at(p, offset);
+	}
+	if r.impl_seek == nil || r.impl_read == nil {
+		return 0, .Empty;
+	}
+
+	current_offset: i64;
+	current_offset, err = r->impl_seek(offset, .Current);
+	if err != nil {
+		return 0, err;
+	}
+
+	n, err = r->impl_read(p);
+	if err != nil {
+		return;
+	}
+	_, err = r->impl_seek(current_offset, .Start);
+	return;
+
+}
+
+write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
+	if w.stream_vtable == nil {
+		return 0, .Empty;
+	}
+	if w.impl_write_at != nil {
+		return w->impl_write_at(p, offset);
+	}
+	if w.impl_seek == nil || w.impl_write == nil {
+		return 0, .Empty;
+	}
+
+	current_offset: i64;
+	current_offset, err = w->impl_seek(offset, .Current);
+	if err != nil {
+		return 0, err;
+	}
+	defer w->impl_seek(current_offset, .Start);
+
+	return w->impl_write(p);
+}
+
+write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
+	if r.stream_vtable == nil || w.stream_vtable == nil {
+		return 0, .Empty;
+	}
+	if r.impl_write_to != nil {
+		return r->impl_write_to(w);
+	}
+	return 0, .Empty;
+}
+read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
+	if r.stream_vtable == nil || w.stream_vtable == nil {
+		return 0, .Empty;
+	}
+	if r.impl_read_from != nil {
+		return w->impl_read_from(r);
+	}
+	return 0, .Empty;
+}
+
+
+read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
+	if r.stream_vtable == nil {
+		return 0, .Empty;
+	}
+	if r.impl_read_byte != nil {
+		return r->impl_read_byte();
+	}
+	if r.impl_read == nil {
+		return 0, .Empty;
+	}
+
+	b: [1]byte;
+	_, err := r->impl_read(b[:]);
+	return b[0], err;
+}
+
+write_byte :: proc{
+	write_byte_to_byte_writer,
+	write_byte_to_writer,
+};
+
+write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error {
+	return _write_byte(auto_cast w, c);
+}
+
+write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
+	return _write_byte(auto_cast w, c);
+}
+
+@(private)
+_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
+	if w.stream_vtable == nil {
+		return .Empty;
+	}
+	if w.impl_write_byte != nil {
+		return w->impl_write_byte(c);
+	}
+	if w.impl_write == nil {
+		return .Empty;
+	}
+
+	b := [1]byte{c};
+	_, err := w->impl_write(b[:]);
+	return err;
+}
+
+read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) {
+	if br.stream_vtable == nil {
+		return 0, 0, .Empty;
+	}
+	if br.impl_read_rune != nil {
+		return br->impl_read_rune();
+	}
+	if br.impl_read == nil {
+		return 0, 0, .Empty;
+	}
+
+	b: [utf8.UTF_MAX]byte;
+	_, err = br->impl_read(b[:1]);
+
+	s0 := b[0];
+	ch = rune(s0);
+	size = 1;
+	if err != nil {
+		return;
+	}
+	if ch < utf8.RUNE_SELF {
+		return;
+	}
+	x := utf8.accept_sizes[s0];
+	if x >= 0xf0 {
+		mask := rune(x) << 31 >> 31;
+		ch = ch &~ mask | utf8.RUNE_ERROR&mask;
+		return;
+	}
+	sz := int(x&7);
+	n: int;
+	n, err = br->impl_read(b[1:sz]);
+	if err != nil || n+1 < sz {
+		ch = utf8.RUNE_ERROR;
+		return;
+	}
+
+	ch, size = utf8.decode_rune(b[:sz]);
+	return;
+}
+
+unread_byte :: proc(s: Byte_Scanner) -> Error {
+	if s.stream_vtable != nil && s.impl_unread_byte != nil {
+		return s->impl_unread_byte();
+	}
+	return .Empty;
+}
+unread_rune :: proc(s: Rune_Scanner) -> Error {
+	if s.stream_vtable != nil && s.impl_unread_rune != nil {
+		return s->impl_unread_rune();
+	}
+	return .Empty;
+}
+
+
+write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
+	return write(s, transmute([]byte)str);
+}
+
+write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) {
+	if s.stream_vtable != nil && s.impl_write_rune != nil {
+		return s->impl_write_rune(r);
+	}
+
+	if r < utf8.RUNE_SELF {
+		err = write_byte(s, byte(r));
+		if err == nil {
+			size = 1;
+		}
+		return;
+	}
+	buf, w := utf8.encode_rune(r);
+	return write(s, buf[:w]);
+}
+
+
+
+read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
+	return read_at_least(r, buf, len(buf));
+}
+
+
+read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
+	if len(buf) < min {
+		return 0, .Short_Buffer;
+	}
+	for n < min && err == nil {
+		nn: int;
+		nn, err = read(r, buf[n:]);
+		n += n;
+	}
+
+	if n >= min {
+		err = nil;
+	} else if n > 0 && err == .EOF {
+		err = .Unexpected_EOF;
+	}
+	return;
+}
+
+// copy copies from src to dst till either EOF is reached on src or an error occurs
+// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
+copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
+	return _copy_buffer(dst, src, nil);
+}
+
+// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required)
+// rather than allocating a temporary one on the stack through `intrinsics.alloca`
+// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic
+copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
+	if buf != nil && len(buf) == 0 {
+		panic("empty buffer in io.copy_buffer");
+	}
+	return _copy_buffer(dst, src, buf);
+}
+
+
+
+// copy_n copies n bytes (or till an error) from src to dst.
+// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
+// On return, written == n IFF err == nil
+copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
+	nsrc := inline_limited_reader(&Limited_Reader{}, src, n);
+	written, err = copy(dst, nsrc);
+	if written == n {
+		return n, nil;
+	}
+	if written < n && err == nil {
+		// src stopped early and must have been an EOF
+		err = .EOF;
+	}
+	return;
+}
+
+
+@(private)
+_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
+	if dst.stream_vtable == nil || src.stream_vtable == nil {
+		return 0, .Empty;
+	}
+	if src.impl_write_to != nil {
+		return src->impl_write_to(dst);
+	}
+	if src.impl_read_from != nil {
+		return dst->impl_read_from(src);
+	}
+	buf := buf;
+	if buf == nil {
+		DEFAULT_SIZE :: 4 * 1024;
+		size := DEFAULT_SIZE;
+		if src.stream_vtable == _limited_reader_vtable {
+			l := (^Limited_Reader)(src.stream_data);
+			if i64(size) > l.n {
+				if l.n < 1 {
+					size = 1;
+				} else {
+					size = int(l.n);
+				}
+			}
+		}
+		// NOTE(bill): alloca is fine here
+		buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
+	}
+	for {
+		nr, er := read(src, buf);
+		if nr > 0 {
+			nw, ew := write(dst, buf[0:nr]);
+			if nw > 0 {
+				written += i64(nw);
+			}
+			if ew != nil {
+				err = ew;
+				break;
+			}
+			if nr != nw {
+				err = .Short_Write;
+				break;
+			}
+		}
+		if er != nil {
+			if er != .EOF {
+				err = er;
+			}
+			break;
+		}
+	}
+	return;
+}

+ 113 - 0
core/io/multi.odin

@@ -0,0 +1,113 @@
+package io
+
+import "core:runtime"
+
+@(private)
+Multi_Reader :: struct {
+	readers: [dynamic]Reader,
+}
+
+@(private)
+_multi_reader_vtable := &Stream_VTable{
+	impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+		mr := (^Multi_Reader)(s.stream_data);
+		for len(mr.readers) > 0 {
+			r := mr.readers[0];
+			n, err = read(r, p);
+			if err == .EOF {
+				ordered_remove(&mr.readers, 0);
+			}
+			if n > 0 || err != .EOF {
+				if err == .EOF && len(mr.readers) > 0 {
+					// Don't return EOF yet, more readers remain
+					err = nil;
+				}
+				return;
+			}
+		}
+		return 0, .EOF;
+	},
+	impl_destroy = proc(s: Stream) -> Error {
+		mr := (^Multi_Reader)(s.stream_data);
+		context.allocator = mr.readers.allocator;
+		delete(mr.readers);
+		free(mr);
+		return .None;
+	},
+};
+
+multi_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
+	context.allocator = allocator;
+	mr := new(Multi_Reader);
+	all_readers := make([dynamic]Reader, 0, len(readers));
+
+	for w in readers {
+		if w.stream_vtable == _multi_reader_vtable {
+			other := (^Multi_Reader)(w.stream_data);
+			append(&all_readers, ..other.readers[:]);
+		} else {
+			append(&all_readers, w);
+		}
+	}
+
+	mr.readers = all_readers;
+
+	r.stream_vtable = _multi_reader_vtable;
+	r.stream_data = mr;
+	return;
+}
+
+
+@(private)
+Multi_Writer :: struct {
+	writers:      []Writer,
+	allocator:    runtime.Allocator,
+}
+
+@(private)
+_multi_writer_vtable := &Stream_VTable{
+	impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+		mw := (^Multi_Writer)(s.stream_data);
+		for w in mw.writers {
+			n, err = write(w, p);
+			if err != nil {
+				return;
+			}
+			if n != len(p) {
+				err = .Short_Write;
+				return;
+			}
+		}
+
+		return len(p), nil;
+	},
+	impl_destroy = proc(s: Stream) -> Error {
+		mw := (^Multi_Writer)(s.stream_data);
+		context.allocator = mw.allocator;
+		delete(mw.writers);
+		free(mw);
+		return .None;
+	},
+};
+
+multi_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
+	context.allocator = allocator;
+	mw := new(Multi_Writer);
+	mw.allocator = allocator;
+	all_writers := make([dynamic]Writer, 0, len(writers));
+
+	for w in writers {
+		if w.stream_vtable == _multi_writer_vtable {
+			other := (^Multi_Writer)(w.stream_data);
+			append(&all_writers, ..other.writers);
+		} else {
+			append(&all_writers, w);
+		}
+	}
+
+	mw.writers = all_writers[:];
+
+	out.stream_vtable = _multi_writer_vtable;
+	out.stream_data = mw;
+	return;
+}

+ 192 - 0
core/io/util.odin

@@ -0,0 +1,192 @@
+package io
+
+import "core:runtime"
+import "core:strconv"
+
+write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
+	buf: [32]byte;
+	s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil);
+	return write_string(w, s);
+}
+write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) {
+	buf: [32]byte;
+	s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
+	return write_string(w, s);
+}
+
+write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) {
+	return write_u64(w, u64(i), base);
+}
+write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) {
+	return write_i64(w, i64(i), base);
+}
+
+@(private)
+Tee_Reader :: struct {
+	r: Reader,
+	w: Writer,
+	allocator: runtime.Allocator,
+}
+
+@(private)
+_tee_reader_vtable := &Stream_VTable{
+	impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+		t := (^Tee_Reader)(s.stream_data);
+		n, err = read(t.r, p);
+		if n > 0 {
+			if wn, werr := write(t.w, p[:n]); werr != nil {
+				return wn, werr;
+			}
+		}
+		return;
+	},
+	impl_destroy = proc(s: Stream) -> Error {
+		t := (^Tee_Reader)(s.stream_data);
+		allocator := t.allocator;
+		free(t, allocator);
+		return .None;
+	},
+};
+
+// tee_reader returns a Reader that writes to 'w' what it reads from 'r'
+// All reads from 'r' performed through it are matched with a corresponding write to 'w'
+// There is no internal buffering done
+// The write must complete before th read completes
+// Any error encountered whilst writing is reported as a 'read' error
+// tee_reader must call io.destroy when done with
+tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) {
+	t := new(Tee_Reader, allocator);
+	t.r, t.w = r, w;
+	t.allocator = allocator;
+
+	out.stream_data = t;
+	out.stream_vtable = _tee_reader_vtable;
+	return;
+}
+
+
+// A Limited_Reader reads from r but limits the amount of data returned to just n bytes.
+// Each call to read updates n to reflect the new amount remaining.
+// read returns EOF when n <= 0 or when the underlying r returns EOF.
+Limited_Reader :: struct {
+	r: Reader, // underlying reader
+	n: i64,    // max_bytes
+}
+
+@(private)
+_limited_reader_vtable := &Stream_VTable{
+	impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
+		l := (^Limited_Reader)(s.stream_data);
+		if l.n <= 0 {
+			return 0, .EOF;
+		}
+		p := p;
+		if i64(len(p)) > l.n {
+			p = p[0:l.n];
+		}
+		n, err = read(l.r, p);
+		l.n -= i64(n);
+		return;
+	},
+};
+
+new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader {
+	l := new(Limited_Reader);
+	l.r = r;
+	l.n = n;
+	return l;
+}
+
+limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
+	r.stream_vtable = _limited_reader_vtable;
+	r.stream_data = l;
+	return;
+}
+
+@(private="package")
+inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
+	l.r = r;
+	l.n = n;
+	return limited_reader_to_reader(l);
+}
+
+// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
+Section_Reader :: struct {
+	r: Reader_At,
+	base:  i64,
+	off:   i64,
+	limit: i64,
+}
+
+init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
+	s.r = r;
+	s.off = off;
+	s.limit = off + n;
+	return;
+}
+section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
+	out.stream_data = s;
+	out.stream_vtable = _section_reader_vtable;
+	return;
+}
+
+@(private)
+_section_reader_vtable := &Stream_VTable{
+	impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
+		s := (^Section_Reader)(stream.stream_data);
+		if s.off >= s.limit {
+			return 0, .EOF;
+		}
+		p := p;
+		if max := s.limit - s.off; i64(len(p)) > max {
+			p = p[0:max];
+		}
+		n, err = read_at(s.r, p, s.off);
+		s.off += i64(n);
+		return;
+	},
+	impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
+		s := (^Section_Reader)(stream.stream_data);
+		p, off := p, off;
+
+		if off < 0 || off >= s.limit - s.base {
+			return 0, .EOF;
+		}
+		off += s.base;
+		if max := s.limit - off; i64(len(p)) > max {
+			p = p[0:max];
+			n, err = read_at(s.r, p, off);
+			if err == nil {
+				err = .EOF;
+			}
+			return;
+		}
+		return read_at(s.r, p, off);
+	},
+	impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+		s := (^Section_Reader)(stream.stream_data);
+
+		offset := offset;
+		switch whence {
+		case:
+			return 0, .Invalid_Whence;
+		case .Start:
+			offset += s.base;
+		case .Current:
+			offset += s.off;
+		case .End:
+			offset += s.limit;
+		}
+		if offset < s.base {
+			return 0, .Invalid_Offset;
+		}
+		s.off = offset;
+		n = offset - s.base;
+		return;
+	},
+	impl_size = proc(stream: Stream) -> i64 {
+		s := (^Section_Reader)(stream.stream_data);
+		return s.limit - s.base;
+	},
+};
+

+ 24 - 0
core/math/math.odin

@@ -593,6 +593,30 @@ is_inf :: proc{is_inf_f32, is_inf_f64};
 
 
 
+inf_f32 :: proc(sign: int) -> f32 {
+	return f32(inf_f64(sign));
+}
+inf_f64 :: proc(sign: int) -> f64 {
+	v: u64;
+	if sign >= 0 {
+		v = 0x7ff00000_00000000;
+	} else {
+		v = 0xfff00000_00000000;
+	}
+	return transmute(f64)v;
+}
+
+
+nan_f32 :: proc() -> f32 {
+	return f32(nan_f64());
+}
+nan_f64 :: proc() -> f64 {
+	v: u64 = 0x7ff80000_00000001;
+	return transmute(f64)v;
+}
+
+
+
 is_power_of_two :: proc(x: int) -> bool {
 	return x > 0 && (x & (x-1)) == 0;
 }

+ 11 - 4
core/odin/ast/ast.odin

@@ -146,6 +146,7 @@ Paren_Expr :: struct {
 Selector_Expr :: struct {
 	using node: Expr,
 	expr:  ^Expr,
+	op:    tokenizer.Token,
 	field: ^Ident,
 }
 
@@ -154,6 +155,13 @@ Implicit_Selector_Expr :: struct {
 	field: ^Ident,
 }
 
+Selector_Call_Expr :: struct {
+	using node: Expr,
+	expr: ^Expr,
+	call: ^Call_Expr,
+	modified_call: bool,
+}
+
 Index_Expr :: struct {
 	using node: Expr,
 	expr:  ^Expr,
@@ -206,9 +214,9 @@ Ternary_Expr :: struct {
 
 Ternary_If_Expr :: struct {
 	using node: Expr,
-	x: ^Expr,
+	x:    ^Expr,
 	op1:  tokenizer.Token,
-	cond:    ^Expr,
+	cond: ^Expr,
 	op2:  tokenizer.Token,
 	y:    ^Expr,
 }
@@ -217,7 +225,7 @@ Ternary_When_Expr :: struct {
 	using node: Expr,
 	x: ^Expr,
 	op1:  tokenizer.Token,
-	cond:    ^Expr,
+	cond: ^Expr,
 	op2:  tokenizer.Token,
 	y:    ^Expr,
 }
@@ -561,7 +569,6 @@ Distinct_Type :: struct {
 
 Opaque_Type :: struct {
 	using node: Expr,
-	tok:  tokenizer.Token_Kind,
 	type: ^Expr,
 }
 

+ 123 - 44
core/odin/parser/parser.odin

@@ -190,6 +190,50 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
 	return;
 }
 
+peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
+	prev_parser := p^;
+	defer p^ = prev_parser;
+
+	p.tok.err = nil;
+	for i := 0; i <= lookahead; i += 1 {
+		advance_token(p);
+	}
+	tok = p.curr_tok;
+	return;
+}
+skip_possible_newline :: proc(p: ^Parser) -> bool {
+	if .Insert_Semicolon not_in p.tok.flags {
+		return false;
+	}
+
+	prev := p.curr_tok;
+	if tokenizer.is_newline(prev) {
+		advance_token(p);
+		return true;
+	}
+	return false;
+}
+
+skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
+	if .Insert_Semicolon not_in p.tok.flags {
+		return false;
+	}
+
+	curr_pos := p.curr_tok.pos;
+	if tokenizer.is_newline(p.curr_tok) {
+		next := peek_token(p);
+		if curr_pos.line+1 >= next.pos.line {
+			#partial switch next.kind {
+			case .Open_Brace, .Else, .Where:
+				advance_token(p);
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
 
 next_token0 :: proc(p: ^Parser) -> bool {
 	p.curr_tok = tokenizer.scan(&p.tok);
@@ -280,7 +324,7 @@ expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token
 	prev := p.curr_tok;
 	if prev.kind != kind {
 		e := tokenizer.to_string(kind);
-		g := tokenizer.to_string(prev.kind);
+		g := tokenizer.token_to_string(prev);
 		error(p, prev.pos, "expected '%s', got '%s'", e, g);
 	}
 	advance_token(p);
@@ -291,7 +335,7 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
 	prev := p.curr_tok;
 	if prev.kind != kind {
 		e := tokenizer.to_string(kind);
-		g := tokenizer.to_string(prev.kind);
+		g := tokenizer.token_to_string(prev);
 		error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
 	}
 	advance_token(p);
@@ -300,8 +344,10 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
 
 expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
 	prev := p.curr_tok;
-	if !tokenizer.is_operator(prev.kind) {
-		g := tokenizer.to_string(prev.kind);
+	if prev.kind == .If || prev.kind == .When {
+		// okay
+	} else if !tokenizer.is_operator(prev.kind) {
+		g := tokenizer.token_to_string(prev);
 		error(p, prev.pos, "expected an operator, got '%s'", g);
 	}
 	advance_token(p);
@@ -398,7 +444,16 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
 	}
 
 	if node != nil {
-		if prev.pos.line != p.curr_tok.pos.line {
+		if .Insert_Semicolon in p.tok.flags  {
+			#partial switch p.curr_tok.kind {
+			case .Close_Brace, .Close_Paren, .Else, .EOF:
+				return true;
+			}
+
+			if is_semicolon_optional_for_node(p, node) {
+				return true;
+			}
+		} else if prev.pos.line != p.curr_tok.pos.line {
 			if is_semicolon_optional_for_node(p, node) {
 				return true;
 			}
@@ -418,7 +473,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
 		}
 	}
 
-	error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(p.curr_tok.kind));
+	error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
 	return false;
 }
 
@@ -491,6 +546,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
 		body = convert_stmt_to_body(p, parse_stmt(p));
 	} else {
 		body = parse_block_stmt(p, true);
+		skip_possible_newline_for_literal(p);
 	}
 
 	if allow_token(p, .Else) {
@@ -566,6 +622,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
 		body = convert_stmt_to_body(p, parse_stmt(p));
 	} else {
 		body = parse_block_stmt(p, false);
+		skip_possible_newline_for_literal(p);
 	}
 
 	if allow_token(p, .Else) {
@@ -627,6 +684,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 				body = convert_stmt_to_body(p, parse_stmt(p));
 			} else {
 				body = parse_body(p);
+				skip_possible_newline_for_literal(p);
 			}
 
 			range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
@@ -661,6 +719,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 		body = convert_stmt_to_body(p, parse_stmt(p));
 	} else {
 		body = parse_body(p);
+		skip_possible_newline_for_literal(p);
 	}
 
 
@@ -838,6 +897,8 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
 	attribute.elems = elems[:];
 	attribute.close = close.pos;
 
+	skip_possible_newline(p);
+
 	decl := parse_stmt(p);
 	switch d in &decl.derived {
 	case ast.Value_Decl:
@@ -1026,10 +1087,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 	    		body = convert_stmt_to_body(p, parse_stmt(p));
 	    	} else {
 	    		body = parse_block_stmt(p, false);
+			skip_possible_newline_for_literal(p);
 	    	}
 
 	    	if bad_stmt {
-				return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
+			return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
 	    	}
 
 	    	range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
@@ -1204,7 +1266,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 	}
 
 	tok := advance_token(p);
-	error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind));
+	error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
 	s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
 	return s;
 }
@@ -1957,13 +2019,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 	     bl.tok = tok;
 	     return bl;
 
-
-	case .Size_Of, .Align_Of, .Offset_Of:
-		tok := advance_token(p);
-		expr := ast.new(ast.Implicit, tok.pos, end_pos(tok));
-		expr.tok = tok;
-		return parse_call_expr(p, expr);
-
 	case .Open_Brace:
 		if !lhs {
 			return parse_literal_value(p, nil);
@@ -1992,15 +2047,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 
 	case .Opaque:
 		tok := advance_token(p);
+		warn(p, tok.pos, "opaque is deprecated in favour of #opaque");
 		type := parse_type(p);
 		ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
-		ot.tok  = tok.kind;
 		ot.type = type;
 		return ot;
+
 	case .Hash:
 		tok := expect_token(p, .Hash);
 		name := expect_token(p, .Ident);
 		switch name.text {
+		case "opaque":
+			type := parse_type(p);
+			ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
+			ot.type = type;
+			return ot;
+
 		case "type":
 			type := parse_type(p);
 			hp := ast.new(ast.Helper_Type, tok.pos, type.end);
@@ -2156,7 +2218,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 
 		where_token: tokenizer.Token;
 		where_clauses: []^ast.Expr;
-		if (p.curr_tok.kind == .Where) {
+
+		skip_possible_newline_for_literal(p);
+
+		if p.curr_tok.kind == .Where {
 			where_token = expect_token(p, .Where);
 			prev_level := p.expr_level;
 			p.expr_level = -1;
@@ -2225,25 +2290,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		ti.specialization = nil;
 		return ti;
 
-	case .Type_Of:
-		tok := advance_token(p);
-		i := ast.new(ast.Implicit, tok.pos, end_pos(tok));
-		i.tok = tok;
-		type: ^ast.Expr = parse_call_expr(p, i);
-		for p.curr_tok.kind == .Period {
-			period := advance_token(p);
-
-			field := parse_ident(p);
-			sel := ast.new(ast.Selector_Expr, period.pos, field.end);
-			sel.expr = type;
-			sel.field = field;
-
-			type = sel;
-		}
-
-		return type;
-
-
 	case .Pointer:
 		tok := expect_token(p, .Pointer);
 		elem := parse_type(p);
@@ -2351,12 +2397,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 
 		where_token: tokenizer.Token;
 		where_clauses: []^ast.Expr;
-		if (p.curr_tok.kind == .Where) {
+
+		skip_possible_newline_for_literal(p);
+
+		if p.curr_tok.kind == .Where {
 			where_token = expect_token(p, .Where);
-			prev_level := p.expr_level;
+			where_prev_level := p.expr_level;
 			p.expr_level = -1;
 			where_clauses = parse_rhs_expr_list(p);
-			p.expr_level = prev_level;
+			p.expr_level = where_prev_level;
 		}
 
 		expect_token(p, .Open_Brace);
@@ -2414,12 +2463,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 
 		where_token: tokenizer.Token;
 		where_clauses: []^ast.Expr;
-		if (p.curr_tok.kind == .Where) {
+
+		skip_possible_newline_for_literal(p);
+
+		if p.curr_tok.kind == .Where {
 			where_token = expect_token(p, .Where);
-			prev_level := p.expr_level;
+			where_prev_level := p.expr_level;
 			p.expr_level = -1;
 			where_clauses = parse_rhs_expr_list(p);
-			p.expr_level = prev_level;
+			p.expr_level = where_prev_level;
 		}
 
 		variants: [dynamic]^ast.Expr;
@@ -2628,7 +2680,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
 	return lit;
 }
 
-parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
+parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
 	args: [dynamic]^ast.Expr;
 
 	ellipsis: tokenizer.Token;
@@ -2686,6 +2738,14 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
 	ce.ellipsis = ellipsis;
 	ce.close    = close.pos;
 
+	o := ast.unparen_expr(operand);
+	if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
+		sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end);
+		sce.expr = o;
+		sce.call = ce;
+		return sce;
+	}
+
 	return ce;
 }
 
@@ -2739,7 +2799,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			case .Colon:
 				interval = advance_token(p);
 				is_slice_op = true;
-				if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) {
+				if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
 					indicies[1] = parse_expr(p, false);
 				}
 			}
@@ -2776,6 +2836,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 
 				sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
 				sel.expr  = operand;
+				sel.op = tok;
 				sel.field = field;
 
 				operand = sel;
@@ -2811,6 +2872,24 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 				operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
 			}
 
+		case .Arrow_Right:
+			tok := expect_token(p, .Arrow_Right);
+			#partial switch p.curr_tok.kind {
+			case .Ident:
+				field := parse_ident(p);
+
+				sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
+				sel.expr  = operand;
+				sel.op = tok;
+				sel.field = field;
+
+				operand = sel;
+			case:
+				error(p, p.curr_tok.pos, "expected a selector");
+				advance_token(p);
+				operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
+			}
+
 		case .Pointer:
 			op := expect_token(p, .Pointer);
 			deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op));

+ 13 - 12
core/odin/tokenizer/token.odin

@@ -133,7 +133,6 @@ Token_Kind :: enum u32 {
 		Defer,
 		Return,
 		Proc,
-		Macro,
 		Struct,
 		Union,
 		Enum,
@@ -150,11 +149,6 @@ Token_Kind :: enum u32 {
 		Inline,
 		No_Inline,
 		Context,
-		Size_Of,
-		Align_Of,
-		Offset_Of,
-		Type_Of,
-		Const,
 	B_Keyword_End,
 
 	COUNT,
@@ -268,7 +262,6 @@ tokens := [Token_Kind.COUNT]string {
 	"defer",
 	"return",
 	"proc",
-	"macro",
 	"struct",
 	"union",
 	"enum",
@@ -285,16 +278,24 @@ tokens := [Token_Kind.COUNT]string {
 	"inline",
 	"no_inline",
 	"context",
-	"size_of",
-	"align_of",
-	"offset_of",
-	"type_of",
-	"const",
 	"",
 };
 
 custom_keyword_tokens: []string;
 
+
+is_newline :: proc(tok: Token) -> bool {
+	return tok.kind == .Semicolon && tok.text == "\n";
+}
+
+
+token_to_string :: proc(tok: Token) -> string {
+	if is_newline(tok) {
+		return "newline";
+	}
+	return to_string(tok.kind);
+}
+
 to_string :: proc(kind: Token_Kind) -> string {
 	if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
 		return tokens[kind];

+ 64 - 15
core/odin/tokenizer/tokenizer.odin

@@ -1,22 +1,31 @@
 package odin_tokenizer
 
 import "core:fmt"
+import "core:unicode"
 import "core:unicode/utf8"
 
 Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
 
+Flag :: enum {
+	Insert_Semicolon,
+}
+Flags :: distinct bit_set[Flag; u32];
+
 Tokenizer :: struct {
 	// Immutable data
 	path: string,
 	src:  []byte,
 	err:  Error_Handler,
 
+	flags: Flags,
+
 	// Tokenizing state
 	ch:          rune,
 	offset:      int,
 	read_offset: int,
 	line_offset: int,
 	line_count:  int,
+	insert_semicolon: bool,
 
 	// Mutable data
 	error_count: int,
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
 }
 
 skip_whitespace :: proc(t: ^Tokenizer) {
-	for t.ch == ' '  ||
-	    t.ch == '\t' ||
-	    t.ch == '\n' ||
-	    t.ch == '\r' {
-		advance_rune(t);
+	for {
+		switch t.ch {
+		case ' ', '\t', '\r':
+			advance_rune(t);
+		case '\n':
+			if t.insert_semicolon {
+				return;
+			}
+			advance_rune(t);
+		case:
+			return;
+		}
 	}
 }
 
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
 			return true;
 		}
 	}
-	// TODO(bill): Add unicode lookup tables
-	return false;
+	return unicode.is_letter(r);
 }
 is_digit :: proc(r: rune) -> bool {
-	// TODO(bill): Add unicode lookup tables
-	return '0' <= r && r <= '9';
+	if '0' <= r && r <= '9' {
+		return true;
+	}
+	return unicode.is_digit(r);
 }
 
 
@@ -491,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
 	lit:  string;
 	pos := offset_to_pos(t, offset);
 
+	insert_semicolon := false;
+
 	switch ch := t.ch; true {
 	case is_letter(ch):
 		lit = scan_identifier(t);
@@ -509,24 +528,39 @@ scan :: proc(t: ^Tokenizer) -> Token {
 					break check_keyword;
 				}
 			}
-			if kind == .Ident && lit == "notin" {
-				kind = .Not_In;
+
+			#partial switch kind {
+			case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
+				insert_semicolon = true;
 			}
 		}
 	case '0' <= ch && ch <= '9':
+		insert_semicolon = true;
 		kind, lit = scan_number(t, false);
 	case:
 		advance_rune(t);
 		switch ch {
 		case -1:
 			kind = .EOF;
+			if t.insert_semicolon {
+				t.insert_semicolon = false;
+				kind = .Semicolon;
+				lit = "\n";
+			}
+		case '\n':
+			t.insert_semicolon = false;
+			kind = .Semicolon;
+			lit = "\n";
 		case '"':
+			insert_semicolon = true;
 			kind = .String;
 			lit = scan_string(t);
 		case '\'':
+			insert_semicolon = true;
 			kind = .Rune;
 			lit = scan_rune(t);
 		case '`':
+			insert_semicolon = true;
 			kind = .String;
 			lit = scan_raw_string(t);
 		case '=':
@@ -540,10 +574,13 @@ scan :: proc(t: ^Tokenizer) -> Token {
 		case '#':
 			kind = .Hash;
 			if t.ch == '!' {
+				insert_semicolon = t.insert_semicolon;
 				kind = .Comment;
 				lit = scan_comment(t);
 			}
-		case '?': kind = .Question;
+		case '?':
+			insert_semicolon = true;
+			kind = .Question;
 		case '@': kind = .At;
 		case '$': kind = .Dollar;
 		case '^': kind = .Pointer;
@@ -562,6 +599,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
 		case '*': kind = switch2(t, .Mul, .Mul_Eq);
 		case '/':
 			if t.ch == '/' || t.ch == '*' {
+				insert_semicolon = t.insert_semicolon;
 				kind = .Comment;
 				lit = scan_comment(t);
 			} else {
@@ -604,11 +642,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
 		case ',': kind = .Comma;
 		case ';': kind = .Semicolon;
 		case '(': kind = .Open_Paren;
-		case ')': kind = .Close_Paren;
+		case ')':
+			insert_semicolon = true;
+			kind = .Close_Paren;
 		case '[': kind = .Open_Bracket;
-		case ']': kind = .Close_Bracket;
+		case ']':
+			insert_semicolon = true;
+			kind = .Close_Bracket;
 		case '{': kind = .Open_Brace;
-		case '}': kind = .Close_Brace;
+		case '}':
+			insert_semicolon = true;
+			kind = .Close_Brace;
 
 		case '\\': kind = .Back_Slash;
 
@@ -616,10 +660,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
 			if ch != utf8.RUNE_BOM {
 				error(t, t.offset, "illegal character '%r': %d", ch, ch);
 			}
+			insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
 			kind = .Invalid;
 		}
 	}
 
+	if .Insert_Semicolon in t.flags {
+		t.insert_semicolon = insert_semicolon;
+	}
+
 	if lit == "" {
 		lit = string(t.src[offset : t.offset]);
 	}

+ 69 - 0
core/os/stream.odin

@@ -0,0 +1,69 @@
+package os
+
+import "core:io"
+
+stream_from_handle :: proc(fd: Handle) -> io.Stream {
+	s: io.Stream;
+	s.stream_data = rawptr(uintptr(fd));
+	s.stream_vtable = _file_stream_vtable;
+	return s;
+}
+
+
+@(private)
+_file_stream_vtable := &io.Stream_VTable{
+	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		fd := Handle(uintptr(s.stream_data));
+		os_err: Errno;
+		n, os_err = read(fd, p);
+		return;
+	},
+	impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
+		when ODIN_OS == "windows" {
+			fd := Handle(uintptr(s.stream_data));
+			os_err: Errno;
+			n, os_err = read_at(fd, p, offset);
+		}
+		return;
+	},
+	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		fd := Handle(uintptr(s.stream_data));
+		os_err: Errno;
+		n, os_err = write(fd, p);
+		return;
+	},
+	impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
+		when ODIN_OS == "windows" {
+			fd := Handle(uintptr(s.stream_data));
+			os_err: Errno;
+			n, os_err = write_at(fd, p, offset);
+			_ = os_err;
+		}
+		return;
+	},
+	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+		fd := Handle(uintptr(s.stream_data));
+		n, os_err := seek(fd, offset, int(whence));
+		_ = os_err;
+		return n, nil;
+	},
+	impl_size = proc(s: io.Stream) -> i64 {
+		fd := Handle(uintptr(s.stream_data));
+		sz, _ := file_size(fd);
+		return sz;
+	},
+	impl_flush = proc(s: io.Stream) -> io.Error {
+		when ODIN_OS == "windows" {
+			fd := Handle(uintptr(s.stream_data));
+			flush(fd);
+		} else {
+			// TOOD(bill): other operating systems
+		}
+		return nil;
+	},
+	impl_close = proc(s: io.Stream) -> io.Error {
+		fd := Handle(uintptr(s.stream_data));
+		close(fd);
+		return nil;
+	},
+};

+ 2 - 1
core/path/filepath/path_windows.odin

@@ -8,7 +8,8 @@ SEPARATOR :: '\\';
 SEPARATOR_STRING :: `\`;
 LIST_SEPARATOR :: ';';
 
-reserved_names := []string{
+@(private)
+reserved_names := [?]string{
 	"CON", "PRN", "AUX", "NUL",
 	"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
 	"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",

+ 0 - 2
core/path/match.odin

@@ -28,8 +28,6 @@ Match_Error :: enum {
 // match requires that the pattern matches the entirety of the name, not just a substring
 // The only possible error returned is .Syntax_Error
 //
-// NOTE(bill): This is effectively the shell pattern matching system found
-//
 match :: proc(pattern, name: string) -> (matched: bool, err: Match_Error) {
 	pattern, name := pattern, name;
 	pattern_loop: for len(pattern) > 0 {

+ 79 - 0
core/reflect/reflect.odin

@@ -1206,3 +1206,82 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
 
 	return;
 }
+
+/*
+not_equal :: proc(a, b: any) -> bool {
+	return !equal(a, b);
+}
+equal :: proc(a, b: any) -> bool {
+	if a == nil && b == nil {
+		return true;
+	}
+
+	if a.id != b.id {
+		return false;
+	}
+
+	if a.data == b.data {
+		return true;
+	}
+
+	t := type_info_of(a.id);
+	if .Comparable not_in t.flags {
+		return false;
+	}
+
+	if t.size == 0 {
+		return true;
+	}
+
+	if .Simple_Compare in t.flags {
+		return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0;
+	}
+
+	t = runtime.type_info_core(t);
+
+	#partial switch v in t.variant {
+	case Type_Info_String:
+		if v.is_cstring {
+			x := string((^cstring)(a.data)^);
+			y := string((^cstring)(b.data)^);
+			return x == y;
+		} else {
+			x := (^string)(a.data)^;
+			y := (^string)(b.data)^;
+			return x == y;
+		}
+
+	case Type_Info_Array:
+		for i in 0..<v.count {
+			x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
+			y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
+			if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
+				return false;
+			}
+		}
+	case Type_Info_Enumerated_Array:
+		for i in 0..<v.count {
+			x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
+			y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
+			if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
+				return false;
+			}
+		}
+	case Type_Info_Struct:
+		if v.equal != nil {
+			return v.equal(a.data, b.data);
+		} else {
+			for offset, i in v.offsets {
+				x := rawptr(uintptr(a.data) + offset);
+				y := rawptr(uintptr(b.data) + offset);
+				id := v.types[i].id;
+				if !equal(any{x, id}, any{y, id}) {
+					return false;
+				}
+			}
+		}
+	}
+
+	return true;
+}
+*/

+ 161 - 132
core/reflect/types.odin

@@ -1,5 +1,6 @@
 package reflect
 
+import "core:io"
 import "core:strings"
 
 are_types_identical :: proc(a, b: ^Type_Info) -> bool {
@@ -218,6 +219,14 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
 	return false;
 }
 
+is_byte :: proc(info: ^Type_Info) -> bool {
+	if info == nil { return false; }
+	#partial switch i in type_info_base(info).variant {
+	case Type_Info_Integer: return info.size == 1;
+	}
+	return false;
+}
+
 
 is_integer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
@@ -352,258 +361,278 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
 
 
 
-write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
+write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
 	write_type(buf, type_info_of(id));
 }
+write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
+	write_type(writer, type_info_of(id));
+}
 
-write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) {
+write_typeid :: proc{
+	write_typeid_builder,
+	write_typeid_writer,
+};
+
+write_type :: proc{
+	write_type_builder,
+	write_type_writer,
+};
+
+write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int {
+	return write_type_writer(strings.to_writer(buf), ti);
+}
+write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
 	using strings;
 	if ti == nil {
-		write_string(buf, "nil");
-		return;
+		return write_string(w, "nil");
 	}
 
+	_n1 :: proc(err: io.Error) -> int { return 1 if err == nil else 0; };
+	_n2 :: proc(n: int, _: io.Error) -> int { return n; };
+	_n :: proc{_n1, _n2};
+
 	switch info in ti.variant {
 	case Type_Info_Named:
-		write_string(buf, info.name);
+		return write_string(w, info.name);
 	case Type_Info_Integer:
 		switch ti.id {
-		case int:     write_string(buf, "int");
-		case uint:    write_string(buf, "uint");
-		case uintptr: write_string(buf, "uintptr");
+		case int:     return write_string(w, "int");
+		case uint:    return write_string(w, "uint");
+		case uintptr: return write_string(w, "uintptr");
 		case:
-			write_byte(buf, 'i' if info.signed else 'u');
-			write_i64(buf, i64(8*ti.size), 10);
+			n += _n(io.write_byte(w, 'i' if info.signed else 'u'));
+			n += _n(io.write_i64(w, i64(8*ti.size), 10));
 			switch info.endianness {
 			case .Platform: // Okay
-			case .Little: write_string(buf, "le");
-			case .Big:    write_string(buf, "be");
+			case .Little: n += write_string(w, "le");
+			case .Big:    n += write_string(w, "be");
 			}
 		}
 	case Type_Info_Rune:
-		write_string(buf, "rune");
+		n += _n(io.write_string(w, "rune"));
 	case Type_Info_Float:
-		write_byte(buf, 'f');
-		write_i64(buf, i64(8*ti.size), 10);
+		n += _n(io.write_byte(w, 'f'));
+		n += _n(io.write_i64(w, i64(8*ti.size), 10));
 		switch info.endianness {
 		case .Platform: // Okay
-		case .Little: write_string(buf, "le");
-		case .Big:    write_string(buf, "be");
+		case .Little: n += write_string(w, "le");
+		case .Big:    n += write_string(w, "be");
 		}
 	case Type_Info_Complex:
-		write_string(buf, "complex");
-		write_i64(buf, i64(8*ti.size), 10);
+		n += _n(io.write_string(w, "complex"));
+		n += _n(io.write_i64(w, i64(8*ti.size), 10));
 	case Type_Info_Quaternion:
-		write_string(buf, "quaternion");
-		write_i64(buf, i64(8*ti.size), 10);
+		n += _n(io.write_string(w, "quaternion"));
+		n += _n(io.write_i64(w, i64(8*ti.size), 10));
 	case Type_Info_String:
 		if info.is_cstring {
-			write_string(buf, "cstring");
+			n += write_string(w, "cstring");
 		} else {
-			write_string(buf, "string");
+			n += write_string(w, "string");
 		}
 	case Type_Info_Boolean:
 		switch ti.id {
-		case bool: write_string(buf, "bool");
+		case bool: n += write_string(w, "bool");
 		case:
-			write_byte(buf, 'b');
-			write_i64(buf, i64(8*ti.size), 10);
+			n += _n(io.write_byte(w, 'b'));
+			n += _n(io.write_i64(w, i64(8*ti.size), 10));
 		}
 	case Type_Info_Any:
-		write_string(buf, "any");
+		n += write_string(w, "any");
 
 	case Type_Info_Type_Id:
-		write_string(buf, "typeid");
+		n += write_string(w, "typeid");
 
 	case Type_Info_Pointer:
 		if info.elem == nil {
-			write_string(buf, "rawptr");
+			write_string(w, "rawptr");
 		} else {
-			write_string(buf, "^");
-			write_type(buf, info.elem);
+			write_string(w, "^");
+			write_type(w, info.elem);
 		}
 	case Type_Info_Procedure:
-		write_string(buf, "proc");
+		n += write_string(w, "proc");
 		if info.params == nil {
-			write_string(buf, "()");
+			n += write_string(w, "()");
 		} else {
 			t := info.params.variant.(Type_Info_Tuple);
-			write_string(buf, "(");
+			n += write_string(w, "(");
 			for t, i in t.types {
 				if i > 0 {
-					write_string(buf, ", ");
+					n += write_string(w, ", ");
 				}
-				write_type(buf, t);
+				n += write_type(w, t);
 			}
-			write_string(buf, ")");
+			n += write_string(w, ")");
 		}
 		if info.results != nil {
-			write_string(buf, " -> ");
-			write_type(buf, info.results);
+			n += write_string(w, " -> ");
+			n += write_type(w, info.results);
 		}
 	case Type_Info_Tuple:
 		count := len(info.names);
-		if count != 1 { write_string(buf, "("); }
+		if count != 1 { n += write_string(w, "("); }
 		for name, i in info.names {
-			if i > 0 { write_string(buf, ", "); }
+			if i > 0 { n += write_string(w, ", "); }
 
 			t := info.types[i];
 
 			if len(name) > 0 {
-				write_string(buf, name);
-				write_string(buf, ": ");
+				n += write_string(w, name);
+				n += write_string(w, ": ");
 			}
-			write_type(buf, t);
+			n += write_type(w, t);
 		}
-		if count != 1 { write_string(buf, ")"); }
+		if count != 1 { n += write_string(w, ")"); }
 
 	case Type_Info_Array:
-		write_string(buf, "[");
-		write_i64(buf, i64(info.count), 10);
-		write_string(buf, "]");
-		write_type(buf, info.elem);
+		n += _n(io.write_string(w, "["));
+		n += _n(io.write_i64(w, i64(info.count), 10));
+		n += _n(io.write_string(w, "]"));
+		n += write_type(w, info.elem);
 
 	case Type_Info_Enumerated_Array:
-		write_string(buf, "[");
-		write_type(buf, info.index);
-		write_string(buf, "]");
-		write_type(buf, info.elem);
+		n += write_string(w, "[");
+		n += write_type(w, info.index);
+		n += write_string(w, "]");
+		n += write_type(w, info.elem);
 
 	case Type_Info_Dynamic_Array:
-		write_string(buf, "[dynamic]");
-		write_type(buf, info.elem);
+		n += _n(io.write_string(w, "[dynamic]"));
+		n += write_type(w, info.elem);
 	case Type_Info_Slice:
-		write_string(buf, "[]");
-		write_type(buf, info.elem);
+		n += _n(io.write_string(w, "[]"));
+		n += write_type(w, info.elem);
 
 	case Type_Info_Map:
-		write_string(buf, "map[");
-		write_type(buf, info.key);
-		write_byte(buf, ']');
-		write_type(buf, info.value);
+		n += _n(io.write_string(w, "map["));
+		n += write_type(w, info.key);
+		n += _n(io.write_byte(w, ']'));
+		n += write_type(w, info.value);
 
 	case Type_Info_Struct:
 		switch info.soa_kind {
 		case .None: // Ignore
 		case .Fixed:
-			write_string(buf, "#soa[");
-			write_i64(buf, i64(info.soa_len));
-			write_byte(buf, ']');
-			write_type(buf, info.soa_base_type);
+			n += _n(io.write_string(w, "#soa["));
+			n += _n(io.write_i64(w, i64(info.soa_len)));
+			n += _n(io.write_byte(w, ']'));
+			n += write_type(w, info.soa_base_type);
 			return;
 		case .Slice:
-			write_string(buf, "#soa[]");
-			write_type(buf, info.soa_base_type);
+			n += _n(io.write_string(w, "#soa[]"));
+			n += write_type(w, info.soa_base_type);
 			return;
 		case .Dynamic:
-			write_string(buf, "#soa[dynamic]");
-			write_type(buf, info.soa_base_type);
+			n += _n(io.write_string(w, "#soa[dynamic]"));
+			n += write_type(w, info.soa_base_type);
 			return;
 		}
 
-		write_string(buf, "struct ");
-		if info.is_packed    { write_string(buf, "#packed "); }
-		if info.is_raw_union { write_string(buf, "#raw_union "); }
+		n += write_string(w, "struct ");
+		if info.is_packed    { n += write_string(w, "#packed "); }
+		if info.is_raw_union { n += write_string(w, "#raw_union "); }
 		if info.custom_align {
-			write_string(buf, "#align ");
-			write_i64(buf, i64(ti.align), 10);
-			write_byte(buf, ' ');
+			n += _n(io.write_string(w, "#align "));
+			n += _n(io.write_i64(w, i64(ti.align), 10));
+			n += _n(io.write_byte(w, ' '));
 		}
-		write_byte(buf, '{');
+		n += _n(io.write_byte(w, '{'));
 		for name, i in info.names {
-			if i > 0 { write_string(buf, ", "); }
-			write_string(buf, name);
-			write_string(buf, ": ");
-			write_type(buf, info.types[i]);
+			if i > 0 { n += write_string(w, ", "); }
+			n += _n(io.write_string(w, name));
+			n += _n(io.write_string(w, ": "));
+			n += write_type(w, info.types[i]);
 		}
-		write_byte(buf, '}');
+		n += _n(io.write_byte(w, '}'));
 
 	case Type_Info_Union:
-		write_string(buf, "union ");
+		n += write_string(w, "union ");
 		if info.custom_align {
-			write_string(buf, "#align ");
-			write_i64(buf, i64(ti.align), 10);
-			write_byte(buf, ' ');
+			n += write_string(w, "#align ");
+			n += _n(io.write_i64(w, i64(ti.align), 10));
+			n += _n(io.write_byte(w, ' '));
 		}
-		write_byte(buf, '{');
+		n += _n(io.write_byte(w, '{'));
 		for variant, i in info.variants {
-			if i > 0 { write_string(buf, ", "); }
-			write_type(buf, variant);
+			if i > 0 { n += write_string(w, ", "); }
+			n += write_type(w, variant);
 		}
-		write_byte(buf, '}');
+		n += _n(io.write_byte(w, '}'));
 
 	case Type_Info_Enum:
-		write_string(buf, "enum ");
-		write_type(buf, info.base);
-		write_string(buf, " {");
+		n += write_string(w, "enum ");
+		n += write_type(w, info.base);
+		n += write_string(w, " {");
 		for name, i in info.names {
-			if i > 0 { write_string(buf, ", "); }
-			write_string(buf, name);
+			if i > 0 { n += write_string(w, ", "); }
+			n += write_string(w, name);
 		}
-		write_byte(buf, '}');
+		n += _n(io.write_byte(w, '}'));
 
 	case Type_Info_Bit_Field:
-		write_string(buf, "bit_field ");
+		n += write_string(w, "bit_field ");
 		if ti.align != 1 {
-			write_string(buf, "#align ");
-			write_i64(buf, i64(ti.align), 10);
-			write_byte(buf, ' ');
+			n += write_string(w, "#align ");
+			n += _n(io.write_i64(w, i64(ti.align), 10));
+			n += _n(io.write_byte(w, ' '));
 		}
-		write_string(buf, " {");
+		n += write_string(w, " {");
 		for name, i in info.names {
-			if i > 0 { write_string(buf, ", "); }
-			write_string(buf, name);
-			write_string(buf, ": ");
-			write_i64(buf, i64(info.bits[i]), 10);
+			if i > 0 { n += write_string(w, ", "); }
+			n += write_string(w, name);
+			n += write_string(w, ": ");
+			n += _n(io.write_i64(w, i64(info.bits[i]), 10));
 		}
-		write_byte(buf, '}');
+		n += _n(io.write_byte(w, '}'));
 
 	case Type_Info_Bit_Set:
-		write_string(buf, "bit_set[");
+		n += write_string(w, "bit_set[");
 		switch {
 		case is_enum(info.elem):
-			write_type(buf, info.elem);
+			n += write_type(w, info.elem);
 		case is_rune(info.elem):
-			write_encoded_rune(buf, rune(info.lower));
-			write_string(buf, "..");
-			write_encoded_rune(buf, rune(info.upper));
+			n += write_encoded_rune(w, rune(info.lower));
+			n += write_string(w, "..");
+			n += write_encoded_rune(w, rune(info.upper));
 		case:
-			write_i64(buf, info.lower, 10);
-			write_string(buf, "..");
-			write_i64(buf, info.upper, 10);
+			n += _n(io.write_i64(w, info.lower, 10));
+			n += write_string(w, "..");
+			n += _n(io.write_i64(w, info.upper, 10));
 		}
 		if info.underlying != nil {
-			write_string(buf, "; ");
-			write_type(buf, info.underlying);
+			n += write_string(w, "; ");
+			n += write_type(w, info.underlying);
 		}
-		write_byte(buf, ']');
+		n += _n(io.write_byte(w, ']'));
 
 	case Type_Info_Opaque:
-		write_string(buf, "opaque ");
-		write_type(buf, info.elem);
+		n += write_string(w, "#opaque ");
+		n += write_type(w, info.elem);
 
 	case Type_Info_Simd_Vector:
 		if info.is_x86_mmx {
-			write_string(buf, "intrinsics.x86_mmx");
+			n += write_string(w, "intrinsics.x86_mmx");
 		} else {
-			write_string(buf, "#simd[");
-			write_i64(buf, i64(info.count));
-			write_byte(buf, ']');
-			write_type(buf, info.elem);
+			n += write_string(w, "#simd[");
+			n += _n(io.write_i64(w, i64(info.count)));
+			n += _n(io.write_byte(w, ']'));
+			n += write_type(w, info.elem);
 		}
 
 	case Type_Info_Relative_Pointer:
-		write_string(buf, "#relative(");
-		write_type(buf, info.base_integer);
-		write_string(buf, ") ");
-		write_type(buf, info.pointer);
+		n += write_string(w, "#relative(");
+		n += write_type(w, info.base_integer);
+		n += write_string(w, ") ");
+		n += write_type(w, info.pointer);
 
 	case Type_Info_Relative_Slice:
-		write_string(buf, "#relative(");
-		write_type(buf, info.base_integer);
-		write_string(buf, ") ");
-		write_type(buf, info.slice);
-
+		n += write_string(w, "#relative(");
+		n += write_type(w, info.base_integer);
+		n += write_string(w, ") ");
+		n += write_type(w, info.slice);
 	}
+
+	return;
 }
 

+ 32 - 1282
core/runtime/core.odin

@@ -1,11 +1,7 @@
 // This is the runtime code required by the compiler
 // IMPORTANT NOTE(bill): Do not change the order of any of this data
 // The compiler relies upon this _exact_ order
-package runtime
-
-import "intrinsics"
-_ :: intrinsics;
-
+//
 // Naming Conventions:
 // In general, Ada_Case for types and snake_case for values
 //
@@ -16,12 +12,13 @@ _ :: intrinsics;
 // Procedures:         snake_case
 // Local Variables:    snake_case
 // Constant Variables: SCREAMING_SNAKE_CASE
-
-
+//
 // IMPORTANT NOTE(bill): `type_info_of` cannot be used within a
 // #shared_global_scope due to  the internals of the compiler.
 // This could change at a later date if the all these data structures are
 // implemented within the compiler rather than in this "preload" file
+//
+package runtime
 
 // NOTE(bill): This must match the compiler's
 Calling_Convention :: enum u8 {
@@ -45,6 +42,11 @@ Platform_Endianness :: enum u8 {
 	Big      = 2,
 }
 
+// Procedure type to test whether two values of the same type are equal
+Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool;
+// Procedure type to hash a value, default seed value is 0
+Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr = 0) -> uintptr;
+
 Type_Info_Struct_Soa_Kind :: enum u8 {
 	None    = 0,
 	Fixed   = 1,
@@ -53,7 +55,12 @@ Type_Info_Struct_Soa_Kind :: enum u8 {
 }
 
 // Variant Types
-Type_Info_Named      :: struct {name: string, base: ^Type_Info};
+Type_Info_Named :: struct {
+	name: string,
+	base: ^Type_Info,
+	pkg:  string,
+	loc:  Source_Code_Location,
+};
 Type_Info_Integer    :: struct {signed: bool, endianness: Platform_Endianness};
 Type_Info_Rune       :: struct {};
 Type_Info_Float      :: struct {endianness: Platform_Endianness};
@@ -87,10 +94,11 @@ Type_Info_Enumerated_Array :: struct {
 };
 Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int};
 Type_Info_Slice         :: struct {elem: ^Type_Info, elem_size: int};
-Type_Info_Tuple :: struct { // Only really used for procedures
+Type_Info_Tuple :: struct { // Only used for procedures parameters and results
 	types:        []^Type_Info,
 	names:        []string,
 };
+
 Type_Info_Struct :: struct {
 	types:        []^Type_Info,
 	names:        []string,
@@ -100,6 +108,9 @@ Type_Info_Struct :: struct {
 	is_packed:    bool,
 	is_raw_union: bool,
 	custom_align: bool,
+
+	equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
+
 	// These are only set iff this structure is an SOA structure
 	soa_kind:      Type_Info_Struct_Soa_Kind,
 	soa_base_type: ^Type_Info,
@@ -122,6 +133,8 @@ Type_Info_Map :: struct {
 	key:              ^Type_Info,
 	value:            ^Type_Info,
 	generated_struct: ^Type_Info,
+	key_equal:        Equal_Proc,
+	key_hasher:       Hasher_Proc,
 };
 Type_Info_Bit_Field :: struct {
 	names:   []string,
@@ -152,9 +165,16 @@ Type_Info_Relative_Slice :: struct {
 	base_integer: ^Type_Info,
 };
 
+Type_Info_Flag :: enum u8 {
+	Comparable     = 0,
+	Simple_Compare = 1,
+};
+Type_Info_Flags :: distinct bit_set[Type_Info_Flag; u32];
+
 Type_Info :: struct {
 	size:  int,
 	align: int,
+	flags: Type_Info_Flags,
 	id:    typeid,
 
 	variant: union {
@@ -237,15 +257,11 @@ args__: []cstring;
 
 // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
 
-@builtin
-Maybe :: union(T: typeid) #maybe {T};
-
 
 Source_Code_Location :: struct {
 	file_path:    string,
 	line, column: int,
 	procedure:    string,
-	hash:         u64,
 }
 
 Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
@@ -320,6 +336,9 @@ Context :: struct {
 	user_data:  any,
 	user_ptr:   rawptr,
 	user_index: int,
+
+	// Internal use only
+	_internal: rawptr,
 }
 
 
@@ -345,47 +364,6 @@ Raw_Map :: struct {
 	entries: Raw_Dynamic_Array,
 }
 
-INITIAL_MAP_CAP :: 16;
-
-Map_Key :: struct {
-	hash: u64,
-	/* NOTE(bill)
-		size_of(Map_Key) == 16 Bytes on 32-bit systems
-		size_of(Map_Key) == 24 Bytes on 64-bit systems
-
-		This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems
-		however, this is probably not a huge problem in terms of memory usage
-	*/
-	key: struct #raw_union {
-		str: string,
-		val: u64,
-	},
-}
-
-Map_Find_Result :: struct {
-	hash_index:  int,
-	entry_prev:  int,
-	entry_index: int,
-}
-
-Map_Entry_Header :: struct {
-	key:  Map_Key,
-	next: int,
-/*
-	value: Value_Type,
-*/
-}
-
-Map_Header :: struct {
-	m:             ^Raw_Map,
-	is_key_string: bool,
-
-	entry_size:    int,
-	entry_align:   int,
-
-	value_offset:  uintptr,
-	value_size:    int,
-}
 
 /////////////////////////////
 // Init Startup Procedures //
@@ -521,13 +499,6 @@ __init_context :: proc "contextless" (c: ^Context) {
 	c.logger.data = nil;
 }
 
-@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
-
-@builtin
-init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
-	default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
-}
-
 
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
 	print_caller_location(loc);
@@ -540,1224 +511,3 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
 	print_byte('\n');
 	debug_trap();
 }
-
-
-
-
-@builtin
-copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
-	n := max(0, min(len(dst), len(src)));
-	if n > 0 {
-		mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
-	}
-	return n;
-}
-@builtin
-copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
-	n := max(0, min(len(dst), len(src)));
-	if n > 0 {
-		mem_copy(raw_data(dst), raw_data(src), n);
-	}
-	return n;
-}
-@builtin
-copy :: proc{copy_slice, copy_from_string};
-
-
-
-@builtin
-unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
-	bounds_check_error_loc(loc, index, len(array));
-	n := len(array)-1;
-	if index != n {
-		array[index] = array[n];
-	}
-	pop(array);
-}
-
-@builtin
-ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
-	bounds_check_error_loc(loc, index, len(array));
-	if index+1 < len(array) {
-		copy(array[index:], array[index+1:]);
-	}
-	pop(array);
-}
-
-@builtin
-remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
-	slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
-	n := max(hi-lo, 0);
-	if n > 0 {
-		if hi != len(array) {
-			copy(array[lo:], array[hi:]);
-		}
-		(^Raw_Dynamic_Array)(array).len -= n;
-	}
-}
-
-
-@builtin
-pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
-	assert(len(array) > 0, "", loc);
-	res = array[len(array)-1];
-	(^Raw_Dynamic_Array)(array).len -= 1;
-	return res;
-}
-
-
-@builtin
-pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
-	if len(array) == 0 {
-		return;
-	}
-	res, ok = array[len(array)-1], true;
-	(^Raw_Dynamic_Array)(array).len -= 1;
-	return;
-}
-
-@builtin
-pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
-	assert(len(array) > 0, "", loc);
-	res = array[0];
-	if len(array) > 1 {
-		copy(array[0:], array[1:]);
-	}
-	(^Raw_Dynamic_Array)(array).len -= 1;
-	return res;
-}
-
-@builtin
-pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
-	if len(array) == 0 {
-		return;
-	}
-	res, ok = array[0], true;
-	if len(array) > 1 {
-		copy(array[0:], array[1:]);
-	}
-	(^Raw_Dynamic_Array)(array).len -= 1;
-	return;
-}
-
-
-@builtin
-clear :: proc{clear_dynamic_array, clear_map};
-
-@builtin
-reserve :: proc{reserve_dynamic_array, reserve_map};
-
-@builtin
-resize :: proc{resize_dynamic_array};
-
-
-@builtin
-free :: proc{mem_free};
-
-@builtin
-free_all :: proc{mem_free_all};
-
-
-
-@builtin
-delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
-	mem_free(raw_data(str), allocator, loc);
-}
-@builtin
-delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
-	mem_free((^byte)(str), allocator, loc);
-}
-@builtin
-delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
-	mem_free(raw_data(array), array.allocator, loc);
-}
-@builtin
-delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
-	mem_free(raw_data(array), allocator, loc);
-}
-@builtin
-delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
-	raw := transmute(Raw_Map)m;
-	delete_slice(raw.hashes);
-	mem_free(raw.entries.data, raw.entries.allocator, loc);
-}
-
-
-@builtin
-delete :: proc{
-	delete_string,
-	delete_cstring,
-	delete_dynamic_array,
-	delete_slice,
-	delete_map,
-};
-
-
-@builtin
-new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
-	ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
-	if ptr != nil { ptr^ = T{}; }
-	return ptr;
-}
-
-@builtin
-new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
-	ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
-	if ptr != nil { ptr^ = data; }
-	return ptr;
-}
-
-make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
-	make_slice_error_loc(loc, len);
-	data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
-	if data == nil && size_of(E) != 0 {
-		return nil;
-	}
-	// mem_zero(data, size_of(E)*len);
-	s := Raw_Slice{data, len};
-	return transmute(T)s;
-}
-
-@builtin
-make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
-	return make_aligned(T, len, align_of(E), allocator, loc);
-}
-
-@builtin
-make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
-	return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
-}
-
-@builtin
-make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
-	return make_dynamic_array_len_cap(T, len, len, allocator, loc);
-}
-
-@builtin
-make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
-	make_dynamic_array_error_loc(loc, len, cap);
-	data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
-	s := Raw_Dynamic_Array{data, len, cap, allocator};
-	if data == nil && size_of(E) != 0 {
-		s.len, s.cap = 0, 0;
-	}
-	// mem_zero(data, size_of(E)*cap);
-	return transmute(T)s;
-}
-
-@builtin
-make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
-	make_map_expr_error_loc(loc, cap);
-	context.allocator = allocator;
-
-	m: T;
-	reserve_map(&m, cap);
-	return m;
-}
-
-@builtin
-make :: proc{
-	make_slice,
-	make_dynamic_array,
-	make_dynamic_array_len,
-	make_dynamic_array_len_cap,
-	make_map,
-};
-
-
-
-@builtin
-clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
-	if m == nil {
-		return;
-	}
-	raw_map := (^Raw_Map)(m);
-	entries := (^Raw_Dynamic_Array)(&raw_map.entries);
-	entries.len = 0;
-	for _, i in raw_map.hashes {
-		raw_map.hashes[i] = -1;
-	}
-}
-
-@builtin
-reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
-	if m != nil {
-		__dynamic_map_reserve(__get_map_header(m), capacity);
-	}
-}
-
-@builtin
-delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
-	if m != nil {
-		__dynamic_map_delete_key(__get_map_header(m), __get_map_key(key));
-	}
-}
-
-
-
-@builtin
-append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location)  {
-	if array == nil {
-		return;
-	}
-
-	arg_len := 1;
-
-	if cap(array) < len(array)+arg_len {
-		cap := 2 * cap(array) + max(8, arg_len);
-		_ = reserve(array, cap, loc);
-	}
-	arg_len = min(cap(array)-len(array), arg_len);
-	if arg_len > 0 {
-		a := (^Raw_Dynamic_Array)(array);
-		if size_of(E) != 0 {
-			data := (^E)(a.data);
-			assert(data != nil);
-			val := arg;
-			mem_copy(ptr_offset(data, a.len), &val, size_of(E));
-		}
-		a.len += arg_len;
-	}
-}
-@builtin
-append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)  {
-	if array == nil {
-		return;
-	}
-
-	arg_len := len(args);
-	if arg_len <= 0 {
-		return;
-	}
-
-
-	if cap(array) < len(array)+arg_len {
-		cap := 2 * cap(array) + max(8, arg_len);
-		_ = reserve(array, cap, loc);
-	}
-	arg_len = min(cap(array)-len(array), arg_len);
-	if arg_len > 0 {
-		a := (^Raw_Dynamic_Array)(array);
-		if size_of(E) != 0 {
-			data := (^E)(a.data);
-			assert(data != nil);
-			mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
-		}
-		a.len += arg_len;
-	}
-}
-@builtin
-append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
-	args := transmute([]E)arg;
-	append_elems(array=array, args=args, loc=loc);
-}
-
-@builtin
-reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
-	if array == nil {
-		return false;
-	}
-
-	old_cap := cap(array);
-	if capacity <= old_cap {
-		return true;
-	}
-
-	if array.allocator.procedure == nil {
-		array.allocator = context.allocator;
-	}
-	assert(array.allocator.procedure != nil);
-
-
-	ti := type_info_of(typeid_of(T));
-	ti = type_info_base(ti);
-	si := &ti.variant.(Type_Info_Struct);
-
-	field_count := uintptr(len(si.offsets) - 3);
-
-	if field_count == 0 {
-		return true;
-	}
-
-	cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
-	assert(cap_ptr^ == old_cap);
-
-
-	old_size := 0;
-	new_size := 0;
-
-	max_align := 0;
-	for i in 0..<field_count {
-		type := si.types[i].variant.(Type_Info_Pointer).elem;
-		max_align = max(max_align, type.align);
-
-		old_size = align_forward_int(old_size, type.align);
-		new_size = align_forward_int(new_size, type.align);
-
-		old_size += type.size * old_cap;
-		new_size += type.size * capacity;
-	}
-
-	old_size = align_forward_int(old_size, max_align);
-	new_size = align_forward_int(new_size, max_align);
-
-	old_data := (^rawptr)(array)^;
-
-	new_data := array.allocator.procedure(
-		array.allocator.data, .Alloc, new_size, max_align,
-		nil, old_size, 0, loc,
-	);
-	if new_data == nil {
-		return false;
-	}
-
-
-	cap_ptr^ = capacity;
-
-	old_offset := 0;
-	new_offset := 0;
-	for i in 0..<field_count {
-		type := si.types[i].variant.(Type_Info_Pointer).elem;
-		max_align = max(max_align, type.align);
-
-		old_offset = align_forward_int(old_offset, type.align);
-		new_offset = align_forward_int(new_offset, type.align);
-
-		new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
-		old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
-
-		mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
-
-		(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
-
-		old_offset += type.size * old_cap;
-		new_offset += type.size * capacity;
-	}
-
-	array.allocator.procedure(
-		array.allocator.data, .Free, 0, max_align,
-		old_data, old_size, 0, loc,
-	);
-
-	return true;
-}
-
-@builtin
-append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
-	if array == nil {
-		return;
-	}
-
-	arg_len := 1;
-
-	if cap(array) <= len(array)+arg_len {
-		cap := 2 * cap(array) + max(8, arg_len);
-		_ = reserve_soa(array, cap, loc);
-	}
-	arg_len = min(cap(array)-len(array), arg_len);
-	if arg_len > 0 {
-		ti := type_info_of(typeid_of(T));
-		ti = type_info_base(ti);
-		si := &ti.variant.(Type_Info_Struct);
-		field_count := uintptr(len(si.offsets) - 3);
-
-		if field_count == 0 {
-			return;
-		}
-
-		data := (^rawptr)(array)^;
-
-		len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
-
-
-		soa_offset := 0;
-		item_offset := 0;
-
-		arg_copy := arg;
-		arg_ptr := &arg_copy;
-
-		max_align := 0;
-		for i in 0..<field_count {
-			type := si.types[i].variant.(Type_Info_Pointer).elem;
-			max_align = max(max_align, type.align);
-
-			soa_offset  = align_forward_int(soa_offset, type.align);
-			item_offset = align_forward_int(item_offset, type.align);
-
-			dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
-			src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
-			mem_copy(dst, src, type.size);
-
-			soa_offset  += type.size * cap(array);
-			item_offset += type.size;
-		}
-
-		len_ptr^ += arg_len;
-	}
-}
-
-@builtin
-append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
-	if array == nil {
-		return;
-	}
-
-	arg_len := len(args);
-	if arg_len == 0 {
-		return;
-	}
-
-	if cap(array) <= len(array)+arg_len {
-		cap := 2 * cap(array) + max(8, arg_len);
-		_ = reserve_soa(array, cap, loc);
-	}
-	arg_len = min(cap(array)-len(array), arg_len);
-	if arg_len > 0 {
-		ti := type_info_of(typeid_of(T));
-		ti = type_info_base(ti);
-		si := &ti.variant.(Type_Info_Struct);
-		field_count := uintptr(len(si.offsets) - 3);
-
-		if field_count == 0 {
-			return;
-		}
-
-		data := (^rawptr)(array)^;
-
-		len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
-
-
-		soa_offset := 0;
-		item_offset := 0;
-
-		args_ptr := &args[0];
-
-		max_align := 0;
-		for i in 0..<field_count {
-			type := si.types[i].variant.(Type_Info_Pointer).elem;
-			max_align = max(max_align, type.align);
-
-			soa_offset  = align_forward_int(soa_offset, type.align);
-			item_offset = align_forward_int(item_offset, type.align);
-
-			dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
-			src := uintptr(args_ptr) + uintptr(item_offset);
-			for j in 0..<arg_len {
-				d := rawptr(dst + uintptr(j*type.size));
-				s := rawptr(src + uintptr(j*size_of(E)));
-				mem_copy(d, s, type.size);
-			}
-
-			soa_offset  += type.size * cap(array);
-			item_offset += type.size;
-		}
-
-		len_ptr^ += arg_len;
-	}
-}
-
-@builtin
-append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
-	for arg in args {
-		append(array = array, args = transmute([]E)(arg), loc = loc);
-	}
-}
-
-
-@builtin append :: proc{append_elem, append_elems, append_elem_string};
-@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
-
-@builtin
-append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
-	if array == nil {
-		return;
-	}
-	resize(array, len(array)+1);
-}
-
-
-@builtin
-insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
-	if array == nil {
-		return;
-	}
-	n := len(array);
-	m :: 1;
-	resize(array, n+m, loc);
-	if n+m <= len(array) {
-		when size_of(E) != 0 {
-			copy(array[index+m:], array[index:]);
-			array[index] = arg;
-		}
-		ok = true;
-	}
-	return;
-}
-
-@builtin
-insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
-	if array == nil {
-		return;
-	}
-	if len(args) == 0 {
-		ok = true;
-		return;
-	}
-
-	n := len(array);
-	m := len(args);
-	resize(array, n+m, loc);
-	if n+m <= len(array) {
-		when size_of(E) != 0 {
-			copy(array[index+m:], array[index:]);
-			copy(array[index:], args);
-		}
-		ok = true;
-	}
-	return;
-}
-
-@builtin
-insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
-	if array == nil {
-		return;
-	}
-	if len(args) == 0 {
-		ok = true;
-		return;
-	}
-
-	n := len(array);
-	m := len(args);
-	resize(array, n+m, loc);
-	if n+m <= len(array) {
-		copy(array[index+m:], array[index:]);
-		copy(array[index:], args);
-		ok = true;
-	}
-	return;
-}
-
-@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
-
-
-
-
-@builtin
-clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
-	if array != nil {
-		(^Raw_Dynamic_Array)(array).len = 0;
-	}
-}
-
-@builtin
-reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
-	if array == nil {
-		return false;
-	}
-	a := (^Raw_Dynamic_Array)(array);
-
-	if capacity <= a.cap {
-		return true;
-	}
-
-	if a.allocator.procedure == nil {
-		a.allocator = context.allocator;
-	}
-	assert(a.allocator.procedure != nil);
-
-	old_size  := a.cap * size_of(E);
-	new_size  := capacity * size_of(E);
-	allocator := a.allocator;
-
-	new_data := allocator.procedure(
-		allocator.data, .Resize, new_size, align_of(E),
-		a.data, old_size, 0, loc,
-	);
-	if new_data == nil {
-		return false;
-	}
-
-	a.data = new_data;
-	a.cap = capacity;
-	return true;
-}
-
-@builtin
-resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
-	if array == nil {
-		return false;
-	}
-	a := (^Raw_Dynamic_Array)(array);
-
-	if length <= a.cap {
-		a.len = max(length, 0);
-		return true;
-	}
-
-	if a.allocator.procedure == nil {
-		a.allocator = context.allocator;
-	}
-	assert(a.allocator.procedure != nil);
-
-	old_size  := a.cap * size_of(E);
-	new_size  := length * size_of(E);
-	allocator := a.allocator;
-
-	new_data := allocator.procedure(
-		allocator.data, .Resize, new_size, align_of(E),
-		a.data, old_size, 0, loc,
-	);
-	if new_data == nil {
-		return false;
-	}
-
-	a.data = new_data;
-	a.len = length;
-	a.cap = length;
-	return true;
-}
-
-
-
-@builtin
-incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
-	s^ |= {elem};
-	return s^;
-}
-@builtin
-incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
-	for elem in elems {
-		s^ |= {elem};
-	}
-	return s^;
-}
-@builtin
-incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
-	s^ |= other;
-	return s^;
-}
-@builtin
-excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
-	s^ &~= {elem};
-	return s^;
-}
-@builtin
-excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
-	for elem in elems {
-		s^ &~= {elem};
-	}
-	return s^;
-}
-@builtin
-excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
-	s^ &~= other;
-	return s^;
-}
-
-@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
-@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
-
-
-@builtin
-card :: proc(s: $S/bit_set[$E; $U]) -> int {
-	when size_of(S) == 1 {
-		foreign { @(link_name="llvm.ctpop.i8")  count_ones :: proc(i: u8) -> u8 --- }
-		return int(count_ones(transmute(u8)s));
-	} else when size_of(S) == 2 {
-		foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
-		return int(count_ones(transmute(u16)s));
-	} else when size_of(S) == 4 {
-		foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
-		return int(count_ones(transmute(u32)s));
-	} else when size_of(S) == 8 {
-		foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
-		return int(count_ones(transmute(u64)s));
-	} else when size_of(S) == 16 {
-		foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
-		return int(count_ones(transmute(u128)s));
-	} else {
-		#panic("Unhandled card bit_set size");
-	}
-}
-
-
-
-@builtin
-raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
-	return (^E)(a);
-}
-@builtin
-raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
-	ptr := (transmute(Raw_Slice)s).data;
-	return (^E)(ptr);
-}
-@builtin
-raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
-	ptr := (transmute(Raw_Dynamic_Array)s).data;
-	return (^E)(ptr);
-}
-@builtin
-raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
-	return (transmute(Raw_String)s).data;
-}
-
-@builtin
-raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
-
-
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-assert :: proc(condition: bool, message := "", loc := #caller_location) {
-	if !condition {
-		proc(message: string, loc: Source_Code_Location) {
-			p := context.assertion_failure_proc;
-			if p == nil {
-				p = default_assertion_failure_proc;
-			}
-			p("runtime assertion", message, loc);
-		}(message, loc);
-	}
-}
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-panic :: proc(message: string, loc := #caller_location) -> ! {
-	p := context.assertion_failure_proc;
-	if p == nil {
-		p = default_assertion_failure_proc;
-	}
-	p("panic", message, loc);
-}
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-unimplemented :: proc(message := "", loc := #caller_location) -> ! {
-	p := context.assertion_failure_proc;
-	if p == nil {
-		p = default_assertion_failure_proc;
-	}
-	p("not yet implemented", message, loc);
-}
-
-@builtin
-@(disabled=ODIN_DISABLE_ASSERT)
-unreachable :: proc(message := "", loc := #caller_location) -> ! {
-	p := context.assertion_failure_proc;
-	if p == nil {
-		p = default_assertion_failure_proc;
-	}
-	if message != "" {
-		p("internal error", message, loc);
-	} else {
-		p("internal error", "entered unreachable code", loc);
-	}
-}
-
-
-// Dynamic Array
-
-
-__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
-	array := (^Raw_Dynamic_Array)(array_);
-	array.allocator = context.allocator;
-	assert(array.allocator.procedure != nil);
-
-	if cap > 0 {
-		__dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
-		array.len = len;
-	}
-}
-
-__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
-	array := (^Raw_Dynamic_Array)(array_);
-
-	// NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
-	// assuming that appending/reserving will set the allocator, if it is not already set.
-	if array.allocator.procedure == nil {
-		array.allocator = context.allocator;
-	}
-	assert(array.allocator.procedure != nil);
-
-	if cap <= array.cap {
-		return true;
-	}
-
-	old_size  := array.cap * elem_size;
-	new_size  := cap * elem_size;
-	allocator := array.allocator;
-
-	new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
-	if new_data != nil || elem_size == 0 {
-		array.data = new_data;
-		array.cap = cap;
-		return true;
-	}
-	return false;
-}
-
-__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
-	array := (^Raw_Dynamic_Array)(array_);
-
-	ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
-	if ok {
-		array.len = len;
-	}
-	return ok;
-}
-
-
-__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
-                               items: rawptr, item_count: int, loc := #caller_location) -> int {
-	array := (^Raw_Dynamic_Array)(array_);
-
-	if items == nil    {
-		return 0;
-	}
-	if item_count <= 0 {
-		return 0;
-	}
-
-
-	ok := true;
-	if array.cap <= array.len+item_count {
-		cap := 2 * array.cap + max(8, item_count);
-		ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
-	}
-	// TODO(bill): Better error handling for failed reservation
-	if !ok {
-		return array.len;
-	}
-
-	assert(array.data != nil);
-	data := uintptr(array.data) + uintptr(elem_size*array.len);
-
-	mem_copy(rawptr(data), items, elem_size * item_count);
-	array.len += item_count;
-	return array.len;
-}
-
-__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
-	array := (^Raw_Dynamic_Array)(array_);
-
-	ok := true;
-	if array.cap <= array.len+1 {
-		cap := 2 * array.cap + max(8, 1);
-		ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
-	}
-	// TODO(bill): Better error handling for failed reservation
-	if !ok {
-		return array.len;
-	}
-
-	assert(array.data != nil);
-	data := uintptr(array.data) + uintptr(elem_size*array.len);
-	mem_zero(rawptr(data), elem_size);
-	array.len += 1;
-	return array.len;
-}
-
-
-
-
-// Map
-
-__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
-	header := Map_Header{m = (^Raw_Map)(m)};
-	Entry :: struct {
-		key:   Map_Key,
-		next:  int,
-		value: V,
-	};
-
-	header.is_key_string = intrinsics.type_is_string(K);
-	header.entry_size    = int(size_of(Entry));
-	header.entry_align   = int(align_of(Entry));
-	header.value_offset  = uintptr(offset_of(Entry, value));
-	header.value_size    = int(size_of(V));
-	return header;
-}
-
-__get_map_key :: proc "contextless" (k: $K) -> Map_Key {
-	key := k;
-	map_key: Map_Key;
-
-	T :: intrinsics.type_core_type(K);
-
-	when intrinsics.type_is_integer(T) {
-		map_key.hash = default_hash_ptr(&key, size_of(T));
-
-		sz :: 8*size_of(T);
-		     when sz ==  8 { map_key.key.val = u64(( ^u8)(&key)^); }
-		else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); }
-		else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); }
-		else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); }
-		else { #panic("Unhandled integer size"); }
-	} else when intrinsics.type_is_rune(T) {
-		map_key.hash = default_hash_ptr(&key, size_of(T));
-		map_key.key.val = u64((^rune)(&key)^);
-	} else when intrinsics.type_is_pointer(T) {
-		map_key.hash = default_hash_ptr(&key, size_of(T));
-		map_key.key.val = u64(uintptr((^rawptr)(&key)^));
-	} else when intrinsics.type_is_float(T) {
-		map_key.hash = default_hash_ptr(&key, size_of(T));
-
-		sz :: 8*size_of(T);
-		     when sz == 32 { map_key.key.val = u64((^u32)(&key)^); }
-		else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); }
-		else { #panic("Unhandled float size"); }
-	} else when intrinsics.type_is_string(T) {
-		#assert(T == string);
-		str := (^string)(&key)^;
-		map_key.hash = default_hash_string(str);
-		map_key.key.str = str;
-	} else {
-		#panic("Unhandled map key type");
-	}
-
-	return map_key;
-}
-
-_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 {
-	h: u64 = seed;
-	for b in data {
-		h = (h ~ u64(b)) * 0x100000001b3;
-	}
-	return h;
-}
-
-
-default_hash :: inline proc "contextless" (data: []byte) -> u64 {
-	return _fnv64a(data);
-}
-default_hash_string :: inline proc "contextless" (s: string) -> u64 {
-	return default_hash(transmute([]byte)(s));
-}
-default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 {
-	s := Raw_Slice{data, size};
-	return default_hash(transmute([]byte)(s));
-}
-
-
-source_code_location_hash :: proc(s: Source_Code_Location) -> u64 {
-	hash := _fnv64a(transmute([]byte)s.file_path);
-	hash = hash ~ (u64(s.line) * 0x100000001b3);
-	hash = hash ~ (u64(s.column) * 0x100000001b3);
-	return hash;
-}
-
-
-
-__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
-	array := (^Raw_Slice)(array_);
-
-	if new_count < array.len {
-		return true;
-	}
-
-	assert(allocator.procedure != nil);
-
-	old_size := array.len*size_of(T);
-	new_size := new_count*size_of(T);
-
-	new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
-	if new_data == nil {
-		return false;
-	}
-	array.data = new_data;
-	array.len = new_count;
-	return true;
-}
-
-__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
-	__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
-
-	old_len := len(m.hashes);
-	__slice_resize(&m.hashes, cap, m.entries.allocator, loc);
-	for i in old_len..<len(m.hashes) {
-		m.hashes[i] = -1;
-	}
-
-}
-__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
-	new_header: Map_Header = header;
-	nm := Raw_Map{};
-	nm.entries.allocator = m.entries.allocator;
-	new_header.m = &nm;
-
-	c := context;
-	if m.entries.allocator.procedure != nil {
-		c.allocator = m.entries.allocator;
-	}
-	context = c;
-
-	__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
-	__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
-	for i in 0 ..< new_count {
-		nm.hashes[i] = -1;
-	}
-
-	for i in 0 ..< m.entries.len {
-		if len(nm.hashes) == 0 {
-			__dynamic_map_grow(new_header, loc);
-		}
-
-		entry_header := __dynamic_map_get_entry(header, i);
-		data := uintptr(entry_header);
-
-		fr := __dynamic_map_find(new_header, entry_header.key);
-		j := __dynamic_map_add_entry(new_header, entry_header.key, loc);
-		if fr.entry_prev < 0 {
-			nm.hashes[fr.hash_index] = j;
-		} else {
-			e := __dynamic_map_get_entry(new_header, fr.entry_prev);
-			e.next = j;
-		}
-
-		e := __dynamic_map_get_entry(new_header, j);
-		e.next = fr.entry_index;
-		ndata := uintptr(e);
-		mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size);
-
-		if __dynamic_map_full(new_header) {
-			__dynamic_map_grow(new_header, loc);
-		}
-	}
-	delete(m.hashes, m.entries.allocator, loc);
-	free(m.entries.data, m.entries.allocator, loc);
-	header.m^ = nm;
-}
-
-__dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr {
-	index := __dynamic_map_find(h, key).entry_index;
-	if index >= 0 {
-		data := uintptr(__dynamic_map_get_entry(h, index));
-		return rawptr(data + h.value_offset);
-	}
-	return nil;
-}
-
-__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check {
-	index: int;
-	assert(value != nil);
-
-	if len(h.m.hashes) == 0 {
-		__dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
-		__dynamic_map_grow(h, loc);
-	}
-
-	fr := __dynamic_map_find(h, key);
-	if fr.entry_index >= 0 {
-		index = fr.entry_index;
-	} else {
-		index = __dynamic_map_add_entry(h, key, loc);
-		if fr.entry_prev >= 0 {
-			entry := __dynamic_map_get_entry(h, fr.entry_prev);
-			entry.next = index;
-		} else {
-			h.m.hashes[fr.hash_index] = index;
-		}
-	}
-	{
-		e := __dynamic_map_get_entry(h, index);
-		e.key = key;
-		val := (^byte)(uintptr(e) + h.value_offset);
-		mem_copy(val, value, h.value_size);
-	}
-
-	if __dynamic_map_full(h) {
-		__dynamic_map_grow(h, loc);
-	}
-}
-
-
-__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
-	// TODO(bill): Determine an efficient growing rate
-	new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
-	__dynamic_map_rehash(h, new_count, loc);
-}
-
-__dynamic_map_full :: inline proc(using h: Map_Header) -> bool {
-	return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
-}
-
-
-__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool {
-	if a.hash == b.hash {
-		if h.is_key_string {
-			return a.key.str == b.key.str;
-		} else {
-			return a.key.val == b.key.val;
-		}
-		return true;
-	}
-	return false;
-}
-
-__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check {
-	fr := Map_Find_Result{-1, -1, -1};
-	if n := u64(len(m.hashes)); n > 0 {
-		fr.hash_index = int(key.hash % n);
-		fr.entry_index = m.hashes[fr.hash_index];
-		for fr.entry_index >= 0 {
-			entry := __dynamic_map_get_entry(h, fr.entry_index);
-			if __dynamic_map_hash_equal(h, entry.key, key) {
-				return fr;
-			}
-			fr.entry_prev = fr.entry_index;
-			fr.entry_index = entry.next;
-		}
-	}
-	return fr;
-}
-
-__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int {
-	prev := m.entries.len;
-	c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
-	if c != prev {
-		end := __dynamic_map_get_entry(h, c-1);
-		end.key = key;
-		end.next = -1;
-	}
-	return prev;
-}
-
-__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) {
-	fr := __dynamic_map_find(h, key);
-	if fr.entry_index >= 0 {
-		__dynamic_map_erase(h, fr);
-	}
-}
-
-__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
-	assert(0 <= index && index < m.entries.len);
-	return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
-}
-
-__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
-	if fr.entry_prev < 0 {
-		m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
-	} else {
-		prev := __dynamic_map_get_entry(h, fr.entry_prev);
-		curr := __dynamic_map_get_entry(h, fr.entry_index);
-		prev.next = curr.next;
-	}
-	if (fr.entry_index == m.entries.len-1) {
-		// NOTE(bill): No need to do anything else, just pop
-	} else {
-		old := __dynamic_map_get_entry(h, fr.entry_index);
-		end := __dynamic_map_get_entry(h, m.entries.len-1);
-		mem_copy(old, end, entry_size);
-
-		if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 {
-			last_entry := __dynamic_map_get_entry(h, last.entry_prev);
-			last_entry.next = fr.entry_index;
-		} else {
-			m.hashes[last.hash_index] = fr.entry_index;
-		}
-	}
-
-	// TODO(bill): Is this correct behaviour?
-	m.entries.len -= 1;
-}

+ 838 - 0
core/runtime/core_builtin.odin

@@ -0,0 +1,838 @@
+package runtime
+
+@builtin
+Maybe :: union(T: typeid) #maybe {T};
+
+@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
+
+@builtin
+init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
+	default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
+}
+
+
+@builtin
+copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
+	n := max(0, min(len(dst), len(src)));
+	if n > 0 {
+		mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
+	}
+	return n;
+}
+@builtin
+copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
+	n := max(0, min(len(dst), len(src)));
+	if n > 0 {
+		mem_copy(raw_data(dst), raw_data(src), n);
+	}
+	return n;
+}
+@builtin
+copy :: proc{copy_slice, copy_from_string};
+
+
+
+@builtin
+unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
+	bounds_check_error_loc(loc, index, len(array));
+	n := len(array)-1;
+	if index != n {
+		array[index] = array[n];
+	}
+	pop(array);
+}
+
+@builtin
+ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
+	bounds_check_error_loc(loc, index, len(array));
+	if index+1 < len(array) {
+		copy(array[index:], array[index+1:]);
+	}
+	pop(array);
+}
+
+@builtin
+remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
+	slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
+	n := max(hi-lo, 0);
+	if n > 0 {
+		if hi != len(array) {
+			copy(array[lo:], array[hi:]);
+		}
+		(^Raw_Dynamic_Array)(array).len -= n;
+	}
+}
+
+
+@builtin
+pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
+	assert(len(array) > 0, "", loc);
+	res = array[len(array)-1];
+	(^Raw_Dynamic_Array)(array).len -= 1;
+	return res;
+}
+
+
+@builtin
+pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
+	if len(array) == 0 {
+		return;
+	}
+	res, ok = array[len(array)-1], true;
+	(^Raw_Dynamic_Array)(array).len -= 1;
+	return;
+}
+
+@builtin
+pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
+	assert(len(array) > 0, "", loc);
+	res = array[0];
+	if len(array) > 1 {
+		copy(array[0:], array[1:]);
+	}
+	(^Raw_Dynamic_Array)(array).len -= 1;
+	return res;
+}
+
+@builtin
+pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
+	if len(array) == 0 {
+		return;
+	}
+	res, ok = array[0], true;
+	if len(array) > 1 {
+		copy(array[0:], array[1:]);
+	}
+	(^Raw_Dynamic_Array)(array).len -= 1;
+	return;
+}
+
+
+@builtin
+clear :: proc{clear_dynamic_array, clear_map};
+
+@builtin
+reserve :: proc{reserve_dynamic_array, reserve_map};
+
+@builtin
+resize :: proc{resize_dynamic_array};
+
+
+@builtin
+free :: proc{mem_free};
+
+@builtin
+free_all :: proc{mem_free_all};
+
+
+
+@builtin
+delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
+	mem_free(raw_data(str), allocator, loc);
+}
+@builtin
+delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
+	mem_free((^byte)(str), allocator, loc);
+}
+@builtin
+delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
+	mem_free(raw_data(array), array.allocator, loc);
+}
+@builtin
+delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
+	mem_free(raw_data(array), allocator, loc);
+}
+@builtin
+delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
+	raw := transmute(Raw_Map)m;
+	delete_slice(raw.hashes);
+	mem_free(raw.entries.data, raw.entries.allocator, loc);
+}
+
+
+@builtin
+delete :: proc{
+	delete_string,
+	delete_cstring,
+	delete_dynamic_array,
+	delete_slice,
+	delete_map,
+};
+
+
+// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
+// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
+@builtin
+new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
+	ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
+	if ptr != nil { ptr^ = T{}; }
+	return ptr;
+}
+
+@builtin
+new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
+	ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
+	if ptr != nil { ptr^ = data; }
+	return ptr;
+}
+
+make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
+	make_slice_error_loc(loc, len);
+	data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
+	if data == nil && size_of(E) != 0 {
+		return nil;
+	}
+	// mem_zero(data, size_of(E)*len);
+	s := Raw_Slice{data, len};
+	return transmute(T)s;
+}
+
+@builtin
+make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
+	return make_aligned(T, len, align_of(E), allocator, loc);
+}
+
+@builtin
+make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
+	return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
+}
+
+@builtin
+make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
+	return make_dynamic_array_len_cap(T, len, len, allocator, loc);
+}
+
+@builtin
+make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
+	make_dynamic_array_error_loc(loc, len, cap);
+	data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
+	s := Raw_Dynamic_Array{data, len, cap, allocator};
+	if data == nil && size_of(E) != 0 {
+		s.len, s.cap = 0, 0;
+	}
+	// mem_zero(data, size_of(E)*cap);
+	return transmute(T)s;
+}
+
+@builtin
+make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
+	make_map_expr_error_loc(loc, cap);
+	context.allocator = allocator;
+
+	m: T;
+	reserve_map(&m, cap);
+	return m;
+}
+
+// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
+// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
+// type of its argument, not a pointer to it.
+// Make uses the specified allocator, default is context.allocator, default is context.allocator
+@builtin
+make :: proc{
+	make_slice,
+	make_dynamic_array,
+	make_dynamic_array_len,
+	make_dynamic_array_len_cap,
+	make_map,
+};
+
+
+
+@builtin
+clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
+	if m == nil {
+		return;
+	}
+	raw_map := (^Raw_Map)(m);
+	entries := (^Raw_Dynamic_Array)(&raw_map.entries);
+	entries.len = 0;
+	for _, i in raw_map.hashes {
+		raw_map.hashes[i] = -1;
+	}
+}
+
+@builtin
+reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
+	if m != nil {
+		__dynamic_map_reserve(__get_map_header(m), capacity);
+	}
+}
+
+// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
+// If m is nil, or there is no such element, this procedure is a no-op
+@builtin
+delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
+	if m != nil {
+		key := key;
+		__dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&key));
+	}
+}
+
+
+
+@builtin
+append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location)  {
+	if array == nil {
+		return;
+	}
+
+	arg_len := 1;
+
+	if cap(array) < len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		a := (^Raw_Dynamic_Array)(array);
+		if size_of(E) != 0 {
+			data := (^E)(a.data);
+			assert(data != nil);
+			val := arg;
+			mem_copy(ptr_offset(data, a.len), &val, size_of(E));
+		}
+		a.len += arg_len;
+	}
+}
+
+@builtin
+append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)  {
+	if array == nil {
+		return;
+	}
+
+	arg_len := len(args);
+	if arg_len <= 0 {
+		return;
+	}
+
+
+	if cap(array) < len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		a := (^Raw_Dynamic_Array)(array);
+		if size_of(E) != 0 {
+			data := (^E)(a.data);
+			assert(data != nil);
+			mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
+		}
+		a.len += arg_len;
+	}
+}
+
+// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
+@builtin
+append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
+	args := transmute([]E)arg;
+	append_elems(array=array, args=args, loc=loc);
+}
+
+@builtin
+reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+	if array == nil {
+		return false;
+	}
+
+	old_cap := cap(array);
+	if capacity <= old_cap {
+		return true;
+	}
+
+	if array.allocator.procedure == nil {
+		array.allocator = context.allocator;
+	}
+	assert(array.allocator.procedure != nil);
+
+
+	ti := type_info_of(typeid_of(T));
+	ti = type_info_base(ti);
+	si := &ti.variant.(Type_Info_Struct);
+
+	field_count := uintptr(len(si.offsets) - 3);
+
+	if field_count == 0 {
+		return true;
+	}
+
+	cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
+	assert(cap_ptr^ == old_cap);
+
+
+	old_size := 0;
+	new_size := 0;
+
+	max_align := 0;
+	for i in 0..<field_count {
+		type := si.types[i].variant.(Type_Info_Pointer).elem;
+		max_align = max(max_align, type.align);
+
+		old_size = align_forward_int(old_size, type.align);
+		new_size = align_forward_int(new_size, type.align);
+
+		old_size += type.size * old_cap;
+		new_size += type.size * capacity;
+	}
+
+	old_size = align_forward_int(old_size, max_align);
+	new_size = align_forward_int(new_size, max_align);
+
+	old_data := (^rawptr)(array)^;
+
+	new_data := array.allocator.procedure(
+		array.allocator.data, .Alloc, new_size, max_align,
+		nil, old_size, 0, loc,
+	);
+	if new_data == nil {
+		return false;
+	}
+
+
+	cap_ptr^ = capacity;
+
+	old_offset := 0;
+	new_offset := 0;
+	for i in 0..<field_count {
+		type := si.types[i].variant.(Type_Info_Pointer).elem;
+		max_align = max(max_align, type.align);
+
+		old_offset = align_forward_int(old_offset, type.align);
+		new_offset = align_forward_int(new_offset, type.align);
+
+		new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
+		old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
+
+		mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
+
+		(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
+
+		old_offset += type.size * old_cap;
+		new_offset += type.size * capacity;
+	}
+
+	array.allocator.procedure(
+		array.allocator.data, .Free, 0, max_align,
+		old_data, old_size, 0, loc,
+	);
+
+	return true;
+}
+
+@builtin
+append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
+	if array == nil {
+		return;
+	}
+
+	arg_len := 1;
+
+	if cap(array) <= len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve_soa(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		ti := type_info_of(typeid_of(T));
+		ti = type_info_base(ti);
+		si := &ti.variant.(Type_Info_Struct);
+		field_count := uintptr(len(si.offsets) - 3);
+
+		if field_count == 0 {
+			return;
+		}
+
+		data := (^rawptr)(array)^;
+
+		len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
+
+
+		soa_offset := 0;
+		item_offset := 0;
+
+		arg_copy := arg;
+		arg_ptr := &arg_copy;
+
+		max_align := 0;
+		for i in 0..<field_count {
+			type := si.types[i].variant.(Type_Info_Pointer).elem;
+			max_align = max(max_align, type.align);
+
+			soa_offset  = align_forward_int(soa_offset, type.align);
+			item_offset = align_forward_int(item_offset, type.align);
+
+			dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
+			src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
+			mem_copy(dst, src, type.size);
+
+			soa_offset  += type.size * cap(array);
+			item_offset += type.size;
+		}
+
+		len_ptr^ += arg_len;
+	}
+}
+
+@builtin
+append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
+	if array == nil {
+		return;
+	}
+
+	arg_len := len(args);
+	if arg_len == 0 {
+		return;
+	}
+
+	if cap(array) <= len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve_soa(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		ti := type_info_of(typeid_of(T));
+		ti = type_info_base(ti);
+		si := &ti.variant.(Type_Info_Struct);
+		field_count := uintptr(len(si.offsets) - 3);
+
+		if field_count == 0 {
+			return;
+		}
+
+		data := (^rawptr)(array)^;
+
+		len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
+
+
+		soa_offset := 0;
+		item_offset := 0;
+
+		args_ptr := &args[0];
+
+		max_align := 0;
+		for i in 0..<field_count {
+			type := si.types[i].variant.(Type_Info_Pointer).elem;
+			max_align = max(max_align, type.align);
+
+			soa_offset  = align_forward_int(soa_offset, type.align);
+			item_offset = align_forward_int(item_offset, type.align);
+
+			dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
+			src := uintptr(args_ptr) + uintptr(item_offset);
+			for j in 0..<arg_len {
+				d := rawptr(dst + uintptr(j*type.size));
+				s := rawptr(src + uintptr(j*size_of(E)));
+				mem_copy(d, s, type.size);
+			}
+
+			soa_offset  += type.size * cap(array);
+			item_offset += type.size;
+		}
+
+		len_ptr^ += arg_len;
+	}
+}
+
+// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
+@builtin
+append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
+	for arg in args {
+		append(array = array, args = transmute([]E)(arg), loc = loc);
+	}
+}
+
+// The append built-in procedure appends elements to the end of a dynamic array
+@builtin append :: proc{append_elem, append_elems, append_elem_string};
+
+// The append_soa built-in procedure appends elements to the end of an #soa dynamic array
+@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
+
+@builtin
+append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
+	if array == nil {
+		return;
+	}
+	resize(array, len(array)+1);
+}
+
+
+@builtin
+insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+	if array == nil {
+		return;
+	}
+	n := len(array);
+	m :: 1;
+	resize(array, n+m, loc);
+	if n+m <= len(array) {
+		when size_of(E) != 0 {
+			copy(array[index+m:], array[index:]);
+			array[index] = arg;
+		}
+		ok = true;
+	}
+	return;
+}
+
+@builtin
+insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+	if array == nil {
+		return;
+	}
+	if len(args) == 0 {
+		ok = true;
+		return;
+	}
+
+	n := len(array);
+	m := len(args);
+	resize(array, n+m, loc);
+	if n+m <= len(array) {
+		when size_of(E) != 0 {
+			copy(array[index+m:], array[index:]);
+			copy(array[index:], args);
+		}
+		ok = true;
+	}
+	return;
+}
+
+@builtin
+insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+	if array == nil {
+		return;
+	}
+	if len(args) == 0 {
+		ok = true;
+		return;
+	}
+
+	n := len(array);
+	m := len(args);
+	resize(array, n+m, loc);
+	if n+m <= len(array) {
+		copy(array[index+m:], array[index:]);
+		copy(array[index:], args);
+		ok = true;
+	}
+	return;
+}
+
+@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
+
+
+
+
+@builtin
+clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
+	if array != nil {
+		(^Raw_Dynamic_Array)(array).len = 0;
+	}
+}
+
+@builtin
+reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+	if array == nil {
+		return false;
+	}
+	a := (^Raw_Dynamic_Array)(array);
+
+	if capacity <= a.cap {
+		return true;
+	}
+
+	if a.allocator.procedure == nil {
+		a.allocator = context.allocator;
+	}
+	assert(a.allocator.procedure != nil);
+
+	old_size  := a.cap * size_of(E);
+	new_size  := capacity * size_of(E);
+	allocator := a.allocator;
+
+	new_data := allocator.procedure(
+		allocator.data, .Resize, new_size, align_of(E),
+		a.data, old_size, 0, loc,
+	);
+	if new_data == nil {
+		return false;
+	}
+
+	a.data = new_data;
+	a.cap = capacity;
+	return true;
+}
+
+@builtin
+resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
+	if array == nil {
+		return false;
+	}
+	a := (^Raw_Dynamic_Array)(array);
+
+	if length <= a.cap {
+		a.len = max(length, 0);
+		return true;
+	}
+
+	if a.allocator.procedure == nil {
+		a.allocator = context.allocator;
+	}
+	assert(a.allocator.procedure != nil);
+
+	old_size  := a.cap * size_of(E);
+	new_size  := length * size_of(E);
+	allocator := a.allocator;
+
+	new_data := allocator.procedure(
+		allocator.data, .Resize, new_size, align_of(E),
+		a.data, old_size, 0, loc,
+	);
+	if new_data == nil {
+		return false;
+	}
+
+	a.data = new_data;
+	a.len = length;
+	a.cap = length;
+	return true;
+}
+
+
+
+@builtin
+incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
+	s^ |= {elem};
+	return s^;
+}
+@builtin
+incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
+	for elem in elems {
+		s^ |= {elem};
+	}
+	return s^;
+}
+@builtin
+incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
+	s^ |= other;
+	return s^;
+}
+@builtin
+excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
+	s^ &~= {elem};
+	return s^;
+}
+@builtin
+excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
+	for elem in elems {
+		s^ &~= {elem};
+	}
+	return s^;
+}
+@builtin
+excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
+	s^ &~= other;
+	return s^;
+}
+
+@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
+@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
+
+
+@builtin
+card :: proc(s: $S/bit_set[$E; $U]) -> int {
+	when size_of(S) == 1 {
+		foreign { @(link_name="llvm.ctpop.i8")  count_ones :: proc(i: u8) -> u8 --- }
+		return int(count_ones(transmute(u8)s));
+	} else when size_of(S) == 2 {
+		foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
+		return int(count_ones(transmute(u16)s));
+	} else when size_of(S) == 4 {
+		foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
+		return int(count_ones(transmute(u32)s));
+	} else when size_of(S) == 8 {
+		foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
+		return int(count_ones(transmute(u64)s));
+	} else when size_of(S) == 16 {
+		foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
+		return int(count_ones(transmute(u128)s));
+	} else {
+		#panic("Unhandled card bit_set size");
+	}
+}
+
+
+
+@builtin
+raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
+	return (^E)(a);
+}
+@builtin
+raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
+	ptr := (transmute(Raw_Slice)s).data;
+	return (^E)(ptr);
+}
+@builtin
+raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
+	ptr := (transmute(Raw_Dynamic_Array)s).data;
+	return (^E)(ptr);
+}
+@builtin
+raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
+	return (transmute(Raw_String)s).data;
+}
+
+@builtin
+raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
+
+
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+assert :: proc(condition: bool, message := "", loc := #caller_location) {
+	if !condition {
+		proc(message: string, loc: Source_Code_Location) {
+			p := context.assertion_failure_proc;
+			if p == nil {
+				p = default_assertion_failure_proc;
+			}
+			p("runtime assertion", message, loc);
+		}(message, loc);
+	}
+}
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+panic :: proc(message: string, loc := #caller_location) -> ! {
+	p := context.assertion_failure_proc;
+	if p == nil {
+		p = default_assertion_failure_proc;
+	}
+	p("panic", message, loc);
+}
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+unimplemented :: proc(message := "", loc := #caller_location) -> ! {
+	p := context.assertion_failure_proc;
+	if p == nil {
+		p = default_assertion_failure_proc;
+	}
+	p("not yet implemented", message, loc);
+}
+
+@builtin
+@(disabled=ODIN_DISABLE_ASSERT)
+unreachable :: proc(message := "", loc := #caller_location) -> ! {
+	p := context.assertion_failure_proc;
+	if p == nil {
+		p = default_assertion_failure_proc;
+	}
+	if message != "" {
+		p("internal error", message, loc);
+	} else {
+		p("internal error", "entered unreachable code", loc);
+	}
+}

+ 100 - 0
core/runtime/dynamic_array_internal.odin

@@ -0,0 +1,100 @@
+package runtime
+
+__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
+	array := (^Raw_Dynamic_Array)(array_);
+	array.allocator = context.allocator;
+	assert(array.allocator.procedure != nil);
+
+	if cap > 0 {
+		__dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
+		array.len = len;
+	}
+}
+
+__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
+	array := (^Raw_Dynamic_Array)(array_);
+
+	// NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
+	// assuming that appending/reserving will set the allocator, if it is not already set.
+	if array.allocator.procedure == nil {
+		array.allocator = context.allocator;
+	}
+	assert(array.allocator.procedure != nil);
+
+	if cap <= array.cap {
+		return true;
+	}
+
+	old_size  := array.cap * elem_size;
+	new_size  := cap * elem_size;
+	allocator := array.allocator;
+
+	new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
+	if new_data != nil || elem_size == 0 {
+		array.data = new_data;
+		array.cap = cap;
+		return true;
+	}
+	return false;
+}
+
+__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
+	array := (^Raw_Dynamic_Array)(array_);
+
+	ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
+	if ok {
+		array.len = len;
+	}
+	return ok;
+}
+
+
+__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
+                               items: rawptr, item_count: int, loc := #caller_location) -> int {
+	array := (^Raw_Dynamic_Array)(array_);
+
+	if items == nil    {
+		return 0;
+	}
+	if item_count <= 0 {
+		return 0;
+	}
+
+
+	ok := true;
+	if array.cap <= array.len+item_count {
+		cap := 2 * array.cap + max(8, item_count);
+		ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
+	}
+	// TODO(bill): Better error handling for failed reservation
+	if !ok {
+		return array.len;
+	}
+
+	assert(array.data != nil);
+	data := uintptr(array.data) + uintptr(elem_size*array.len);
+
+	mem_copy(rawptr(data), items, elem_size * item_count);
+	array.len += item_count;
+	return array.len;
+}
+
+__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
+	array := (^Raw_Dynamic_Array)(array_);
+
+	ok := true;
+	if array.cap <= array.len+1 {
+		cap := 2 * array.cap + max(8, 1);
+		ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
+	}
+	// TODO(bill): Better error handling for failed reservation
+	if !ok {
+		return array.len;
+	}
+
+	assert(array.data != nil);
+	data := uintptr(array.data) + uintptr(elem_size*array.len);
+	mem_zero(rawptr(data), elem_size);
+	array.len += 1;
+	return array.len;
+}

+ 394 - 0
core/runtime/dynamic_map_internal.odin

@@ -0,0 +1,394 @@
+package runtime
+
+import "intrinsics"
+_ :: intrinsics;
+
+INITIAL_MAP_CAP :: 16;
+
+// Temporary data structure for comparing hashes and keys
+Map_Hash :: struct {
+	hash:    uintptr,
+	key_ptr: rawptr, // address of Map_Entry_Header.key
+}
+
+__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) {
+	hasher := intrinsics.type_hasher_proc(K);
+	map_hash.key_ptr = k;
+	map_hash.hash = hasher(k, 0);
+	return;
+}
+
+__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) {
+	hash.hash = entry.hash;
+	hash.key_ptr = rawptr(uintptr(entry) + h.key_offset);
+	return;
+}
+
+
+
+Map_Find_Result :: struct {
+	hash_index:  int,
+	entry_prev:  int,
+	entry_index: int,
+}
+
+Map_Entry_Header :: struct {
+	hash: uintptr,
+	next: int,
+/*
+	key:   Key_Value,
+	value: Value_Type,
+*/
+}
+
+Map_Header :: struct {
+	m:             ^Raw_Map,
+	equal:         Equal_Proc,
+
+	entry_size:    int,
+	entry_align:   int,
+
+	key_offset:    uintptr,
+	key_size:      int,
+
+	value_offset:  uintptr,
+	value_size:    int,
+}
+
+INITIAL_HASH_SEED :: 0xcbf29ce484222325;
+
+_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 {
+	h: u64 = seed;
+	for b in data {
+		h = (h ~ u64(b)) * 0x100000001b3;
+	}
+	return h;
+}
+
+default_hash :: inline proc "contextless" (data: []byte) -> uintptr {
+	return uintptr(_fnv64a(data));
+}
+default_hash_string :: inline proc "contextless" (s: string) -> uintptr {
+	return default_hash(transmute([]byte)(s));
+}
+default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintptr {
+	s := Raw_Slice{data, size};
+	return default_hash(transmute([]byte)(s));
+}
+
+@(private)
+_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 {
+	h := u64(seed) + 0xcbf29ce484222325;
+	p := uintptr(data);
+	inline for _ in 0..<N {
+		b := u64((^byte)(p)^);
+		h = (h ~ b) * 0x100000001b3;
+		p += 1;
+	}
+	return uintptr(h);
+}
+
+default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr {
+	h := u64(seed) + 0xcbf29ce484222325;
+	p := uintptr(data);
+	for _ in 0..<N {
+		b := u64((^byte)(p)^);
+		h = (h ~ b) * 0x100000001b3;
+		p += 1;
+	}
+	return uintptr(h);
+}
+
+// NOTE(bill): There are loads of predefined ones to improve optimizations for small types
+
+default_hasher1  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  1); }
+default_hasher2  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  2); }
+default_hasher3  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  3); }
+default_hasher4  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  4); }
+default_hasher5  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  5); }
+default_hasher6  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  6); }
+default_hasher7  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  7); }
+default_hasher8  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  8); }
+default_hasher9  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed,  9); }
+default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); }
+default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); }
+default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); }
+default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 13); }
+default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 14); }
+default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 15); }
+default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); }
+
+default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
+	h := u64(seed) + 0xcbf29ce484222325;
+	str := (^[]byte)(data)^;
+	for b in str {
+		h = (h ~ u64(b)) * 0x100000001b3;
+	}
+	return uintptr(h);
+}
+default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
+	h := u64(seed) + 0xcbf29ce484222325;
+	ptr := (^uintptr)(data)^;
+	for (^byte)(ptr)^ != 0 {
+		b := (^byte)(ptr)^;
+		h = (h ~ u64(b)) * 0x100000001b3;
+		ptr += 1;
+	}
+	return uintptr(h);
+}
+
+
+
+source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr {
+	hash := _fnv64a(transmute([]byte)s.file_path);
+	hash = hash ~ (u64(s.line) * 0x100000001b3);
+	hash = hash ~ (u64(s.column) * 0x100000001b3);
+	return uintptr(hash);
+}
+
+
+
+__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
+	header := Map_Header{m = (^Raw_Map)(m)};
+	Entry :: struct {
+		hash:  uintptr,
+		next:  int,
+		key:   K,
+		value: V,
+	};
+
+	header.equal = intrinsics.type_equal_proc(K);
+
+	header.entry_size    = int(size_of(Entry));
+	header.entry_align   = int(align_of(Entry));
+
+	header.key_offset    = uintptr(offset_of(Entry, key));
+	header.key_size      = int(size_of(K));
+
+	header.value_offset  = uintptr(offset_of(Entry, value));
+	header.value_size    = int(size_of(V));
+
+	return header;
+}
+
+__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
+	array := (^Raw_Slice)(array_);
+
+	if new_count < array.len {
+		return true;
+	}
+
+	assert(allocator.procedure != nil);
+
+	old_size := array.len*size_of(T);
+	new_size := new_count*size_of(T);
+
+	new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
+	if new_data == nil {
+		return false;
+	}
+	array.data = new_data;
+	array.len = new_count;
+	return true;
+}
+
+__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
+	__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
+
+	old_len := len(m.hashes);
+	__slice_resize(&m.hashes, cap, m.entries.allocator, loc);
+	for i in old_len..<len(m.hashes) {
+		m.hashes[i] = -1;
+	}
+
+}
+__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
+	new_header: Map_Header = header;
+	nm := Raw_Map{};
+	nm.entries.allocator = m.entries.allocator;
+	new_header.m = &nm;
+
+	c := context;
+	if m.entries.allocator.procedure != nil {
+		c.allocator = m.entries.allocator;
+	}
+	context = c;
+
+	new_count := new_count;
+	new_count = max(new_count, 2*m.entries.len);
+
+	__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
+	__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
+	for i in 0 ..< new_count {
+		nm.hashes[i] = -1;
+	}
+
+	for i in 0 ..< m.entries.len {
+		if len(nm.hashes) == 0 {
+			__dynamic_map_grow(new_header, loc);
+		}
+
+		entry_header := __dynamic_map_get_entry(header, i);
+		entry_hash := __get_map_hash_from_entry(header, entry_header);
+
+		fr := __dynamic_map_find(new_header, entry_hash);
+		j := __dynamic_map_add_entry(new_header, entry_hash, loc);
+		if fr.entry_prev < 0 {
+			nm.hashes[fr.hash_index] = j;
+		} else {
+			e := __dynamic_map_get_entry(new_header, fr.entry_prev);
+			e.next = j;
+		}
+
+		e := __dynamic_map_get_entry(new_header, j);
+		__dynamic_map_copy_entry(header, e, entry_header);
+		e.next = fr.entry_index;
+
+		if __dynamic_map_full(new_header) {
+			__dynamic_map_grow(new_header, loc);
+		}
+	}
+
+	delete(m.hashes, m.entries.allocator, loc);
+	free(m.entries.data, m.entries.allocator, loc);
+	header.m^ = nm;
+}
+
+__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
+	index := __dynamic_map_find(h, hash).entry_index;
+	if index >= 0 {
+		data := uintptr(__dynamic_map_get_entry(h, index));
+		return rawptr(data + h.value_offset);
+	}
+	return nil;
+}
+
+__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check {
+	index: int;
+	assert(value != nil);
+
+	if len(h.m.hashes) == 0 {
+		__dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
+		__dynamic_map_grow(h, loc);
+	}
+
+	fr := __dynamic_map_find(h, hash);
+	if fr.entry_index >= 0 {
+		index = fr.entry_index;
+	} else {
+		index = __dynamic_map_add_entry(h, hash, loc);
+		if fr.entry_prev >= 0 {
+			entry := __dynamic_map_get_entry(h, fr.entry_prev);
+			entry.next = index;
+		} else {
+			h.m.hashes[fr.hash_index] = index;
+		}
+	}
+	{
+		e := __dynamic_map_get_entry(h, index);
+		e.hash = hash.hash;
+
+		key := rawptr(uintptr(e) + h.key_offset);
+		mem_copy(key, hash.key_ptr, h.key_size);
+
+		val := rawptr(uintptr(e) + h.value_offset);
+		mem_copy(val, value, h.value_size);
+	}
+
+	if __dynamic_map_full(h) {
+		__dynamic_map_grow(h, loc);
+	}
+}
+
+
+__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
+	// TODO(bill): Determine an efficient growing rate
+	new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
+	__dynamic_map_rehash(h, new_count, loc);
+}
+
+__dynamic_map_full :: inline proc "contextless" (using h: Map_Header) -> bool {
+	return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
+}
+
+
+__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool {
+	if a.hash == b.hash {
+		return h.equal(a.key_ptr, b.key_ptr);
+	}
+	return false;
+}
+
+__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check {
+	fr := Map_Find_Result{-1, -1, -1};
+	if n := uintptr(len(m.hashes)); n > 0 {
+		fr.hash_index = int(hash.hash % n);
+		fr.entry_index = m.hashes[fr.hash_index];
+		for fr.entry_index >= 0 {
+			entry := __dynamic_map_get_entry(h, fr.entry_index);
+			entry_hash := __get_map_hash_from_entry(h, entry);
+			if __dynamic_map_hash_equal(h, entry_hash, hash) {
+				return fr;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = entry.next;
+		}
+	}
+	return fr;
+}
+
+__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int {
+	prev := m.entries.len;
+	c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
+	if c != prev {
+		end := __dynamic_map_get_entry(h, c-1);
+		end.hash = hash.hash;
+		mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size);
+		end.next = -1;
+	}
+	return prev;
+}
+
+__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) {
+	fr := __dynamic_map_find(h, hash);
+	if fr.entry_index >= 0 {
+		__dynamic_map_erase(h, fr);
+	}
+}
+
+__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
+	assert(0 <= index && index < m.entries.len);
+	return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
+}
+
+__dynamic_map_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) {
+	mem_copy(new, old, h.entry_size);
+}
+
+__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
+	if fr.entry_prev < 0 {
+		m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
+	} else {
+		prev := __dynamic_map_get_entry(h, fr.entry_prev);
+		curr := __dynamic_map_get_entry(h, fr.entry_index);
+		prev.next = curr.next;
+	}
+	if (fr.entry_index == m.entries.len-1) {
+		// NOTE(bill): No need to do anything else, just pop
+	} else {
+		old := __dynamic_map_get_entry(h, fr.entry_index);
+		end := __dynamic_map_get_entry(h, m.entries.len-1);
+		__dynamic_map_copy_entry(h, old, end);
+
+		old_hash := __get_map_hash_from_entry(h, old);
+
+		if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 {
+			last_entry := __dynamic_map_get_entry(h, last.entry_prev);
+			last_entry.next = fr.entry_index;
+		} else {
+			m.hashes[last.hash_index] = fr.entry_index;
+		}
+	}
+
+	m.entries.len -= 1;
+}

+ 57 - 4
core/runtime/error_checks.odin

@@ -23,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
 	}
 	handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
 		context = default_context();
-		print_caller_location(Source_Code_Location{file, line, column, "", 0});
+		print_caller_location(Source_Code_Location{file, line, column, ""});
 		print_string(" Index ");
 		print_i64(i64(index));
 		print_string(" is out of bounds range 0:");
@@ -36,7 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
 
 slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
 	context = default_context();
-	print_caller_location(Source_Code_Location{file, line, column, "", 0});
+	print_caller_location(Source_Code_Location{file, line, column, ""});
 	print_string(" Invalid slice indices: ");
 	print_i64(i64(lo));
 	print_string(":");
@@ -67,7 +67,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int,
 	}
 	handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
 		context = default_context();
-		print_caller_location(Source_Code_Location{file, line, column, "", 0});
+		print_caller_location(Source_Code_Location{file, line, column, ""});
 		print_string(" Invalid dynamic array values: ");
 		print_i64(i64(low));
 		print_string(":");
@@ -87,7 +87,7 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
 	}
 	handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
 		context = default_context();
-		print_caller_location(Source_Code_Location{file, line, column, "", 0});
+		print_caller_location(Source_Code_Location{file, line, column, ""});
 		print_string(" Invalid type assertion from ");
 		print_typeid(from);
 		print_string(" to ");
@@ -98,6 +98,59 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
 	handle_error(file, line, column, from, to);
 }
 
+type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid, from_data: rawptr) {
+	if ok {
+		return;
+	}
+
+	variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
+		if id == nil || data == nil {
+			return id;
+		}
+		ti := type_info_base(type_info_of(id));
+		#partial switch v in ti.variant {
+		case Type_Info_Any:
+			return (^any)(data).id;
+		case Type_Info_Union:
+			tag_ptr := uintptr(data) + v.tag_offset;
+			idx := 0;
+			switch v.tag_type.size {
+			case 1:  idx = int((^u8)(tag_ptr)^)   - 1;
+			case 2:  idx = int((^u16)(tag_ptr)^)  - 1;
+			case 4:  idx = int((^u32)(tag_ptr)^)  - 1;
+			case 8:  idx = int((^u64)(tag_ptr)^)  - 1;
+			case 16: idx = int((^u128)(tag_ptr)^) - 1;
+			}
+			if idx < 0 {
+				return nil;
+			} else if idx < len(v.variants) {
+				return v.variants[idx].id;
+			}
+		}
+		return id;
+	}
+
+	handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid, from_data: rawptr) {
+		context = default_context();
+
+		actual := variant_type(from, from_data);
+
+		print_caller_location(Source_Code_Location{file, line, column, ""});
+		print_string(" Invalid type assertion from ");
+		print_typeid(from);
+		print_string(" to ");
+		print_typeid(to);
+		if actual != from {
+			print_string(", actual type: ");
+			print_typeid(actual);
+		}
+		print_byte('\n');
+		type_assertion_trap();
+	}
+	handle_error(file, line, column, from, to, from_data);
+}
+
+
 make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
 	if 0 <= len {
 		return;

+ 24 - 17
core/runtime/internal.odin

@@ -93,18 +93,18 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
 		when ODIN_USE_LLVM_API {
 			when size_of(rawptr) == 8 {
 				@(link_name="llvm.memmove.p0i8.p0i8.i64")
-				llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+				llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
 			} else {
 				@(link_name="llvm.memmove.p0i8.p0i8.i32")
-				llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+				llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
 			}
 		} else {
 			when size_of(rawptr) == 8 {
 				@(link_name="llvm.memmove.p0i8.p0i8.i64")
-				llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
 			} else {
 				@(link_name="llvm.memmove.p0i8.p0i8.i32")
-				llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
 			}
 		}
 	}
@@ -121,18 +121,18 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
 		when ODIN_USE_LLVM_API {
 			when size_of(rawptr) == 8 {
 				@(link_name="llvm.memcpy.p0i8.p0i8.i64")
-				llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+				llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
 			} else {
 				@(link_name="llvm.memcpy.p0i8.p0i8.i32")
-				llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+				llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
 			}
 		} else {
 			when size_of(rawptr) == 8 {
 				@(link_name="llvm.memcpy.p0i8.p0i8.i64")
-				llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
 			} else {
 				@(link_name="llvm.memcpy.p0i8.p0i8.i32")
-				llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
 			}
 		}
 	}
@@ -180,9 +180,16 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int =
 	}
 	return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
 }
-
-
+memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
+	return memory_compare(a, b, n) == 0;
+}
 memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
+	switch {
+	case a == b:   return 0;
+	case a == nil: return -1;
+	case b == nil: return +1;
+	}
+
 	x := uintptr(a);
 	y := uintptr(b);
 	n := uintptr(n);
@@ -389,45 +396,45 @@ string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
 	return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
 }
 
-@(default_calling_convention = "c")
+@(default_calling_convention = "none")
 foreign {
 	@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
 	@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
 }
 abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
 	foreign {
-		@(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
+		@(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
 	}
 	return _abs(x);
 }
 abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
 	foreign {
-		@(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
+		@(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 ---
 	}
 	return _abs(x);
 }
 
 min_f32 :: proc(a, b: f32) -> f32 {
 	foreign {
-		@(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
+		@(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
 	}
 	return _min(a, b);
 }
 min_f64 :: proc(a, b: f64) -> f64 {
 	foreign {
-		@(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
+		@(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 ---
 	}
 	return _min(a, b);
 }
 max_f32 :: proc(a, b: f32) -> f32 {
 	foreign {
-		@(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
+		@(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
 	}
 	return _max(a, b);
 }
 max_f64 :: proc(a, b: f64) -> f64 {
 	foreign {
-		@(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
+		@(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 ---
 	}
 	return _max(a, b);
 }

+ 135 - 0
core/runtime/internal_linux.odin

@@ -0,0 +1,135 @@
+package runtime
+
+@(link_name="__umodti3")
+umodti3 :: proc "c" (a, b: u128) -> u128 {
+	r: u128 = ---;
+	_ = udivmod128(a, b, &r);
+	return r;
+}
+
+
+@(link_name="__udivmodti4")
+udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
+	return udivmod128(a, b, rem);
+}
+
+@(link_name="__udivti3")
+udivti3 :: proc "c" (a, b: u128) -> u128 {
+	return udivmodti4(a, b, nil);
+}
+
+
+@(link_name="__modti3")
+modti3 :: proc "c" (a, b: i128) -> i128 {
+	s_a := a >> (128 - 1);
+	s_b := b >> (128 - 1);
+	an := (a ~ s_a) - s_a;
+	bn := (b ~ s_b) - s_b;
+
+	r: u128 = ---;
+	_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
+	return (transmute(i128)r ~ s_a) - s_a;
+}
+
+
+@(link_name="__divmodti4")
+divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
+	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
+	return transmute(i128)u;
+}
+
+@(link_name="__divti3")
+divti3 :: proc "c" (a, b: i128) -> i128 {
+	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
+	return transmute(i128)u;
+}
+
+
+@(link_name="__fixdfti")
+fixdfti :: proc(a: u64) -> i128 {
+	significandBits :: 52;
+	typeWidth       :: (size_of(u64)*8);
+	exponentBits    :: (typeWidth - significandBits - 1);
+	maxExponent     :: ((1 << exponentBits) - 1);
+	exponentBias    :: (maxExponent >> 1);
+
+	implicitBit     :: (u64(1) << significandBits);
+	significandMask :: (implicitBit - 1);
+	signBit         :: (u64(1) << (significandBits + exponentBits));
+	absMask         :: (signBit - 1);
+	exponentMask    :: (absMask ~ significandMask);
+
+	// Break a into sign, exponent, significand
+	aRep := a;
+	aAbs := aRep & absMask;
+	sign := i128(-1 if aRep & signBit != 0 else 1);
+	exponent := u64((aAbs >> significandBits) - exponentBias);
+	significand := u64((aAbs & significandMask) | implicitBit);
+
+	// If exponent is negative, the result is zero.
+	if exponent < 0 {
+		return 0;
+	}
+
+	// If the value is too large for the integer type, saturate.
+	if exponent >= size_of(i128) * 8 {
+		return max(i128) if sign == 1 else min(i128);
+	}
+
+	// If 0 <= exponent < significandBits, right shift to get the result.
+	// Otherwise, shift left.
+	if exponent < significandBits {
+		return sign * i128(significand >> (significandBits - exponent));
+	} else {
+		return sign * (i128(significand) << (exponent - significandBits));
+	}
+
+}
+
+@(default_calling_convention = "none")
+foreign {
+	@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
+}
+
+
+@(link_name="__floattidf")
+floattidf :: proc(a: i128) -> f64 {
+	DBL_MANT_DIG :: 53;
+	if a == 0 {
+		return 0.0;
+	}
+	a := a;
+	N :: size_of(i128) * 8;
+	s := a >> (N-1);
+	a = (a ~ s) - s;
+	sd: = N - _clz_i128(a);  // number of significant digits
+	e := u32(sd - 1);        // exponent 
+	if sd > DBL_MANT_DIG {
+		switch sd {
+		case DBL_MANT_DIG + 1:
+			a <<= 1;
+		case DBL_MANT_DIG + 2:
+			// okay
+		case:
+			a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
+				i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
+		};
+
+		a |= i128((a & 4) != 0);  
+		a += 1; 
+		a >>= 2;
+
+		if a & (1 << DBL_MANT_DIG) != 0 {
+			a >>= 1;
+			e += 1;
+		}
+	} else {
+		a <<= u128(DBL_MANT_DIG - sd);
+	}
+	fb: [2]u32;
+	fb[1] = (u32(s) & 0x80000000) |        // sign
+	        ((e + 1023) << 20)      |      // exponent
+	        ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
+	fb[1] = u32(a);                        // mantissa-low
+	return transmute(f64)fb;
+}

+ 92 - 92
core/runtime/internal_windows.odin

@@ -2,134 +2,134 @@ package runtime
 
 @(link_name="__umodti3")
 umodti3 :: proc "c" (a, b: u128) -> u128 {
-	r: u128 = ---;
-	_ = udivmod128(a, b, &r);
-	return r;
+    r: u128 = ---;
+    _ = udivmod128(a, b, &r);
+    return r;
 }
 
 
 @(link_name="__udivmodti4")
 udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
-	return udivmod128(a, b, rem);
+    return udivmod128(a, b, rem);
 }
 
 @(link_name="__udivti3")
 udivti3 :: proc "c" (a, b: u128) -> u128 {
-	return udivmodti4(a, b, nil);
+    return udivmodti4(a, b, nil);
 }
 
 
 @(link_name="__modti3")
 modti3 :: proc "c" (a, b: i128) -> i128 {
-	s_a := a >> (128 - 1);
-	s_b := b >> (128 - 1);
-	an := (a ~ s_a) - s_a;
-	bn := (b ~ s_b) - s_b;
-
-	r: u128 = ---;
-	_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
-	return (transmute(i128)r ~ s_a) - s_a;
+    s_a := a >> (128 - 1);
+    s_b := b >> (128 - 1);
+    an := (a ~ s_a) - s_a;
+    bn := (b ~ s_b) - s_b;
+
+    r: u128 = ---;
+    _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
+    return (transmute(i128)r ~ s_a) - s_a;
 }
 
 
 @(link_name="__divmodti4")
 divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
-	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
-	return transmute(i128)u;
+    u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
+    return transmute(i128)u;
 }
 
 @(link_name="__divti3")
 divti3 :: proc "c" (a, b: i128) -> i128 {
-	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
-	return transmute(i128)u;
+    u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
+    return transmute(i128)u;
 }
 
 
 @(link_name="__fixdfti")
 fixdfti :: proc(a: u64) -> i128 {
-	significandBits :: 52;
-	typeWidth       :: (size_of(u64)*8);
-	exponentBits    :: (typeWidth - significandBits - 1);
-	maxExponent     :: ((1 << exponentBits) - 1);
-	exponentBias    :: (maxExponent >> 1);
-
-	implicitBit     :: (u64(1) << significandBits);
-	significandMask :: (implicitBit - 1);
-	signBit         :: (u64(1) << (significandBits + exponentBits));
-	absMask         :: (signBit - 1);
-	exponentMask    :: (absMask ~ significandMask);
-
-	// Break a into sign, exponent, significand
-	aRep := a;
-	aAbs := aRep & absMask;
-	sign := i128(-1 if aRep & signBit != 0 else 1);
-	exponent := u64((aAbs >> significandBits) - exponentBias);
-	significand := u64((aAbs & significandMask) | implicitBit);
-
-	// If exponent is negative, the result is zero.
-	if exponent < 0 {
-		return 0;
-	}
-
-	// If the value is too large for the integer type, saturate.
-	if exponent >= size_of(i128) * 8 {
-		return max(i128) if sign == 1 else min(i128);
-	}
-
-	// If 0 <= exponent < significandBits, right shift to get the result.
-	// Otherwise, shift left.
-	if exponent < significandBits {
-		return sign * i128(significand >> (significandBits - exponent));
-	} else {
-		return sign * (i128(significand) << (exponent - significandBits));
-	}
+    significandBits :: 52;
+    typeWidth       :: (size_of(u64)*8);
+    exponentBits    :: (typeWidth - significandBits - 1);
+    maxExponent     :: ((1 << exponentBits) - 1);
+    exponentBias    :: (maxExponent >> 1);
+
+    implicitBit     :: (u64(1) << significandBits);
+    significandMask :: (implicitBit - 1);
+    signBit         :: (u64(1) << (significandBits + exponentBits));
+    absMask         :: (signBit - 1);
+    exponentMask    :: (absMask ~ significandMask);
+
+    // Break a into sign, exponent, significand
+    aRep := a;
+    aAbs := aRep & absMask;
+    sign := i128(-1 if aRep & signBit != 0 else 1);
+    exponent := u64((aAbs >> significandBits) - exponentBias);
+    significand := u64((aAbs & significandMask) | implicitBit);
+
+    // If exponent is negative, the result is zero.
+    if exponent < 0 {
+        return 0;
+    }
+
+    // If the value is too large for the integer type, saturate.
+    if exponent >= size_of(i128) * 8 {
+        return max(i128) if sign == 1 else min(i128);
+    }
+
+    // If 0 <= exponent < significandBits, right shift to get the result.
+    // Otherwise, shift left.
+    if exponent < significandBits {
+        return sign * i128(significand >> (significandBits - exponent));
+    } else {
+        return sign * (i128(significand) << (exponent - significandBits));
+    }
 
 }
 
 @(default_calling_convention = "none")
 foreign {
-	@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
+    @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
 }
 
 
 @(link_name="__floattidf")
 floattidf :: proc(a: i128) -> f64 {
-	DBL_MANT_DIG :: 53;
-	if a == 0 {
-		return 0.0;
-	}
-	a := a;
-	N :: size_of(i128) * 8;
-	s := a >> (N-1);
-	a = (a ~ s) - s;
-	sd: = N - _clz_i128(a);  // number of significant digits
-	e := u32(sd - 1);        // exponent 
-	if sd > DBL_MANT_DIG {
-		switch sd {
-		case DBL_MANT_DIG + 1:
-			a <<= 1;
-		case DBL_MANT_DIG + 2:
-			// okay
-		case:
-			a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
-				i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
-		};
-
-		a |= i128((a & 4) != 0);  
-		a += 1; 
-		a >>= 2;
-
-		if a & (1 << DBL_MANT_DIG) != 0 {
-			a >>= 1;
-			e += 1;
-		}
-	} else {
-		a <<= u128(DBL_MANT_DIG - sd);
-	}
-	fb: [2]u32;
-	fb[1] = (u32(s) & 0x80000000) |        // sign
-	        ((e + 1023) << 20)      |      // exponent
-	        ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
-	fb[1] = u32(a);                        // mantissa-low
-	return transmute(f64)fb;
+    DBL_MANT_DIG :: 53;
+    if a == 0 {
+        return 0.0;
+    }
+    a := a;
+    N :: size_of(i128) * 8;
+    s := a >> (N-1);
+    a = (a ~ s) - s;
+    sd: = N - _clz_i128(a);  // number of significant digits
+    e := u32(sd - 1);        // exponent 
+    if sd > DBL_MANT_DIG {
+        switch sd {
+        case DBL_MANT_DIG + 1:
+            a <<= 1;
+        case DBL_MANT_DIG + 2:
+            // okay
+        case:
+            a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
+                i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
+        };
+
+        a |= i128((a & 4) != 0);  
+        a += 1; 
+        a >>= 2;
+
+        if a & (1 << DBL_MANT_DIG) != 0 {
+            a >>= 1;
+            e += 1;
+        }
+    } else {
+        a <<= u128(DBL_MANT_DIG - sd);
+    }
+    fb: [2]u32;
+    fb[1] = (u32(s) & 0x80000000) |        // sign
+            ((e + 1023) << 20)      |      // exponent
+            ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
+    fb[1] = u32(a);                        // mantissa-low
+    return transmute(f64)fb;
 }

+ 1 - 1
core/runtime/print.odin

@@ -350,7 +350,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
 		print_byte(']');
 
 	case Type_Info_Opaque:
-		print_string("opaque ");
+		print_string("#opaque ");
 		print_type(info.elem);
 
 	case Type_Info_Simd_Vector:

+ 5 - 6
core/runtime/procs_windows_amd64.odin

@@ -2,15 +2,14 @@ package runtime
 
 foreign import kernel32 "system:Kernel32.lib"
 
-windows_trap_array_bounds :: proc "contextless" () -> ! {
-	DWORD :: u32;
-	ULONG_PTR :: uint;
+@(private)
+foreign kernel32 {
+	RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
+}
 
+windows_trap_array_bounds :: proc "contextless" () -> ! {
 	EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C;
 
-	foreign kernel32 {
-		RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
-	}
 
 	RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil);
 }

+ 42 - 1
core/slice/slice.odin

@@ -216,7 +216,7 @@ split_last :: proc(array: $T/[]$E) -> (rest: T, last: E) {
 first :: proc(array: $T/[]$E) -> E {
 	return array[0];
 }
-last :: proc(array: $T/[]$E) -> ^E {
+last :: proc(array: $T/[]$E) -> E {
 	return array[len(array)-1];
 }
 
@@ -252,3 +252,44 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
 as_ptr :: proc(array: $T/[]$E) -> ^E {
 	return raw_data(array);
 }
+
+
+mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> []V {
+	r := make([]V, len(s), allocator);
+	for v, i in s {
+		r[i] = f(v);
+	}
+	return r;
+}
+
+reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V {
+	r := initializer;
+	for v in s {
+		r = f(r, v);
+	}
+	return r;
+}
+
+filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -> S {
+	r := make([dynamic]S, 0, 0, allocator);
+	for v in s {
+		if f(v) {
+			append(&r, v);
+		}
+	}
+	return r[:];
+}
+
+
+
+dot_product :: proc(a, b: $S/[]$T) -> T
+	where intrinsics.type_is_numeric(T) {
+	if len(a) != len(b) {
+		panic("slice.dot_product: slices of unequal length");
+	}
+	r: T;
+	#no_bounds_check for _, i in a {
+		r += a[i] * b[i];
+	}
+	return r;
+}

+ 4 - 4
core/slice/sort.odin

@@ -54,17 +54,17 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
 
 // TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
 sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
-	context.user_ptr = rawptr(key);
+	context._internal = rawptr(key);
 	sort_by(data, proc(i, j: E) -> bool {
-		k := (proc(E) -> K)(context.user_ptr);
+		k := (proc(E) -> K)(context._internal);
 		return k(i) < k(j);
 	});
 }
 
 reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
-	context.user_ptr = rawptr(key);
+	context._internal = rawptr(key);
 	sort_by(data, proc(i, j: E) -> bool {
-		k := (proc(E) -> K)(context.user_ptr);
+		k := (proc(E) -> K)(context._internal);
 		return k(j) < k(i);
 	});
 }

+ 176 - 81
core/strings/builder.odin

@@ -3,15 +3,12 @@ package strings
 import "core:mem"
 import "core:unicode/utf8"
 import "core:strconv"
+import "core:io"
 
 Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool);
 
 Builder :: struct {
 	buf: [dynamic]byte,
-
-	// The custom flush procedure allows for the ability to flush the buffer, i.e. write to file
-	flush_proc: Builder_Flush_Proc,
-	flush_data: rawptr,
 }
 
 make_builder_none :: proc(allocator := context.allocator) -> Builder {
@@ -32,6 +29,61 @@ make_builder :: proc{
 	make_builder_len_cap,
 };
 
+init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
+	b.buf = make([dynamic]byte, allocator);
+}
+
+init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
+	b.buf = make([dynamic]byte, len, allocator);
+}
+
+init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
+	b.buf = make([dynamic]byte, len, cap, allocator);
+}
+
+init_builder :: proc{
+	init_builder_none,
+	init_builder_len,
+	init_builder_len_cap,
+};
+
+@(private)
+_builder_stream_vtable := &io.Stream_VTable{
+	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		b := (^Builder)(s.stream_data);
+		n = write_bytes(b, p);
+		if len(b.buf) == cap(b.buf) {
+			err = .EOF;
+		}
+		return;
+	},
+	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+		b := (^Builder)(s.stream_data);
+		_ = write_byte(b, c);
+		if len(b.buf) == cap(b.buf) {
+			return .EOF;
+		}
+		return nil;
+	},
+	impl_size = proc(s: io.Stream) -> i64 {
+		b := (^Builder)(s.stream_data);
+		return i64(len(b.buf));
+	},
+	impl_destroy = proc(s: io.Stream) -> io.Error {
+		b := (^Builder)(s.stream_data);
+		delete(b.buf);
+		return .None;
+	},
+};
+
+to_stream :: proc(b: ^Builder) -> io.Stream {
+	return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b};
+}
+to_writer :: proc(b: ^Builder) -> io.Writer {
+	w, _ := io.to_writer(to_stream(b));
+	return w;
+}
+
 
 
 
@@ -48,24 +100,6 @@ reset_builder :: proc(b: ^Builder) {
 	clear(&b.buf);
 }
 
-flush_builder :: proc(b: ^Builder) -> (was_reset: bool) {
-	if b.flush_proc != nil {
-		was_reset = b.flush_proc(b);
-		if was_reset {
-			reset_builder(b);
-
-		}
-	}
-	return;
-}
-
-flush_builder_check_space :: proc(b: ^Builder, required: int) -> (was_reset: bool) {
-	if n := max(cap(b.buf) - len(b.buf), 0); n < required {
-		was_reset = flush_builder(b);
-	}
-	return;
-}
-
 
 builder_from_slice :: proc(backing: []byte) -> Builder {
 	s := transmute(mem.Raw_Slice)backing;
@@ -94,7 +128,6 @@ builder_space :: proc(b: Builder) -> int {
 }
 
 write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
-	flush_builder_check_space(b, 1);
 	if builder_space(b^) > 0 {
 		append(&b.buf, x);
 		n += 1;
@@ -105,7 +138,6 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
 write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
 	x := x;
 	for len(x) != 0 {
-		flush_builder_check_space(b, len(x));
 		space := builder_space(b^);
 		if space == 0 {
 			break; // No need to append
@@ -121,20 +153,56 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
 	return;
 }
 
-write_rune :: proc(b: ^Builder, r: rune) -> int {
-	if r < utf8.RUNE_SELF {
-		return write_byte(b, byte(r));
+write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
+	return io.write_rune(to_writer(b), r);
+}
+
+
+write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
+	return write_quoted_rune(to_writer(b), r);
+}
+
+@(private)
+_write_byte :: proc(w: io.Writer, c: byte) -> int {
+	err := io.write_byte(w, c);
+	return 1 if err == nil else 0;
+}
+
+
+write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
+	quote := byte('\'');
+	n += _write_byte(w, quote);
+	buf, width := utf8.encode_rune(r);
+	if width == 1 && r == utf8.RUNE_ERROR {
+		n += _write_byte(w, '\\');
+		n += _write_byte(w, 'x');
+		n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]);
+		n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]);
+	} else {
+		n += write_escaped_rune(w, r, quote);
 	}
+	n += _write_byte(w, quote);
+	return;
+}
 
-	s, n := utf8.encode_rune(r);
-	write_bytes(b, s[:n]);
-	return n;
+
+write_string :: proc{
+	write_string_builder,
+	write_string_writer,
+};
+
+write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
+	return write_string_writer(to_writer(b), s);
 }
 
-write_string :: proc(b: ^Builder, s: string) -> (n: int) {
-	return write_bytes(b, transmute([]byte)s);
+write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
+	n, _ = io.write(w, transmute([]byte)s);
+	return;
 }
 
+
+
+
 pop_byte :: proc(b: ^Builder) -> (r: byte) {
 	if len(b.buf) == 0 {
 		return 0;
@@ -156,8 +224,17 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
 @(private, static)
 DIGITS_LOWER := "0123456789abcdefx";
 
-write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
-	n += write_byte(b, quote);
+write_quoted_string :: proc{
+	write_quoted_string_builder,
+	write_quoted_string_writer,
+};
+
+write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
+	return write_quoted_string_writer(to_writer(b), str, quote);
+}
+
+write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
+	n += _write_byte(w, quote);
 	for width, s := 0, str; len(s) > 0; s = s[width:] {
 		r := rune(s[0]);
 		width = 1;
@@ -165,57 +242,75 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n:
 			r, width = utf8.decode_rune_in_string(s);
 		}
 		if width == 1 && r == utf8.RUNE_ERROR {
-			n += write_byte(b, '\\');
-			n += write_byte(b, 'x');
-			n += write_byte(b, DIGITS_LOWER[s[0]>>4]);
-			n += write_byte(b, DIGITS_LOWER[s[0]&0xf]);
+			n += _write_byte(w, '\\');
+			n += _write_byte(w, 'x');
+			n += _write_byte(w, DIGITS_LOWER[s[0]>>4]);
+			n += _write_byte(w, DIGITS_LOWER[s[0]&0xf]);
 			continue;
 		}
 
-		n += write_escaped_rune(b, r, quote);
+		n += write_escaped_rune(w, r, quote);
 
 	}
-	n += write_byte(b, quote);
+	n += _write_byte(w, quote);
 	return;
 }
 
+write_encoded_rune :: proc{
+	write_encoded_rune_builder,
+	write_encoded_rune_writer,
+};
 
-write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
+write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
+	return write_encoded_rune_writer(to_writer(b), r, write_quote);
+
+}
+write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
 	if write_quote {
-		n += write_byte(b, '\'');
+		n += _write_byte(w, '\'');
 	}
 	switch r {
-	case '\a': n += write_string(b, `\a"`);
-	case '\b': n += write_string(b, `\b"`);
-	case '\e': n += write_string(b, `\e"`);
-	case '\f': n += write_string(b, `\f"`);
-	case '\n': n += write_string(b, `\n"`);
-	case '\r': n += write_string(b, `\r"`);
-	case '\t': n += write_string(b, `\t"`);
-	case '\v': n += write_string(b, `\v"`);
+	case '\a': n += write_string(w, `\a"`);
+	case '\b': n += write_string(w, `\b"`);
+	case '\e': n += write_string(w, `\e"`);
+	case '\f': n += write_string(w, `\f"`);
+	case '\n': n += write_string(w, `\n"`);
+	case '\r': n += write_string(w, `\r"`);
+	case '\t': n += write_string(w, `\t"`);
+	case '\v': n += write_string(w, `\v"`);
 	case:
 		if r < 32 {
-			n += write_string(b, `\x`);
+			n += write_string(w, `\x`);
 			buf: [2]byte;
 			s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil);
 			switch len(s) {
-			case 0: n += write_string(b, "00");
-			case 1: n += write_byte(b, '0');
-			case 2: n += write_string(b, s);
+			case 0: n += write_string(w, "00");
+			case 1: n += _write_byte(w, '0');
+			case 2: n += write_string(w, s);
 			}
 		} else {
-			n += write_rune(b, r);
+			rn, _ := io.write_rune(w, r);
+			n += rn;
 		}
 
 	}
 	if write_quote {
-		n += write_byte(b, '\'');
+		n += _write_byte(w, '\'');
 	}
 	return;
 }
 
 
-write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
+write_escaped_rune :: proc{
+	write_escaped_rune_builder,
+	write_escaped_rune_writer,
+};
+
+write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
+	return write_escaped_rune_writer(to_writer(b), r, quote, html_safe);
+}
+
+write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
 	is_printable :: proc(r: rune) -> bool {
 		if r <= 0xff {
 			switch r {
@@ -233,54 +328,54 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
 	if html_safe {
 		switch r {
 		case '<', '>', '&':
-			n += write_byte(b, '\\');
-			n += write_byte(b, 'u');
+			n += _write_byte(w, '\\');
+			n += _write_byte(w, 'u');
 			for s := 12; s >= 0; s -= 4 {
-				n += write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
+				n += _write_byte(w, DIGITS_LOWER[r>>uint(s) & 0xf]);
 			}
 			return;
 		}
 	}
 
 	if r == rune(quote) || r == '\\' {
-		n += write_byte(b, '\\');
-		n += write_byte(b, byte(r));
+		n += _write_byte(w, '\\');
+		n += _write_byte(w, byte(r));
 		return;
 	} else if is_printable(r) {
-		n += write_encoded_rune(b, r, false);
+		n += write_encoded_rune(w, r, false);
 		return;
 	}
 	switch r {
-	case '\a': n += write_string(b, `\a`);
-	case '\b': n += write_string(b, `\b`);
-	case '\e': n += write_string(b, `\e`);
-	case '\f': n += write_string(b, `\f`);
-	case '\n': n += write_string(b, `\n`);
-	case '\r': n += write_string(b, `\r`);
-	case '\t': n += write_string(b, `\t`);
-	case '\v': n += write_string(b, `\v`);
+	case '\a': n += write_string(w, `\a`);
+	case '\b': n += write_string(w, `\b`);
+	case '\e': n += write_string(w, `\e`);
+	case '\f': n += write_string(w, `\f`);
+	case '\n': n += write_string(w, `\n`);
+	case '\r': n += write_string(w, `\r`);
+	case '\t': n += write_string(w, `\t`);
+	case '\v': n += write_string(w, `\v`);
 	case:
 		switch c := r; {
 		case c < ' ':
-			n += write_byte(b, '\\');
-			n += write_byte(b, 'x');
-			n += write_byte(b, DIGITS_LOWER[byte(c)>>4]);
-			n += write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
+			n += _write_byte(w, '\\');
+			n += _write_byte(w, 'x');
+			n += _write_byte(w, DIGITS_LOWER[byte(c)>>4]);
+			n += _write_byte(w, DIGITS_LOWER[byte(c)&0xf]);
 
 		case c > utf8.MAX_RUNE:
 			c = 0xfffd;
 			fallthrough;
 		case c < 0x10000:
-			n += write_byte(b, '\\');
-			n += write_byte(b, 'u');
+			n += _write_byte(w, '\\');
+			n += _write_byte(w, 'u');
 			for s := 12; s >= 0; s -= 4 {
-				n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
+				n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
 			}
 		case:
-			n += write_byte(b, '\\');
-			n += write_byte(b, 'U');
+			n += _write_byte(w, '\\');
+			n += _write_byte(w, 'U');
 			for s := 28; s >= 0; s -= 4 {
-				n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
+				n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
 			}
 		}
 	}

+ 269 - 0
core/strings/conversion.odin

@@ -0,0 +1,269 @@
+package strings
+
+import "core:io"
+import "core:unicode"
+import "core:unicode/utf8"
+
+to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
+	if len(s) == 0 {
+		return "";
+	}
+
+	b: Builder;
+	init_builder(&b, 0, 0, allocator);
+
+	s := s;
+	for c, i in s {
+		if c != utf8.RUNE_ERROR {
+			continue;
+		}
+
+		_, w := utf8.decode_rune_in_string(s[i:]);
+		if w == 1 {
+			grow_builder(&b, len(s) + len(replacement));
+			write_string(&b, s[:i]);
+			s = s[i:];
+			break;
+		}
+	}
+
+	if builder_cap(b) == 0 {
+		return clone(s, allocator);
+	}
+
+	invalid := false;
+
+	for i := 0; i < len(s); /**/ {
+		c := s[i];
+		if c < utf8.RUNE_SELF {
+			i += 1;
+			invalid = false;
+			write_byte(&b, c);
+			continue;
+		}
+
+		_, w := utf8.decode_rune_in_string(s[i:]);
+		if w == 1 {
+			i += 1;
+			if !invalid {
+				invalid = true;
+				write_string(&b, replacement);
+			}
+			continue;
+		}
+		invalid = false;
+		write_string(&b, s[i:][:w]);
+		i += w;
+	}
+	return to_string(b);
+}
+
+to_lower :: proc(s: string, allocator := context.allocator) -> string {
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+	for r in s {
+		write_rune_builder(&b, unicode.to_lower(r));
+	}
+	return to_string(b);
+}
+to_upper :: proc(s: string, allocator := context.allocator) -> string {
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+	for r in s {
+		write_rune_builder(&b, unicode.to_upper(r));
+	}
+	return to_string(b);
+}
+
+
+
+
+is_delimiter :: proc(c: rune) -> bool {
+	return c == '-' || c == '_' || is_space(c);
+}
+
+is_separator :: proc(r: rune) -> bool {
+	if r <= 0x7f {
+		switch r {
+		case '0'..'9': return false;
+		case 'a'..'z': return false;
+		case 'A'..'Z': return false;
+		case '_': return false;
+		}
+		return true;
+	}
+
+	// TODO(bill): unicode categories
+	// if unicode.is_letter(r) || unicode.is_digit(r) {
+	// 	return false;
+	// }
+
+	return unicode.is_space(r);
+}
+
+
+string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
+	prev, curr: rune;
+	for next in s {
+		if curr == 0 {
+			prev = curr;
+			curr = next;
+			continue;
+		}
+
+		callback(w, prev, curr, next);
+
+		prev = curr;
+		curr = next;
+	}
+
+	if len(s) > 0 {
+		callback(w, prev, curr, 0);
+	}
+}
+
+
+to_lower_camel_case :: to_camel_case;
+to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
+	s := s;
+	s = trim_space(s);
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+	w := to_writer(&b);
+
+	string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
+		if !is_delimiter(curr) {
+			if is_delimiter(prev) {
+				io.write_rune(w, unicode.to_upper(curr));
+			} else if unicode.is_lower(prev) {
+				io.write_rune(w, curr);
+			} else {
+				io.write_rune(w, unicode.to_lower(curr));
+			}
+		}
+	});
+
+	return to_string(b);
+}
+
+to_upper_camel_case :: to_pascal_case;
+to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
+	s := s;
+	s = trim_space(s);
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+	w := to_writer(&b);
+
+	string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
+		if !is_delimiter(curr) {
+			if is_delimiter(prev) || prev == 0 {
+				io.write_rune(w, unicode.to_upper(curr));
+			} else if unicode.is_lower(prev) {
+				io.write_rune(w, curr);
+			} else {
+				io.write_rune(w, unicode.to_lower(curr));
+			}
+		}
+	});
+
+	return to_string(b);
+}
+
+to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
+	s := s;
+	s = trim_space(s);
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+	w := to_writer(&b);
+
+	adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
+
+	prev, curr: rune;
+
+	for next in s {
+		if is_delimiter(curr) {
+			if !is_delimiter(prev) {
+				io.write_rune(w, delimiter);
+			}
+		} else if unicode.is_upper(curr) {
+			if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
+				io.write_rune(w, delimiter);
+			}
+			io.write_rune(w, adjust_case(curr));
+		} else if curr != 0 {
+			io.write_rune(w, adjust_case(curr));
+		}
+
+		prev = curr;
+		curr = next;
+	}
+
+	if len(s) > 0 {
+		if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
+			io.write_rune(w, delimiter);
+		}
+		io.write_rune(w, adjust_case(curr));
+	}
+
+	return to_string(b);
+}
+
+
+to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
+	return to_delimiter_case(s, '_', false, allocator);
+}
+
+to_screaming_snake_case :: to_upper_snake_case;
+to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
+	return to_delimiter_case(s, '_', true, allocator);
+}
+
+to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
+	return to_delimiter_case(s, '-', false, allocator);
+}
+
+to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
+	return to_delimiter_case(s, '-', true, allocator);
+}
+
+to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
+	delimiter :: '_';
+
+	s := s;
+	s = trim_space(s);
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
+	w := to_writer(&b);
+
+	prev, curr: rune;
+
+	for next in s {
+		if is_delimiter(curr) {
+			if !is_delimiter(prev) {
+				io.write_rune(w, delimiter);
+			}
+		} else if unicode.is_upper(curr) {
+			if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
+				io.write_rune(w, delimiter);
+			}
+			io.write_rune(w, unicode.to_upper(curr));
+		} else if curr != 0 {
+			io.write_rune(w, unicode.to_lower(curr));
+		}
+
+		prev = curr;
+		curr = next;
+	}
+
+	if len(s) > 0 {
+		if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
+			io.write_rune(w, delimiter);
+			io.write_rune(w, unicode.to_upper(curr));
+		} else {
+			io.write_rune(w, unicode.to_lower(curr));
+		}
+	}
+
+	return to_string(b);
+}
+

+ 177 - 0
core/strings/reader.odin

@@ -0,0 +1,177 @@
+package strings
+
+import "core:io"
+import "core:unicode/utf8"
+
+Reader :: struct {
+	s:         string, // read-only buffer
+	i:         i64,    // current reading index
+	prev_rune: int,    // previous reading index of rune or < 0
+}
+
+reader_init :: proc(r: ^Reader, s: string) {
+	r.s = s;
+	r.i = 0;
+	r.prev_rune = -1;
+}
+
+reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
+	s.stream_data = r;
+	s.stream_vtable = _reader_vtable;
+	return;
+}
+
+reader_length :: proc(r: ^Reader) -> int {
+	if r.i >= i64(len(r.s)) {
+		return 0;
+	}
+	return int(i64(len(r.s)) - r.i);
+}
+
+reader_size :: proc(r: ^Reader) -> i64 {
+	return i64(len(r.s));
+}
+
+reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+	if r.i >= i64(len(r.s)) {
+		return 0, .EOF;
+	}
+	r.prev_rune = -1;
+	n = copy(p, r.s[r.i:]);
+	r.i += i64(n);
+	return;
+}
+reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
+	if off < 0 {
+		return 0, .Invalid_Offset;
+	}
+	if off >= i64(len(r.s)) {
+		return 0, .EOF;
+	}
+	n = copy(p, r.s[off:]);
+	if n < len(p) {
+		err = .EOF;
+	}
+	return;
+}
+reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
+	r.prev_rune = -1;
+	if r.i >= i64(len(r.s)) {
+		return 0, .EOF;
+	}
+	b := r.s[r.i];
+	r.i += 1;
+	return b, nil;
+}
+reader_unread_byte :: proc(r: ^Reader) -> io.Error {
+	if r.i <= 0 {
+		return .Invalid_Unread;
+	}
+	r.prev_rune = -1;
+	r.i -= 1;
+	return nil;
+}
+reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
+	if r.i >= i64(len(r.s)) {
+		r.prev_rune = -1;
+		return 0, 0, .EOF;
+	}
+	r.prev_rune = int(r.i);
+	if c := r.s[r.i]; c < utf8.RUNE_SELF {
+		r.i += 1;
+		return rune(c), 1, nil;
+	}
+	ch, size = utf8.decode_rune_in_string(r.s[r.i:]);
+	r.i += i64(size);
+	return;
+}
+reader_unread_rune :: proc(r: ^Reader) -> io.Error {
+	if r.i <= 0 {
+		return .Invalid_Unread;
+	}
+	if r.prev_rune < 0 {
+		return .Invalid_Unread;
+	}
+	r.i = i64(r.prev_rune);
+	r.prev_rune = -1;
+	return nil;
+}
+reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+	r.prev_rune = -1;
+	abs: i64;
+	switch whence {
+	case .Start:
+		abs = offset;
+	case .Current:
+		abs = r.i + offset;
+	case .End:
+		abs = i64(len(r.s)) + offset;
+	case:
+		return 0, .Invalid_Whence;
+	}
+
+	if abs < 0 {
+		return 0, .Invalid_Offset;
+	}
+	r.i = abs;
+	return abs, nil;
+}
+reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
+	r.prev_rune = -1;
+	if r.i >= i64(len(r.s)) {
+		return 0, nil;
+	}
+	s := r.s[r.i:];
+	m: int;
+	m, err = io.write_string(w, s);
+	if m > len(s) {
+		panic("bytes.Reader.write_to: invalid io.write_string count");
+	}
+	r.i += i64(m);
+	n = i64(m);
+	if m != len(s) && err == nil {
+		err = .Short_Write;
+	}
+	return;
+}
+
+
+@(private)
+_reader_vtable := &io.Stream_VTable{
+	impl_size = proc(s: io.Stream) -> i64 {
+		r := (^Reader)(s.stream_data);
+		return reader_size(r);
+	},
+	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read(r, p);
+	},
+	impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read_at(r, p, off);
+	},
+	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read_byte(r);
+	},
+	impl_unread_byte = proc(s: io.Stream) -> io.Error {
+		r := (^Reader)(s.stream_data);
+		return reader_unread_byte(r);
+	},
+	impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_read_rune(r);
+	},
+	impl_unread_rune = proc(s: io.Stream) -> io.Error {
+		r := (^Reader)(s.stream_data);
+		return reader_unread_rune(r);
+	},
+	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_seek(r, offset, whence);
+	},
+	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
+		r := (^Reader)(s.stream_data);
+		return reader_write_to(r, w);
+	},
+};

+ 114 - 268
core/strings/strings.odin

@@ -1,5 +1,6 @@
 package strings
 
+import "core:io"
 import "core:mem"
 import "core:unicode"
 import "core:unicode/utf8"
@@ -225,7 +226,7 @@ index_byte :: proc(s: string, c: byte) -> int {
 	return -1;
 }
 
-// Returns i1 if c is not present
+// Returns -1 if c is not present
 last_index_byte :: proc(s: string, c: byte) -> int {
 	for i := len(s)-1; i >= 0; i -= 1 {
 		if s[i] == c {
@@ -467,10 +468,12 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
 	return;
 }
 
+@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+
+
 is_ascii_space :: proc(r: rune) -> bool {
-	switch r {
-	case '\t', '\n', '\v', '\f', '\r', ' ':
-		return true;
+	if r < utf8.RUNE_SELF {
+		return _ascii_space[u8(r)] != 0;
 	}
 	return false;
 }
@@ -757,7 +760,8 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator
 // Adjacent invalid bytes are only replaced once
 scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
 	str := s;
-	b := make_builder(0, len(str), allocator);
+	b: Builder;
+	init_builder(&b, 0, len(s), allocator);
 
 	has_error := false;
 	cursor := 0;
@@ -787,207 +791,6 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
 }
 
 
-to_lower :: proc(s: string, allocator := context.allocator) -> string {
-	b := make_builder(0, len(s), allocator);
-	for r in s {
-		write_rune(&b, unicode.to_lower(r));
-	}
-	return to_string(b);
-}
-to_upper :: proc(s: string, allocator := context.allocator) -> string {
-	b := make_builder(0, len(s), allocator);
-	for r in s {
-		write_rune(&b, unicode.to_upper(r));
-	}
-	return to_string(b);
-}
-
-
-
-
-is_delimiter :: proc(c: rune) -> bool {
-	return c == '-' || c == '_' || is_space(c);
-}
-
-is_separator :: proc(r: rune) -> bool {
-	if r <= 0x7f {
-		switch r {
-		case '0'..'9': return false;
-		case 'a'..'z': return false;
-		case 'A'..'Z': return false;
-		case '_': return false;
-		}
-		return true;
-	}
-
-	// TODO(bill): unicode categories
-	// if unicode.is_letter(r) || unicode.is_digit(r) {
-	// 	return false;
-	// }
-
-	return unicode.is_space(r);
-}
-
-
-string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) {
-	prev, curr: rune;
-	for next in s {
-		if curr == 0 {
-			prev = curr;
-			curr = next;
-			continue;
-		}
-
-		callback(b, prev, curr, next);
-
-		prev = curr;
-		curr = next;
-	}
-
-	if len(s) > 0 {
-		callback(b, prev, curr, 0);
-	}
-}
-
-
-to_lower_camel_case :: to_camel_case;
-to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
-	s := s;
-	s = trim_space(s);
-	b := make_builder(0, len(s), allocator);
-
-	string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
-		if !is_delimiter(curr) {
-			if is_delimiter(prev) {
-				write_rune(b, unicode.to_upper(curr));
-			} else if unicode.is_lower(prev) {
-				write_rune(b, curr);
-			} else {
-				write_rune(b, unicode.to_lower(curr));
-			}
-		}
-	});
-
-	return to_string(b);
-}
-
-to_upper_camel_case :: to_pascal_case;
-to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
-	s := s;
-	s = trim_space(s);
-	b := make_builder(0, len(s), allocator);
-
-	string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
-		if !is_delimiter(curr) {
-			if is_delimiter(prev) || prev == 0 {
-				write_rune(b, unicode.to_upper(curr));
-			} else if unicode.is_lower(prev) {
-				write_rune(b, curr);
-			} else {
-				write_rune(b, unicode.to_lower(curr));
-			}
-		}
-	});
-
-	return to_string(b);
-}
-
-to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
-	s := s;
-	s = trim_space(s);
-	b := make_builder(0, len(s), allocator);
-
-	adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
-
-	prev, curr: rune;
-
-	for next in s {
-		if is_delimiter(curr) {
-			if !is_delimiter(prev) {
-				write_rune(&b, delimiter);
-			}
-		} else if unicode.is_upper(curr) {
-			if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
-				write_rune(&b, delimiter);
-			}
-			write_rune(&b, adjust_case(curr));
-		} else if curr != 0 {
-			write_rune(&b, adjust_case(curr));
-		}
-
-		prev = curr;
-		curr = next;
-	}
-
-	if len(s) > 0 {
-		if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
-			write_rune(&b, delimiter);
-		}
-		write_rune(&b, adjust_case(curr));
-	}
-
-	return to_string(b);
-}
-
-
-to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
-	return to_delimiter_case(s, '_', false, allocator);
-}
-
-to_screaming_snake_case :: to_upper_snake_case;
-to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
-	return to_delimiter_case(s, '_', true, allocator);
-}
-
-to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
-	return to_delimiter_case(s, '-', false, allocator);
-}
-
-to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
-	return to_delimiter_case(s, '-', true, allocator);
-}
-
-to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
-	delimiter :: '_';
-
-	s := s;
-	s = trim_space(s);
-	b := make_builder(0, len(s), allocator);
-
-	prev, curr: rune;
-
-	for next in s {
-		if is_delimiter(curr) {
-			if !is_delimiter(prev) {
-				write_rune(&b, delimiter);
-			}
-		} else if unicode.is_upper(curr) {
-			if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
-				write_rune(&b, delimiter);
-			}
-			write_rune(&b, unicode.to_upper(curr));
-		} else if curr != 0 {
-			write_rune(&b, unicode.to_lower(curr));
-		}
-
-		prev = curr;
-		curr = next;
-	}
-
-	if len(s) > 0 {
-		if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
-			write_rune(&b, delimiter);
-			write_rune(&b, unicode.to_upper(curr));
-		} else {
-			write_rune(&b, unicode.to_lower(curr));
-		}
-	}
-
-	return to_string(b);
-}
-
-
-
 reverse :: proc(s: string, allocator := context.allocator) -> string {
 	str := s;
 	n := len(str);
@@ -1013,7 +816,9 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
 		return "";
 	}
 
-	b := make_builder(allocator);
+	b: Builder;
+	init_builder(&b, allocator);
+	writer := to_writer(&b);
 	str := s;
 	column: int;
 
@@ -1024,7 +829,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
 			expand := tab_size - column%tab_size;
 
 			for i := 0; i < expand; i += 1 {
-				write_byte(&b, ' ');
+				io.write_byte(writer, ' ');
 			}
 
 			column += expand;
@@ -1035,7 +840,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
 				column += w;
 			}
 
-			write_rune(&b, r);
+			io.write_rune(writer, r);
 		}
 
 		str = str[w:];
@@ -1070,12 +875,15 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
 	remains := length-1;
 	pad_len := rune_count(pad);
 
-	b := make_builder(allocator);
+	b: Builder;
+	init_builder(&b, allocator);
 	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
 
-	write_pad_string(&b, pad, pad_len, remains/2);
-	write_string(&b, str);
-	write_pad_string(&b, pad, pad_len, (remains+1)/2);
+	w := to_writer(&b);
+
+	write_pad_string(w, pad, pad_len, remains/2);
+	io.write_string(w, str);
+	write_pad_string(w, pad, pad_len, (remains+1)/2);
 
 	return to_string(b);
 }
@@ -1090,11 +898,14 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
 	remains := length-1;
 	pad_len := rune_count(pad);
 
-	b := make_builder(allocator);
+	b: Builder;
+	init_builder(&b, allocator);
 	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
 
-	write_string(&b, str);
-	write_pad_string(&b, pad, pad_len, remains);
+	w := to_writer(&b);
+
+	io.write_string(w, str);
+	write_pad_string(w, pad, pad_len, remains);
 
 	return to_string(b);
 }
@@ -1109,86 +920,121 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
 	remains := length-1;
 	pad_len := rune_count(pad);
 
-	b := make_builder(allocator);
+	b: Builder;
+	init_builder(&b, allocator);
 	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
 
-	write_pad_string(&b, pad, pad_len, remains);
-	write_string(&b, str);
+	w := to_writer(&b);
+
+	write_pad_string(w, pad, pad_len, remains);
+	io.write_string(w, str);
 
 	return to_string(b);
 }
 
 
 
-to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
-	if len(s) == 0 {
-		return "";
+
+@private
+write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
+	repeats := remains / pad_len;
+
+	for i := 0; i < repeats; i += 1 {
+		io.write_string(w, pad);
 	}
 
-	b := make_builder_len_cap(0, 0, allocator);
+	n := remains % pad_len;
+	p := pad;
 
-	s := s;
-	for c, i in s {
-		if c != utf8.RUNE_ERROR {
-			continue;
-		}
+	for i := 0; i < n; i += 1 {
+		r, width := utf8.decode_rune_in_string(p);
+		io.write_rune(w, r);
+		p = p[width:];
+	}
+}
 
-		_, w := utf8.decode_rune_in_string(s[i:]);
-		if w == 1 {
-			grow_builder(&b, len(s) + len(replacement));
-			write_string(&b, s[:i]);
-			s = s[i:];
-			break;
-		}
+
+// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space
+// returning a slice of substrings of s or an empty slice if s only contains white space
+fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check {
+	n := 0;
+	was_space := 1;
+	set_bits := u8(0);
+
+	// check to see
+	for i in 0..<len(s) {
+		r := s[i];
+		set_bits |= r;
+		is_space := int(_ascii_space[r]);
+		n += was_space & ~is_space;
+		was_space = is_space;
 	}
 
-	if builder_cap(b) == 0 {
-		return clone(s, allocator);
+	if set_bits >= utf8.RUNE_SELF {
+		return fields_proc(s, unicode.is_space, allocator);
 	}
 
-	invalid := false;
+	if n == 0 {
+		return nil;
+	}
 
-	for i := 0; i < len(s); /**/ {
-		c := s[i];
-		if c < utf8.RUNE_SELF {
+	a := make([]string, n, allocator);
+	na := 0;
+	field_start := 0;
+	i := 0;
+	for i < len(s) && _ascii_space[s[i]] != 0 {
+		i += 1;
+	}
+	field_start = i;
+	for i < len(s) {
+		if _ascii_space[s[i]] == 0 {
 			i += 1;
-			invalid = false;
-			write_byte(&b, c);
 			continue;
 		}
-
-		_, w := utf8.decode_rune_in_string(s[i:]);
-		if w == 1 {
+		a[na] = s[field_start : i];
+		na += 1;
+		i += 1;
+		for i < len(s) && _ascii_space[s[i]] != 0 {
 			i += 1;
-			if !invalid {
-				invalid = true;
-				write_string(&b, replacement);
-			}
-			continue;
 		}
-		invalid = false;
-		write_string(&b, s[i:][:w]);
-		i += w;
+		field_start = i;
 	}
-	return to_string(b);
+	if field_start < len(s) {
+		a[na] = s[field_start:];
+	}
+	return a;
 }
 
 
+// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch)
+// returns a slice of substrings of s
+// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
+//
+// fields_proc makes no guarantee about the order in which it calls f(ch)
+// it assumes that `f` always returns the same value for a given ch
+fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check {
+	substrings := make([dynamic]string, 0, 32, allocator);
 
-@private
-write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
-	repeats := remains / pad_len;
-
-	for i := 0; i < repeats; i += 1 {
-		write_string(b, pad);
+	start, end := -1, -1;
+	for r, offset in s {
+		end = offset;
+		if f(r) {
+			if start >= 0 {
+				append(&substrings, s[start : end]);
+				// -1 could be used, but just speed it up through bitwise not
+				// gotta love 2's complement
+				start = ~start;
+			}
+		} else {
+			if start < 0 {
+				start = end;
+			}
+		}
 	}
 
-	n := remains % pad_len;
-	p := pad;
-
-	for i := 0; i < n; i += 1 {
-		r, w := utf8.decode_rune_in_string(p);
-		write_rune(b, r);
-		p = p[w:];
+	if start >= 0 {
+		append(&substrings, s[start : end]);
 	}
+
+	return substrings[:];
 }

+ 69 - 42
core/sync/channel.odin

@@ -2,60 +2,89 @@ package sync
 
 import "core:mem"
 import "core:time"
-import "core:intrinsics"
+import "intrinsics"
 import "core:math/rand"
 
 _, _ :: time, rand;
 
+Channel_Direction :: enum i8 {
+	Both =  0,
+	Send = +1,
+	Recv = -1,
+}
 
-Channel :: struct(T: typeid) {
+Channel :: struct(T: typeid, Direction := Channel_Direction.Both) {
 	using _internal: ^Raw_Channel,
 }
 
-channel_init :: proc(ch: ^$C/Channel($T), cap := 0, allocator := context.allocator) {
+channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
 	context.allocator = allocator;
 	ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
 	return;
 }
 
-channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T)) {
+channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
 	context.allocator = allocator;
 	ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
 	return;
 }
 
-channel_destroy :: proc(ch: $C/Channel($T)) {
+channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
+	context.allocator = allocator;
+	ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
+	return;
+}
+channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
+	context.allocator = allocator;
+	ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
+	return;
+}
+
+channel_destroy :: proc(ch: $C/Channel($T, $D)) {
 	raw_channel_destroy(ch._internal);
 }
 
+channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
+	res._internal = ch._internal;
+	return;
+}
+
+channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
+	res._internal = ch._internal;
+	return;
+}
 
-channel_len :: proc(ch: $C/Channel($T)) -> int {
-	return ch._internal.len;
+
+channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
+	return ch._internal.len if ch._internal != nil else 0;
 }
-channel_cap :: proc(ch: $C/Channel($T)) -> int {
-	return ch._internal.cap;
+channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
+	return ch._internal.cap if ch._internal != nil else 0;
 }
 
 
-channel_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) {
+channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
 	msg := msg;
 	_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
 }
-channel_try_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) -> bool {
+channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
 	msg := msg;
 	return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
 }
 
-channel_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T) {
+channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
 	c := ch._internal;
+	if c == nil {
+		panic(message="cannot recv message; channel is nil", loc=loc);
+	}
 	mutex_lock(&c.mutex);
 	raw_channel_recv_impl(c, &msg, loc);
 	mutex_unlock(&c.mutex);
 	return;
 }
-channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T, ok: bool) {
+channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
 	c := ch._internal;
-	if mutex_try_lock(&c.mutex) {
+	if c != nil && mutex_try_lock(&c.mutex) {
 		if c.len > 0 {
 			raw_channel_recv_impl(c, &msg, loc);
 			ok = true;
@@ -64,7 +93,7 @@ channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T
 	}
 	return;
 }
-channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_location) -> (ok: bool) {
+channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
 	res: T;
 	res, ok = channel_try_recv(ch, loc);
 	if ok && msg != nil {
@@ -74,32 +103,32 @@ channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_locatio
 }
 
 
-channel_is_nil :: proc(ch: $C/Channel($T)) -> bool {
+channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
 	return ch._internal == nil;
 }
-channel_is_open :: proc(ch: $C/Channel($T)) -> bool {
+channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
 	c := ch._internal;
 	return c != nil && !c.closed;
 }
 
 
-channel_eq :: proc(a, b: $C/Channel($T)) -> bool {
+channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
 	return a._internal == b._internal;
 }
-channel_ne :: proc(a, b: $C/Channel($T)) -> bool {
+channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
 	return a._internal != b._internal;
 }
 
 
-channel_can_send :: proc(ch: $C/Channel($T)) -> (ok: bool) {
+channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
 	return raw_channel_can_send(ch._internal);
 }
-channel_can_recv :: proc(ch: $C/Channel($T)) -> (ok: bool) {
+channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
 	return raw_channel_can_recv(ch._internal);
 }
 
 
-channel_peek :: proc(ch: $C/Channel($T)) -> int {
+channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
 	c := ch._internal;
 	if c == nil {
 		return -1;
@@ -111,12 +140,12 @@ channel_peek :: proc(ch: $C/Channel($T)) -> int {
 }
 
 
-channel_close :: proc(ch: $C/Channel($T), loc := #caller_location) {
+channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
 	raw_channel_close(ch._internal, loc);
 }
 
 
-channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
+channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
 	c := ch._internal;
 	if c == nil {
 		return;
@@ -127,12 +156,12 @@ channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
 	}
 	return;
 }
-channel_drain :: proc(ch: $C/Channel($T)) {
+channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
 	raw_channel_drain(ch._internal);
 }
 
 
-channel_move :: proc(dst, src: $C/Channel($T)) {
+channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
 	for msg in channel_iterator(src) {
 		channel_send(dst, msg);
 	}
@@ -258,18 +287,19 @@ raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc :=
 		for c.len >= c.cap {
 			condition_wait_for(&c.cond);
 		}
-	} else if c.len > 0 {
+	} else if c.len > 0 { // TODO(bill): determine correct behaviour
 		if !block {
 			return false;
 		}
 		condition_wait_for(&c.cond);
+	} else if c.len == 0 && !block {
+		return false;
 	}
 
 	send(c, msg);
 	condition_signal(&c.cond);
 	raw_channel_wait_queue_signal(c.recvq);
 
-
 	return true;
 }
 
@@ -509,7 +539,7 @@ select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
 	return;
 }
 
-select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
+select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
 	switch len(channels) {
 	case 0:
 		panic("sync: select with no channels");
@@ -535,7 +565,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
 			q.state = &state;
 			raw_channel_wait_queue_insert(&c.recvq, q);
 		}
-		raw_channel_wait_queue_wait_on(&state);
+		raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
 		for c, i in channels {
 			q := &queues[i];
 			raw_channel_wait_queue_remove(&c.recvq, q);
@@ -560,7 +590,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
 	return;
 }
 
-select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
+select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
 	switch len(channels) {
 	case 0:
 		panic("sync: select with no channels");
@@ -589,7 +619,7 @@ select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
 			q.state = &state;
 			raw_channel_wait_queue_insert(&c.recvq, q);
 		}
-		raw_channel_wait_queue_wait_on(&state);
+		raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
 		for c, i in channels {
 			q := &queues[i];
 			raw_channel_wait_queue_remove(&c.recvq, q);
@@ -781,16 +811,15 @@ select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_che
 	return;
 }
 
-select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
+select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
 	switch len(channels) {
 	case 0:
-		index = 0;
+		index = -1;
 		return;
 	case 1:
-		if c := channels[0]; channel_can_recv(c) {
+		ok: bool;
+		if msg, ok = channel_try_recv(channels[0]); ok {
 			index = 0;
-			msg = channel_recv(c);
-			return;
 		}
 		return;
 	}
@@ -820,16 +849,14 @@ select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int)
 	return;
 }
 
-select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
+select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
+	index = -1;
 	switch len(channels) {
 	case 0:
-		index = 0;
 		return;
 	case 1:
-		if c := channels[0]; channel_can_send(c) {
+		if channel_try_send(channels[0], msg) {
 			index = 0;
-			channel_send(c, msg);
-			return;
 		}
 		return;
 	}

+ 35 - 0
core/sys/cpu/cpu.odin

@@ -0,0 +1,35 @@
+package sys_cpu
+
+#assert(ODIN_USE_LLVM_API);
+
+Cache_Line_Pad :: struct {_: [_cache_line_size]byte};
+
+initialized: bool;
+
+x86: struct {
+	_: Cache_Line_Pad,
+	has_aes:       bool, // AES hardware implementation (AES NI)
+	has_adx:       bool, // Multi-precision add-carry instruction extensions
+	has_avx:       bool, // Advanced vector extension
+	has_avx2:      bool, // Advanced vector extension 2
+	has_bmi1:      bool, // Bit manipulation instruction set 1
+	has_bmi2:      bool, // Bit manipulation instruction set 2
+	has_erms:      bool, // Enhanced REP for MOVSB and STOSB
+	has_fma:       bool, // Fused-multiply-add instructions
+	has_os_xsave:  bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
+	has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM
+	has_popcnt:    bool, // Hamming weight instruction POPCNT.
+	has_rdrand:    bool, // RDRAND instruction (on-chip random number generator)
+	has_rdseed:    bool, // RDSEED instruction (on-chip random number generator)
+	has_sse2:      bool, // Streaming SIMD extension 2 (always available on amd64)
+	has_sse3:      bool, // Streaming SIMD extension 3
+	has_ssse3:     bool, // Supplemental streaming SIMD extension 3
+	has_sse41:     bool, // Streaming SIMD extension 4 and 4.1
+	has_sse42:     bool, // Streaming SIMD extension 4 and 4.2
+	_: Cache_Line_Pad,
+};
+
+
+init :: proc() {
+	_init();
+}

+ 67 - 0
core/sys/cpu/cpu_x86.odin

@@ -0,0 +1,67 @@
+//+build 386, amd64
+package sys_cpu
+
+_cache_line_size :: 64;
+
+cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) {
+	return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} {
+		"cpuid",
+		"={ax},={bx},={cx},={dx},{ax},{cx}",
+	}(ax, cx));
+}
+
+xgetbv :: proc() -> (eax, edx: u32) {
+	return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} {
+		"xgetbv",
+		"={ax},={dx},{cx}",
+	}(0));
+}
+
+_init :: proc() {
+	is_set :: proc(hwc: u32, value: u32) -> bool {
+		return hwc&value != 0;
+	}
+
+	initialized = true;
+
+	max_id, _, _, _ := cpuid(0, 0);
+
+	if max_id < 1 {
+		return;
+	}
+
+	_, _, ecx1, edx1 := cpuid(1, 0);
+
+	x86.has_sse2 = is_set(26, edx1);
+
+	x86.has_sse3      = is_set(0, ecx1);
+	x86.has_pclmulqdq = is_set(1, ecx1);
+	x86.has_ssse3     = is_set(9, ecx1);
+	x86.has_fma       = is_set(12, ecx1);
+	x86.has_sse41     = is_set(19, ecx1);
+	x86.has_sse42     = is_set(20, ecx1);
+	x86.has_popcnt    = is_set(23, ecx1);
+	x86.has_aes       = is_set(25, ecx1);
+	x86.has_os_xsave  = is_set(27, ecx1);
+	x86.has_rdrand    = is_set(30, ecx1);
+
+	os_supports_avx := false;
+	if x86.has_os_xsave {
+		eax, _ := xgetbv();
+		os_supports_avx = is_set(1, eax) && is_set(2, eax);
+	}
+
+	x86.has_avx = is_set(28, ecx1) && os_supports_avx;
+
+	if max_id < 7 {
+		return;
+	}
+
+	_, ebx7, _, _ := cpuid(7, 0);
+	x86.has_bmi1   = is_set(3, ebx7);
+	x86.has_avx2   = is_set(5, ebx7) && os_supports_avx;
+	x86.has_bmi2   = is_set(8, ebx7);
+	x86.has_erms   = is_set(9, ebx7);
+	x86.has_rdseed = is_set(18, ebx7);
+	x86.has_adx    = is_set(19, ebx7);
+}

+ 9 - 9
core/sys/unix/pthread_darwin.odin

@@ -14,44 +14,44 @@ PTHREAD_ONCE_SIZE      :: 8;
 PTHREAD_RWLOCK_SIZE    :: 192;
 PTHREAD_RWLOCKATTR_SIZE :: 16;
 
-pthread_t :: opaque u64;
+pthread_t :: #opaque u64;
 
-pthread_attr_t :: opaque struct #align 16 {
+pthread_attr_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_ATTR_SIZE] c.char,
 };
 
-pthread_cond_t :: opaque struct #align 16 {
+pthread_cond_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_COND_SIZE] c.char,
 };
 
-pthread_condattr_t :: opaque struct #align 16 {
+pthread_condattr_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_CONDATTR_SIZE] c.char,
 };
 
-pthread_mutex_t :: opaque struct #align 16 {
+pthread_mutex_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_MUTEX_SIZE] c.char,
 };
 
-pthread_mutexattr_t :: opaque struct #align 16 {
+pthread_mutexattr_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_MUTEXATTR_SIZE] c.char,
 };
 
-pthread_once_t :: opaque struct #align 16 {
+pthread_once_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_ONCE_SIZE] c.char,
 };
 
-pthread_rwlock_t :: opaque struct #align 16 {
+pthread_rwlock_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_RWLOCK_SIZE] c.char,
 };
 
-pthread_rwlockattr_t :: opaque struct #align 16 {
+pthread_rwlockattr_t :: #opaque struct #align 16 {
 	sig: c.long,
 	_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
 };

+ 9 - 9
core/sys/unix/pthread_freebsd.odin

@@ -26,32 +26,32 @@ when size_of(int) == 8 {
 	PTHREAD_BARRIER_T_SIZE :: 20;
 }
 
-pthread_cond_t :: opaque struct #align 16 {
+pthread_cond_t :: #opaque struct #align 16 {
 	_: [PTHREAD_COND_T_SIZE] c.char,
 };
-pthread_mutex_t :: opaque struct #align 16 {
+pthread_mutex_t :: #opaque struct #align 16 {
 	_: [PTHREAD_MUTEX_T_SIZE] c.char,
 };
-pthread_rwlock_t :: opaque struct #align 16 {
+pthread_rwlock_t :: #opaque struct #align 16 {
 	_: [PTHREAD_RWLOCK_T_SIZE] c.char,
 };
-pthread_barrier_t :: opaque struct #align 16 {
+pthread_barrier_t :: #opaque struct #align 16 {
 	_: [PTHREAD_BARRIER_T_SIZE] c.char,
 };
 
-pthread_attr_t :: opaque struct #align 16 {
+pthread_attr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_ATTR_T_SIZE] c.char,
 };
-pthread_condattr_t :: opaque struct #align 16 {
+pthread_condattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_CONDATTR_T_SIZE] c.char,
 };
-pthread_mutexattr_t :: opaque struct #align 16 {
+pthread_mutexattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
 };
-pthread_rwlockattr_t :: opaque struct #align 16 {
+pthread_rwlockattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
 };
-pthread_barrierattr_t :: opaque struct #align 16 {
+pthread_barrierattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
 };
 

+ 9 - 9
core/sys/unix/pthread_linux.odin

@@ -33,32 +33,32 @@ when size_of(int) == 8 {
 	PTHREAD_BARRIER_T_SIZE :: 20;
 }
 
-pthread_cond_t :: opaque struct #align 16 {
+pthread_cond_t :: #opaque struct #align 16 {
 	_: [PTHREAD_COND_T_SIZE] c.char,
 };
-pthread_mutex_t :: opaque struct #align 16 {
+pthread_mutex_t :: #opaque struct #align 16 {
 	_: [PTHREAD_MUTEX_T_SIZE] c.char,
 };
-pthread_rwlock_t :: opaque struct #align 16 {
+pthread_rwlock_t :: #opaque struct #align 16 {
 	_: [PTHREAD_RWLOCK_T_SIZE] c.char,
 };
-pthread_barrier_t :: opaque struct #align 16 {
+pthread_barrier_t :: #opaque struct #align 16 {
 	_: [PTHREAD_BARRIER_T_SIZE] c.char,
 };
 
-pthread_attr_t :: opaque struct #align 16 {
+pthread_attr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_ATTR_T_SIZE] c.char,
 };
-pthread_condattr_t :: opaque struct #align 16 {
+pthread_condattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_CONDATTR_T_SIZE] c.char,
 };
-pthread_mutexattr_t :: opaque struct #align 16 {
+pthread_mutexattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
 };
-pthread_rwlockattr_t :: opaque struct #align 16 {
+pthread_rwlockattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
 };
-pthread_barrierattr_t :: opaque struct #align 16 {
+pthread_barrierattr_t :: #opaque struct #align 16 {
 	_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
 };
 

+ 2 - 2
core/sys/win32/kernel32.odin

@@ -8,12 +8,12 @@ foreign kernel32 {
 	@(link_name="CreateProcessA")		     create_process_a		      :: proc(application_name, command_line: cstring,
 	                                                              				 process_attributes, thread_attributes: ^Security_Attributes,
 	                                                              				 inherit_handle: Bool, creation_flags: u32, environment: rawptr,
-	                                                              				 current_direcotry: cstring, startup_info: ^Startup_Info,
+	                                                              				 current_directory: cstring, startup_info: ^Startup_Info,
 	                                                              				 process_information: ^Process_Information) -> Bool ---;
 	@(link_name="CreateProcessW")            create_process_w             :: proc(application_name, command_line: Wstring,
 	                                                                             process_attributes, thread_attributes: ^Security_Attributes,
 	                                                                             inherit_handle: Bool, creation_flags: u32, environment: rawptr,
-	                                                                             current_direcotry: Wstring, startup_info: ^Startup_Info,
+	                                                                             current_directory: Wstring, startup_info: ^Startup_Info,
 	                                                                             process_information: ^Process_Information) -> Bool ---;
 	@(link_name="GetExitCodeProcess")		 get_exit_code_process        :: proc(process: Handle, exit: ^u32) -> Bool ---;
 	@(link_name="ExitProcess")               exit_process                 :: proc(exit_code: u32) ---;

+ 585 - 0
core/text/scanner/scanner.odin

@@ -0,0 +1,585 @@
+package text_scanner
+
+import "core:fmt"
+import "core:strings"
+import "core:unicode"
+import "core:unicode/utf8"
+
+Position :: struct {
+	filename: string, // filename, if present
+	offset:   int,    // byte offset, starting @ 0
+	line:     int,    // line number, starting @ 1
+	column:   int,    // column number, starting @ 1 (character count per line)
+}
+
+position_is_valid :: proc(pos: Position) -> bool {
+	return pos.line > 0;
+}
+
+position_to_string :: proc(pos: Position, allocator := context.temp_allocator) -> string {
+	s := pos.filename;
+	if s == "" {
+		s = "<input>";
+	}
+
+	context.allocator = allocator;
+	if position_is_valid(pos) {
+		return fmt.aprintf("%s(%d:%d)", s, pos.line, pos.column);
+	} else {
+		return strings.clone(s);
+	}
+}
+
+EOF        :: -1;
+Ident      :: -2;
+Int        :: -3;
+Float      :: -4;
+Char       :: -5;
+String     :: -6;
+Raw_String :: -7;
+Comment    :: -8;
+
+Scan_Flag :: enum u32 {
+	Scan_Idents,
+	Scan_Ints,
+	Scan_C_Int_Prefixes,
+	Scan_Floats,
+	Scan_Chars,
+	Scan_Strings,
+	Scan_Raw_Strings,
+	Scan_Comments,
+	Skip_Comments,
+}
+Scan_Flags :: bit_set[Scan_Flag; u32];
+
+Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
+C_Like_Tokens    :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
+
+Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' ';
+C_Whitespace    :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' ';
+
+
+Scanner :: struct {
+	src: string,
+
+	src_pos: int,
+	src_end: int,
+
+	tok_pos: int,
+	tok_end: int,
+
+	ch: rune,
+
+	line:   int,
+	column: int,
+	prev_line_len: int,
+	prev_char_len: int,
+
+	error: proc(s: ^Scanner, msg: string),
+	error_count: int,
+
+	flags: Scan_Flags,
+	whitespace: u64,
+
+	is_ident_rune: proc(ch: rune, i: int) -> bool,
+
+	pos: Position,
+}
+
+init :: proc(s: ^Scanner, src: string, filename := "") -> ^Scanner {
+	s^ = {};
+
+	s.src = src;
+	s.pos.filename = filename;
+
+	s.tok_pos = -1;
+
+	s.ch = -2; // no char read yet, not an EOF
+
+	s.line = 1;
+
+	s.flags = Odin_Like_Tokens;
+	s.whitespace = Odin_Whitespace;
+
+	return s;
+}
+
+
+@(private)
+advance :: proc(s: ^Scanner) -> rune {
+	if s.src_pos >= len(s.src) {
+		s.prev_char_len = 0;
+		return EOF;
+	}
+	ch, width := rune(s.src[s.src_pos]), 1;
+
+	if ch >= utf8.RUNE_SELF {
+		ch, width = utf8.decode_rune_in_string(s.src[s.src_pos:]);
+		if ch == utf8.RUNE_ERROR && width == 1 {
+			s.src_pos += width;
+			s.prev_char_len = width;
+			s.column += 1;
+			error(s, "invalid UTF-8 encoding");
+			return ch;
+		}
+	}
+
+	s.src_pos += width;
+	s.prev_char_len = width;
+	s.column += 1;
+
+	switch ch {
+	case 0:
+		error(s, "invalid character NUL");
+	case '\n':
+		s.line += 1;
+		s.prev_line_len = s.column;
+		s.column = 0;
+	}
+
+	return ch;
+}
+
+next :: proc(s: ^Scanner) -> rune {
+	s.tok_pos = -1;
+	s.pos.line = 0;
+	ch := peek(s);
+	if ch != EOF {
+		s.ch = advance(s);
+	}
+	return ch;
+}
+
+peek :: proc(s: ^Scanner) -> rune {
+	if s.ch == -2 {
+		s.ch = advance(s);
+		if s.ch == '\ufeff' { // Ignore BOM
+			s.ch = advance(s);
+		}
+	}
+	return s.ch;
+}
+
+
+error :: proc(s: ^Scanner, msg: string) {
+	s.error_count += 1;
+	if s.error != nil {
+		s.error(s, msg);
+		return;
+	}
+	p := s.pos;
+	if !position_is_valid(p) {
+		p = position(s);
+	}
+
+	s := p.filename;
+	if s == "" {
+		s = "<input>";
+	}
+
+	if position_is_valid(p) {
+		fmt.eprintf("%s(%d:%d): %s\n", s, p.line, p.column, msg);
+	} else {
+		fmt.eprintf("%s: %s\n", s, msg);
+	}
+}
+
+errorf :: proc(s: ^Scanner, format: string, args: ..any) {
+	error(s, fmt.tprintf(format, ..args));
+}
+
+@(private)
+is_ident_rune :: proc(s: ^Scanner, ch: rune, i: int) -> bool {
+	if s.is_ident_rune != nil {
+		return s.is_ident_rune(ch, i);
+	}
+	return ch == '_' || unicode.is_letter(ch) || unicode.is_digit(ch) && i > 0;
+}
+
+@(private)
+scan_identifier :: proc(s: ^Scanner) -> rune {
+	ch := advance(s);
+	for i := 1; is_ident_rune(s, ch, i); i += 1 {
+		ch = advance(s);
+	}
+	return ch;
+}
+
+@(private) lower      :: proc(ch: rune) -> rune { return ('a' - 'A') | ch; }
+@(private) is_decimal :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9'; }
+@(private) is_hex     :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'; }
+
+
+
+@(private)
+scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) {
+	lit_name :: proc(prefix: rune) -> string {
+		switch prefix {
+		case 'b': return "binary literal";
+		case 'o': return "octal literal";
+		case 'z': return "dozenal literal";
+		case 'x': return "hexadecimal literal";
+		}
+		return "decimal literal";
+	}
+
+	digits :: proc(s: ^Scanner, ch0: rune, base: int, invalid: ^rune) -> (ch: rune, digsep: int) {
+		ch = ch0;
+		if base <= 10 {
+			max := rune('0' + base);
+			for is_decimal(ch) || ch == '_' {
+				ds := 1;
+				if ch == '_' {
+					ds = 2;
+				} else if ch >= max && invalid^ == 0 {
+					invalid^ = ch;
+				}
+				digsep |= ds;
+				ch = advance(s);
+			}
+		} else {
+			for is_hex(ch) || ch == '_' {
+				ds := 1;
+				if ch == '_' {
+					ds = 2;
+				}
+				digsep |= ds;
+				ch = advance(s);
+			}
+		}
+		return;
+	}
+
+	ch, seen_dot := ch, seen_dot;
+
+	base := 10;
+	prefix := rune(0);
+	digsep := 0;
+	invalid := rune(0);
+
+	tok: rune;
+	ds: int;
+
+	if !seen_dot {
+		tok = Int;
+		if ch == '0' {
+			ch = advance(s);
+
+			p := lower(ch);
+			if .Scan_C_Int_Prefixes in s.flags {
+				switch p {
+				case 'b':
+					ch = advance(s);
+					base, prefix = 2, 'b';
+				case 'x':
+					ch = advance(s);
+					base, prefix = 16, 'x';
+				case:
+					base, prefix = 8, 'o';
+					digsep = 1; // Leading zero
+				}
+			} else {
+				switch p {
+				case 'b':
+					ch = advance(s);
+					base, prefix = 2, 'b';
+				case 'o':
+					ch = advance(s);
+					base, prefix = 8, 'o';
+				case 'd':
+					ch = advance(s);
+					base, prefix = 10, 'd';
+				case 'z':
+					ch = advance(s);
+					base, prefix = 12, 'z';
+				case 'h':
+					tok = Float;
+					fallthrough;
+				case 'x':
+					ch = advance(s);
+					base, prefix = 16, 'x';
+				case:
+					digsep = 1; // Leading zero
+				}
+			}
+		}
+
+		ch, ds = digits(s, ch, base, &invalid);
+		digsep |= ds;
+		if ch == '.' && .Scan_Floats in s.flags {
+			ch = advance(s);
+			seen_dot = true;
+		}
+	}
+
+	if seen_dot {
+		tok = Float;
+		if prefix != 0 && prefix != 'x' {
+			errorf(s, "invalid radix point in %s", lit_name(prefix));
+		}
+		ch, ds = digits(s, ch, base, &invalid);
+		digsep |= ds;
+	}
+
+	if digsep&1 == 0 {
+		errorf(s, "%s has no digits", lit_name(prefix));
+	}
+
+	if e := lower(ch); (e == 'e' || e == 'p') && .Scan_Floats in s.flags {
+		switch {
+		case e == 'e' && prefix != 0:
+			errorf(s, "%q exponent requires decimal mantissa", ch);
+		case e == 'p' && prefix != 'x':
+			errorf(s, "%q exponent requires hexadecimal mantissa", ch);
+		}
+		ch = advance(s);
+		tok = Float;
+		if ch == '+' || ch == '-' {
+			ch = advance(s);
+		}
+		ch, ds = digits(s, ch, 10, nil);
+		digsep |= ds;
+		if ds&1 == 0 {
+			error(s, "exponent has no digits");
+		}
+	} else if prefix == 'x' && tok == Float {
+		error(s, "hexadecimal mantissa requires a 'p' exponent");
+	}
+
+	if tok == Int && invalid != 0 {
+		errorf(s, "invalid digit %q in %s", invalid, lit_name(prefix));
+	}
+
+	if digsep&2 != 0 {
+		s.tok_end = s.src_pos - s.prev_char_len;
+	}
+	return tok, ch;
+}
+
+@(private)
+scan_string :: proc(s: ^Scanner, quote: rune) -> (n: int) {
+	digit_val :: proc(ch: rune) -> int {
+		switch v := lower(ch); v {
+		case '0'..'9': return int(v - '0');
+		case 'a'..'z': return int(v - 'a');
+		}
+		return 16;
+	}
+
+	scan_digits :: proc(s: ^Scanner, ch: rune, base, n: int) -> rune {
+		ch, n := ch, n;
+		for n > 0 && digit_val(ch) < base {
+			ch = advance(s);
+			n -= 1;
+		}
+		if n > 0 {
+			error(s, "invalid char escape");
+		}
+		return ch;
+	}
+
+	ch := advance(s);
+	for ch != quote {
+		if ch == '\n' || ch < 0 {
+			error(s, "literal no terminated");
+			return;
+		}
+		if ch == '\\' {
+			ch = advance(s);
+			switch ch {
+			case quote, 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\':
+				ch = advance(s);
+			case '0'..'7': ch = scan_digits(s, advance(s), 8, 3);
+			case 'x':      ch = scan_digits(s, advance(s), 16, 2);
+			case 'u':      ch = scan_digits(s, advance(s), 16, 4);
+			case 'U':      ch = scan_digits(s, advance(s), 16, 8);
+			case:
+				error(s, "invalid char escape");
+			}
+		} else {
+			ch = advance(s);
+		}
+		n += 1;
+	}
+	return;
+}
+
+@(private)
+scan_raw_string :: proc(s: ^Scanner) {
+	ch := advance(s);
+	for ch != '`' {
+		if ch < 0 {
+			error(s, "literal not terminated");
+			return;
+		}
+		ch = advance(s);
+	}
+}
+
+@(private)
+scan_char :: proc(s: ^Scanner) {
+	if scan_string(s, '\'') != 1 {
+		error(s, "invalid char literal");
+	}
+}
+
+@(private)
+scan_comment :: proc(s: ^Scanner, ch: rune) -> rune {
+	ch := ch;
+	if ch == '/' { // line comment
+		ch = advance(s);
+		for ch != '\n' && ch >= 0 {
+			ch = advance(s);
+		}
+		return ch;
+	}
+
+	// block /**/ comment
+	ch = advance(s);
+	for {
+		if ch < 0 {
+			error(s, "comment not terminated");
+			break;
+		}
+		ch0 := ch;
+		ch = advance(s);
+		if ch0 == '*' && ch == '/' {
+			return advance(s);
+		}
+	}
+	return ch;
+}
+
+scan :: proc(s: ^Scanner) -> (tok: rune) {
+	ch := peek(s);
+	if ch == EOF {
+		return ch;
+	}
+
+	// reset position
+	s.tok_pos = -1;
+	s.pos.line = 0;
+
+	redo: for {
+		for s.whitespace & (1<<uint(ch)) != 0 {
+			ch = advance(s);
+		}
+
+		s.tok_pos = s.src_pos - s.prev_char_len;
+		s.pos.offset = s.tok_pos;
+
+		if s.column > 0 {
+			s.pos.line = s.line;
+			s.pos.column = s.column;
+		} else {
+			// previous character was newline
+			s.pos.line = s.line - 1;
+			s.pos.column = s.prev_line_len;
+		}
+
+		tok = ch;
+		if is_ident_rune(s, ch, 0) {
+			if .Scan_Idents in s.flags {
+				tok = Ident;
+				ch = scan_identifier(s);
+			} else {
+				ch = advance(s);
+			}
+
+		} else if is_decimal(ch) {
+			if s.flags >= {.Scan_Ints, .Scan_Floats} {
+				tok, ch = scan_number(s, ch, false);
+			} else {
+				ch = advance(s);
+			}
+		} else {
+			switch ch {
+			case EOF:
+				break;
+			case '"':
+				if .Scan_Strings in s.flags {
+					scan_string(s, '"');
+					tok = String;
+				}
+				ch = advance(s);
+			case '\'':
+				if .Scan_Chars in s.flags {
+					scan_string(s, '\'');
+					tok = Char;
+				}
+				ch = advance(s);
+			case '`':
+				if .Scan_Raw_Strings in s.flags {
+					scan_raw_string(s);
+					tok = Raw_String;
+				}
+				ch = advance(s);
+			case '.':
+				ch = advance(s);
+				if is_decimal(ch) && .Scan_Floats in s.flags {
+					tok, ch = scan_number(s, ch, true);
+				}
+			case '/':
+				ch = advance(s);
+				if (ch == '/' || ch == '*') && .Scan_Comments in s.flags {
+					if .Skip_Comments in s.flags {
+						s.tok_pos = -1;
+						ch = scan_comment(s, ch);
+						continue redo;
+					}
+					ch = scan_comment(s, ch);
+					tok = Comment;
+				}
+			case:
+				ch = advance(s);
+			}
+		}
+
+		break redo;
+	}
+
+	s.tok_end = s.src_pos - s.prev_char_len;
+
+	s.ch = ch;
+	return tok;
+}
+
+position :: proc(s: ^Scanner) -> Position {
+	pos: Position;
+	pos.filename = s.pos.filename;
+	pos.offset = s.src_pos - s.prev_char_len;
+	switch {
+	case s.column > 0:
+		pos.line = s.line;
+		pos.column = s.column;
+	case s.prev_line_len > 0:
+		pos.line = s.line-1;
+		pos.column = s.prev_line_len;
+	case:
+		pos.line = 1;
+		pos.column = 1;
+	}
+	return pos;
+}
+
+token_text :: proc(s: ^Scanner) -> string {
+	if s.tok_pos < 0 {
+		return "";
+	}
+	return string(s.src[s.tok_pos:s.tok_end]);
+}
+
+token_string :: proc(tok: rune, allocator := context.temp_allocator) -> string {
+	context.allocator = allocator;
+	switch tok {
+	case EOF:        return strings.clone("EOF");
+	case Ident:      return strings.clone("Ident");
+	case Int:        return strings.clone("Int");
+	case Float:      return strings.clone("Float");
+	case Char:       return strings.clone("Char");
+	case String:     return strings.clone("String");
+	case Raw_String: return strings.clone("Raw_String");
+	case Comment:    return strings.clone("Comment");
+	}
+	return fmt.aprintf("%q", tok);
+}

+ 107 - 7
core/thread/thread.odin

@@ -2,17 +2,26 @@ package thread
 
 import "core:runtime"
 import "core:sync"
-import "core:intrinsics"
+import "core:mem"
+import "intrinsics"
+
+_ :: intrinsics;
 
 Thread_Proc :: #type proc(^Thread);
 
+MAX_USER_ARGUMENTS :: 8;
+
 Thread :: struct {
-	using specific:   Thread_Os_Specific,
-	procedure:        Thread_Proc,
-	data:             rawptr,
-	user_index:       int,
+	using specific: Thread_Os_Specific,
+	procedure:      Thread_Proc,
+	data:           rawptr,
+	user_index:     int,
+	user_args:      [MAX_USER_ARGUMENTS]rawptr,
 
 	init_context: Maybe(runtime.Context),
+
+
+	creation_allocator: mem.Allocator,
 }
 
 #assert(size_of(Thread{}.user_index) == size_of(uintptr));
@@ -34,17 +43,108 @@ run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority :=
 run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
 	thread_proc :: proc(t: ^Thread) {
 		fn := cast(proc(rawptr))t.data;
-		data := rawptr(uintptr(t.user_index));
+		assert(t.user_index >= 1);
+		data := t.user_args[0];
 		fn(data);
 		destroy(t);
 	}
 	t := create(thread_proc, priority);
 	t.data = rawptr(fn);
-	t.user_index = int(uintptr(data));
+	t.user_index = 1;
+	t.user_args = data;
 	t.init_context = init_context;
 	start(t);
 }
 
+run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+	where size_of(T) <= size_of(rawptr) {
+	thread_proc :: proc(t: ^Thread) {
+		fn := cast(proc(T))t.data;
+		assert(t.user_index >= 1);
+		data := (^T)(&t.user_args[0])^;
+		fn(data);
+		destroy(t);
+	}
+	t := create(thread_proc, priority);
+	t.data = rawptr(fn);
+	t.user_index = 1;
+	data := data;
+	mem.copy(&t.user_args[0], &data, size_of(data));
+	t.init_context = init_context;
+	start(t);
+}
+
+run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+	where size_of(T1) <= size_of(rawptr),
+	      size_of(T2) <= size_of(rawptr) {
+	thread_proc :: proc(t: ^Thread) {
+		fn := cast(proc(T1, T2))t.data;
+		assert(t.user_index >= 2);
+		arg1 := (^T1)(&t.user_args[0])^;
+		arg2 := (^T2)(&t.user_args[1])^;
+		fn(arg1, arg2);
+		destroy(t);
+	}
+	t := create(thread_proc, priority);
+	t.data = rawptr(fn);
+	t.user_index = 2;
+	arg1, arg2 := arg1, arg2;
+	mem.copy(&t.user_args[0], &arg1, size_of(arg1));
+	mem.copy(&t.user_args[1], &arg2, size_of(arg2));
+	t.init_context = init_context;
+	start(t);
+}
+
+run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+	where size_of(T1) <= size_of(rawptr),
+	      size_of(T2) <= size_of(rawptr),
+	      size_of(T3) <= size_of(rawptr) {
+	thread_proc :: proc(t: ^Thread) {
+		fn := cast(proc(T1, T2, T3))t.data;
+		assert(t.user_index >= 3);
+		arg1 := (^T1)(&t.user_args[0])^;
+		arg2 := (^T2)(&t.user_args[1])^;
+		arg3 := (^T3)(&t.user_args[2])^;
+		fn(arg1, arg2, arg3);
+		destroy(t);
+	}
+	t := create(thread_proc, priority);
+	t.data = rawptr(fn);
+	t.user_index = 3;
+	arg1, arg2, arg3 := arg1, arg2, arg3;
+	mem.copy(&t.user_args[0], &arg1, size_of(arg1));
+	mem.copy(&t.user_args[1], &arg2, size_of(arg2));
+	mem.copy(&t.user_args[2], &arg3, size_of(arg3));
+	t.init_context = init_context;
+	start(t);
+}
+run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
+	where size_of(T1) <= size_of(rawptr),
+	      size_of(T2) <= size_of(rawptr),
+	      size_of(T3) <= size_of(rawptr) {
+	thread_proc :: proc(t: ^Thread) {
+		fn := cast(proc(T1, T2, T3, T4))t.data;
+		assert(t.user_index >= 4);
+		arg1 := (^T1)(&t.user_args[0])^;
+		arg2 := (^T2)(&t.user_args[1])^;
+		arg3 := (^T3)(&t.user_args[2])^;
+		arg4 := (^T4)(&t.user_args[3])^;
+		fn(arg1, arg2, arg3, arg4);
+		destroy(t);
+	}
+	t := create(thread_proc, priority);
+	t.data = rawptr(fn);
+	t.user_index = 4;
+	arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4;
+	mem.copy(&t.user_args[0], &arg1, size_of(arg1));
+	mem.copy(&t.user_args[1], &arg2, size_of(arg2));
+	mem.copy(&t.user_args[2], &arg3, size_of(arg3));
+	mem.copy(&t.user_args[3], &arg4, size_of(arg4));
+	t.init_context = init_context;
+	start(t);
+}
+
+
 
 create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
 	t := create(fn, priority);

+ 3 - 2
core/thread/thread_unix.odin

@@ -85,6 +85,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
 	if thread == nil {
 		return nil;
 	}
+	thread.creation_allocator = context.allocator;
 
 	// Set thread priority.
 	policy: i32;
@@ -106,7 +107,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
 	sync.mutex_init(&thread.start_mutex);
 	sync.condition_init(&thread.start_gate, &thread.start_mutex);
 	if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
-		free(thread);
+		free(thread, thread.creation_allocator);
 		return nil;
 	}
 	thread.procedure = procedure;
@@ -172,7 +173,7 @@ join_multiple :: proc(threads: ..^Thread) {
 destroy :: proc(t: ^Thread) {
 	join(t);
 	t.unix_thread = {};
-	free(t);
+	free(t, t.creation_allocator);
 }
 
 

+ 6 - 2
core/thread/thread_windows.odin

@@ -49,10 +49,14 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
 
 
 	thread := new(Thread);
+	if thread == nil {
+		return nil;
+	}
+	thread.creation_allocator = context.allocator;
 
 	win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
 	if win32_thread == nil {
-		free(thread);
+		free(thread, thread.creation_allocator);
 		return nil;
 	}
 	thread.procedure       = procedure;
@@ -111,7 +115,7 @@ join_multiple :: proc(threads: ..^Thread) {
 
 destroy :: proc(thread: ^Thread) {
 	join(thread);
-	free(thread);
+	free(thread, thread.creation_allocator);
 }
 
 terminate :: proc(using thread : ^Thread, exit_code: u32) {

+ 42 - 996
core/unicode/letter.odin

@@ -60,7 +60,7 @@ to_title :: proc(r: rune) -> rune {
 
 is_lower :: proc(r: rune) -> bool {
 	if r <= MAX_ASCII {
-		return u8(r)-'a' < 26;
+		return u32(r)-'a' < 26;
 	}
 	c := i32(r);
 	p := binary_search(c, to_upper_ranges[:], len(to_upper_ranges)/3, 3);
@@ -76,7 +76,7 @@ is_lower :: proc(r: rune) -> bool {
 
 is_upper :: proc(r: rune) -> bool {
 	if r <= MAX_ASCII {
-		return u8(r)-'A' < 26;
+		return u32(r)-'A' < 26;
 	}
 	c := i32(r);
 	p := binary_search(c, to_lower_ranges[:], len(to_lower_ranges)/3, 3);
@@ -90,9 +90,10 @@ is_upper :: proc(r: rune) -> bool {
 	return false;
 }
 
-is_alpha :: proc(r: rune) -> bool {
-	if r <= MAX_ASCII {
-		return (u8(r)|32)-'a' < 26;
+is_alpha :: is_letter;
+is_letter :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pLmask != 0;
 	}
 	if is_upper(r) || is_lower(r) {
 		return true;
@@ -150,1000 +151,45 @@ is_combining :: proc(r: rune) -> bool {
 }
 
 
-@(static)
-alpha_ranges := [?]i32{
-	0x00d8,  0x00f6,
-	0x00f8,  0x01f5,
-	0x0250,  0x02a8,
-	0x038e,  0x03a1,
-	0x03a3,  0x03ce,
-	0x03d0,  0x03d6,
-	0x03e2,  0x03f3,
-	0x0490,  0x04c4,
-	0x0561,  0x0587,
-	0x05d0,  0x05ea,
-	0x05f0,  0x05f2,
-	0x0621,  0x063a,
-	0x0640,  0x064a,
-	0x0671,  0x06b7,
-	0x06ba,  0x06be,
-	0x06c0,  0x06ce,
-	0x06d0,  0x06d3,
-	0x0905,  0x0939,
-	0x0958,  0x0961,
-	0x0985,  0x098c,
-	0x098f,  0x0990,
-	0x0993,  0x09a8,
-	0x09aa,  0x09b0,
-	0x09b6,  0x09b9,
-	0x09dc,  0x09dd,
-	0x09df,  0x09e1,
-	0x09f0,  0x09f1,
-	0x0a05,  0x0a0a,
-	0x0a0f,  0x0a10,
-	0x0a13,  0x0a28,
-	0x0a2a,  0x0a30,
-	0x0a32,  0x0a33,
-	0x0a35,  0x0a36,
-	0x0a38,  0x0a39,
-	0x0a59,  0x0a5c,
-	0x0a85,  0x0a8b,
-	0x0a8f,  0x0a91,
-	0x0a93,  0x0aa8,
-	0x0aaa,  0x0ab0,
-	0x0ab2,  0x0ab3,
-	0x0ab5,  0x0ab9,
-	0x0b05,  0x0b0c,
-	0x0b0f,  0x0b10,
-	0x0b13,  0x0b28,
-	0x0b2a,  0x0b30,
-	0x0b32,  0x0b33,
-	0x0b36,  0x0b39,
-	0x0b5c,  0x0b5d,
-	0x0b5f,  0x0b61,
-	0x0b85,  0x0b8a,
-	0x0b8e,  0x0b90,
-	0x0b92,  0x0b95,
-	0x0b99,  0x0b9a,
-	0x0b9e,  0x0b9f,
-	0x0ba3,  0x0ba4,
-	0x0ba8,  0x0baa,
-	0x0bae,  0x0bb5,
-	0x0bb7,  0x0bb9,
-	0x0c05,  0x0c0c,
-	0x0c0e,  0x0c10,
-	0x0c12,  0x0c28,
-	0x0c2a,  0x0c33,
-	0x0c35,  0x0c39,
-	0x0c60,  0x0c61,
-	0x0c85,  0x0c8c,
-	0x0c8e,  0x0c90,
-	0x0c92,  0x0ca8,
-	0x0caa,  0x0cb3,
-	0x0cb5,  0x0cb9,
-	0x0ce0,  0x0ce1,
-	0x0d05,  0x0d0c,
-	0x0d0e,  0x0d10,
-	0x0d12,  0x0d28,
-	0x0d2a,  0x0d39,
-	0x0d60,  0x0d61,
-	0x0e01,  0x0e30,
-	0x0e32,  0x0e33,
-	0x0e40,  0x0e46,
-	0x0e5a,  0x0e5b,
-	0x0e81,  0x0e82,
-	0x0e87,  0x0e88,
-	0x0e94,  0x0e97,
-	0x0e99,  0x0e9f,
-	0x0ea1,  0x0ea3,
-	0x0eaa,  0x0eab,
-	0x0ead,  0x0eae,
-	0x0eb2,  0x0eb3,
-	0x0ec0,  0x0ec4,
-	0x0edc,  0x0edd,
-	0x0f18,  0x0f19,
-	0x0f40,  0x0f47,
-	0x0f49,  0x0f69,
-	0x10d0,  0x10f6,
-	0x1100,  0x1159,
-	0x115f,  0x11a2,
-	0x11a8,  0x11f9,
-	0x1e00,  0x1e9b,
-	0x1f50,  0x1f57,
-	0x1f80,  0x1fb4,
-	0x1fb6,  0x1fbc,
-	0x1fc2,  0x1fc4,
-	0x1fc6,  0x1fcc,
-	0x1fd0,  0x1fd3,
-	0x1fd6,  0x1fdb,
-	0x1fe0,  0x1fec,
-	0x1ff2,  0x1ff4,
-	0x1ff6,  0x1ffc,
-	0x210a,  0x2113,
-	0x2115,  0x211d,
-	0x2120,  0x2122,
-	0x212a,  0x2131,
-	0x2133,  0x2138,
-	0x3041,  0x3094,
-	0x30a1,  0x30fa,
-	0x3105,  0x312c,
-	0x3131,  0x318e,
-	0x3192,  0x319f,
-	0x3260,  0x327b,
-	0x328a,  0x32b0,
-	0x32d0,  0x32fe,
-	0x3300,  0x3357,
-	0x3371,  0x3376,
-	0x337b,  0x3394,
-	0x3399,  0x339e,
-	0x33a9,  0x33ad,
-	0x33b0,  0x33c1,
-	0x33c3,  0x33c5,
-	0x33c7,  0x33d7,
-	0x33d9,  0x33dd,
-	0x4e00,  0x9fff,
-	0xac00,  0xd7a3,
-	0xf900,  0xfb06,
-	0xfb13,  0xfb17,
-	0xfb1f,  0xfb28,
-	0xfb2a,  0xfb36,
-	0xfb38,  0xfb3c,
-	0xfb40,  0xfb41,
-	0xfb43,  0xfb44,
-	0xfb46,  0xfbb1,
-	0xfbd3,  0xfd3d,
-	0xfd50,  0xfd8f,
-	0xfd92,  0xfdc7,
-	0xfdf0,  0xfdf9,
-	0xfe70,  0xfe72,
-	0xfe76,  0xfefc,
-	0xff66,  0xff6f,
-	0xff71,  0xff9d,
-	0xffa0,  0xffbe,
-	0xffc2,  0xffc7,
-	0xffca,  0xffcf,
-	0xffd2,  0xffd7,
-	0xffda,  0xffdc,
-};
-
-@(static)
-alpha_singlets := [?]i32{
-	0x00aa,
-	0x00b5,
-	0x00ba,
-	0x03da,
-	0x03dc,
-	0x03de,
-	0x03e0,
-	0x06d5,
-	0x09b2,
-	0x0a5e,
-	0x0a8d,
-	0x0ae0,
-	0x0b9c,
-	0x0cde,
-	0x0e4f,
-	0x0e84,
-	0x0e8a,
-	0x0e8d,
-	0x0ea5,
-	0x0ea7,
-	0x0eb0,
-	0x0ebd,
-	0x1fbe,
-	0x207f,
-	0x20a8,
-	0x2102,
-	0x2107,
-	0x2124,
-	0x2126,
-	0x2128,
-	0xfb3e,
-	0xfe74,
-};
-
-@(static)
-space_ranges := [?]i32{
-	0x0009,  0x000d, // tab and newline
-	0x0020,  0x0020, // space
-	0x0085,  0x0085, // next line
-	0x00a0,  0x00a0,
-	0x1680,  0x1680, // Ogham space mark
-	0x2000,  0x200b, // en dash .. zero-width space
-	0x200e,  0x200f, // LTR mark .. RTL mark (pattern whitespace)
-	0x2028,  0x2029,     0x3000,  0x3000,
-	0x202f,  0x202f, // narrow no-break space
-	0x205f,  0x205f, // medium mathematical space
-	0x3000,  0x3000, // ideographic space
-	0xfeff,  0xfeff,
-};
 
-@(static)
-unicode_spaces := [?]i32{
-	0x0009, // tab
-	0x000a, // LF
-	0x000d, // CR
-	0x0020, // space
-	0x0085, // next line
-	0x00a0, // unknown
-	0x1680, // Ogham space mark
-	0x2000, // en dash .. zero-width space
-	0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace)
-	0x2028, 0x2029,  0x3000,  0x3000,
-	0x202f, // narrow no-break space
-	0x205f, // medium mathematical space
-	0x3000, // ideographic space
-	0xfeff, // unknown
-};
+is_graphic :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pg != 0;
+	}
+	return false;
+}
 
-@(static)
-to_upper_ranges := [?]i32{
-	0x0061,  0x007a, 468, // a-z A-Z
-	0x00e0,  0x00f6, 468,
-	0x00f8,  0x00fe, 468,
-	0x0256,  0x0257, 295,
-	0x0258,  0x0259, 298,
-	0x028a,  0x028b, 283,
-	0x03ad,  0x03af, 463,
-	0x03b1,  0x03c1, 468,
-	0x03c3,  0x03cb, 468,
-	0x03cd,  0x03ce, 437,
-	0x0430,  0x044f, 468,
-	0x0451,  0x045c, 420,
-	0x045e,  0x045f, 420,
-	0x0561,  0x0586, 452,
-	0x1f00,  0x1f07, 508,
-	0x1f10,  0x1f15, 508,
-	0x1f20,  0x1f27, 508,
-	0x1f30,  0x1f37, 508,
-	0x1f40,  0x1f45, 508,
-	0x1f60,  0x1f67, 508,
-	0x1f70,  0x1f71, 574,
-	0x1f72,  0x1f75, 586,
-	0x1f76,  0x1f77, 600,
-	0x1f78,  0x1f79, 628,
-	0x1f7a,  0x1f7b, 612,
-	0x1f7c,  0x1f7d, 626,
-	0x1f80,  0x1f87, 508,
-	0x1f90,  0x1f97, 508,
-	0x1fa0,  0x1fa7, 508,
-	0x1fb0,  0x1fb1, 508,
-	0x1fd0,  0x1fd1, 508,
-	0x1fe0,  0x1fe1, 508,
-	0x2170,  0x217f, 484,
-	0x24d0,  0x24e9, 474,
-	0xff41,  0xff5a, 468,
-};
+is_print :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pp != 0;
+	}
+	return false;
+}
 
-@(static)
-to_upper_singlets := [?]i32{
-	0x00ff, 621,
-	0x0101, 499,
-	0x0103, 499,
-	0x0105, 499,
-	0x0107, 499,
-	0x0109, 499,
-	0x010b, 499,
-	0x010d, 499,
-	0x010f, 499,
-	0x0111, 499,
-	0x0113, 499,
-	0x0115, 499,
-	0x0117, 499,
-	0x0119, 499,
-	0x011b, 499,
-	0x011d, 499,
-	0x011f, 499,
-	0x0121, 499,
-	0x0123, 499,
-	0x0125, 499,
-	0x0127, 499,
-	0x0129, 499,
-	0x012b, 499,
-	0x012d, 499,
-	0x012f, 499,
-	0x0131, 268, //  I
-	0x0133, 499,
-	0x0135, 499,
-	0x0137, 499,
-	0x013a, 499,
-	0x013c, 499,
-	0x013e, 499,
-	0x0140, 499,
-	0x0142, 499,
-	0x0144, 499,
-	0x0146, 499,
-	0x0148, 499,
-	0x014b, 499,
-	0x014d, 499,
-	0x014f, 499,
-	0x0151, 499,
-	0x0153, 499,
-	0x0155, 499,
-	0x0157, 499,
-	0x0159, 499,
-	0x015b, 499,
-	0x015d, 499,
-	0x015f, 499,
-	0x0161, 499,
-	0x0163, 499,
-	0x0165, 499,
-	0x0167, 499,
-	0x0169, 499,
-	0x016b, 499,
-	0x016d, 499,
-	0x016f, 499,
-	0x0171, 499,
-	0x0173, 499,
-	0x0175, 499,
-	0x0177, 499,
-	0x017a, 499,
-	0x017c, 499,
-	0x017e, 499,
-	0x017f, 200, //  S
-	0x0183, 499,
-	0x0185, 499,
-	0x0188, 499,
-	0x018c, 499,
-	0x0192, 499,
-	0x0199, 499,
-	0x01a1, 499,
-	0x01a3, 499,
-	0x01a5, 499,
-	0x01a8, 499,
-	0x01ad, 499,
-	0x01b0, 499,
-	0x01b4, 499,
-	0x01b6, 499,
-	0x01b9, 499,
-	0x01bd, 499,
-	0x01c5, 499,
-	0x01c6, 498,
-	0x01c8, 499,
-	0x01c9, 498,
-	0x01cb, 499,
-	0x01cc, 498,
-	0x01ce, 499,
-	0x01d0, 499,
-	0x01d2, 499,
-	0x01d4, 499,
-	0x01d6, 499,
-	0x01d8, 499,
-	0x01da, 499,
-	0x01dc, 499,
-	0x01df, 499,
-	0x01e1, 499,
-	0x01e3, 499,
-	0x01e5, 499,
-	0x01e7, 499,
-	0x01e9, 499,
-	0x01eb, 499,
-	0x01ed, 499,
-	0x01ef, 499,
-	0x01f2, 499,
-	0x01f3, 498,
-	0x01f5, 499,
-	0x01fb, 499,
-	0x01fd, 499,
-	0x01ff, 499,
-	0x0201, 499,
-	0x0203, 499,
-	0x0205, 499,
-	0x0207, 499,
-	0x0209, 499,
-	0x020b, 499,
-	0x020d, 499,
-	0x020f, 499,
-	0x0211, 499,
-	0x0213, 499,
-	0x0215, 499,
-	0x0217, 499,
-	0x0253, 290,
-	0x0254, 294,
-	0x025b, 297,
-	0x0260, 295,
-	0x0263, 293,
-	0x0268, 291,
-	0x0269, 289,
-	0x026f, 289,
-	0x0272, 287,
-	0x0283, 282,
-	0x0288, 282,
-	0x0292, 281,
-	0x03ac, 462,
-	0x03cc, 436,
-	0x03d0, 438,
-	0x03d1, 443,
-	0x03d5, 453,
-	0x03d6, 446,
-	0x03e3, 499,
-	0x03e5, 499,
-	0x03e7, 499,
-	0x03e9, 499,
-	0x03eb, 499,
-	0x03ed, 499,
-	0x03ef, 499,
-	0x03f0, 414,
-	0x03f1, 420,
-	0x0461, 499,
-	0x0463, 499,
-	0x0465, 499,
-	0x0467, 499,
-	0x0469, 499,
-	0x046b, 499,
-	0x046d, 499,
-	0x046f, 499,
-	0x0471, 499,
-	0x0473, 499,
-	0x0475, 499,
-	0x0477, 499,
-	0x0479, 499,
-	0x047b, 499,
-	0x047d, 499,
-	0x047f, 499,
-	0x0481, 499,
-	0x0491, 499,
-	0x0493, 499,
-	0x0495, 499,
-	0x0497, 499,
-	0x0499, 499,
-	0x049b, 499,
-	0x049d, 499,
-	0x049f, 499,
-	0x04a1, 499,
-	0x04a3, 499,
-	0x04a5, 499,
-	0x04a7, 499,
-	0x04a9, 499,
-	0x04ab, 499,
-	0x04ad, 499,
-	0x04af, 499,
-	0x04b1, 499,
-	0x04b3, 499,
-	0x04b5, 499,
-	0x04b7, 499,
-	0x04b9, 499,
-	0x04bb, 499,
-	0x04bd, 499,
-	0x04bf, 499,
-	0x04c2, 499,
-	0x04c4, 499,
-	0x04c8, 499,
-	0x04cc, 499,
-	0x04d1, 499,
-	0x04d3, 499,
-	0x04d5, 499,
-	0x04d7, 499,
-	0x04d9, 499,
-	0x04db, 499,
-	0x04dd, 499,
-	0x04df, 499,
-	0x04e1, 499,
-	0x04e3, 499,
-	0x04e5, 499,
-	0x04e7, 499,
-	0x04e9, 499,
-	0x04eb, 499,
-	0x04ef, 499,
-	0x04f1, 499,
-	0x04f3, 499,
-	0x04f5, 499,
-	0x04f9, 499,
-	0x1e01, 499,
-	0x1e03, 499,
-	0x1e05, 499,
-	0x1e07, 499,
-	0x1e09, 499,
-	0x1e0b, 499,
-	0x1e0d, 499,
-	0x1e0f, 499,
-	0x1e11, 499,
-	0x1e13, 499,
-	0x1e15, 499,
-	0x1e17, 499,
-	0x1e19, 499,
-	0x1e1b, 499,
-	0x1e1d, 499,
-	0x1e1f, 499,
-	0x1e21, 499,
-	0x1e23, 499,
-	0x1e25, 499,
-	0x1e27, 499,
-	0x1e29, 499,
-	0x1e2b, 499,
-	0x1e2d, 499,
-	0x1e2f, 499,
-	0x1e31, 499,
-	0x1e33, 499,
-	0x1e35, 499,
-	0x1e37, 499,
-	0x1e39, 499,
-	0x1e3b, 499,
-	0x1e3d, 499,
-	0x1e3f, 499,
-	0x1e41, 499,
-	0x1e43, 499,
-	0x1e45, 499,
-	0x1e47, 499,
-	0x1e49, 499,
-	0x1e4b, 499,
-	0x1e4d, 499,
-	0x1e4f, 499,
-	0x1e51, 499,
-	0x1e53, 499,
-	0x1e55, 499,
-	0x1e57, 499,
-	0x1e59, 499,
-	0x1e5b, 499,
-	0x1e5d, 499,
-	0x1e5f, 499,
-	0x1e61, 499,
-	0x1e63, 499,
-	0x1e65, 499,
-	0x1e67, 499,
-	0x1e69, 499,
-	0x1e6b, 499,
-	0x1e6d, 499,
-	0x1e6f, 499,
-	0x1e71, 499,
-	0x1e73, 499,
-	0x1e75, 499,
-	0x1e77, 499,
-	0x1e79, 499,
-	0x1e7b, 499,
-	0x1e7d, 499,
-	0x1e7f, 499,
-	0x1e81, 499,
-	0x1e83, 499,
-	0x1e85, 499,
-	0x1e87, 499,
-	0x1e89, 499,
-	0x1e8b, 499,
-	0x1e8d, 499,
-	0x1e8f, 499,
-	0x1e91, 499,
-	0x1e93, 499,
-	0x1e95, 499,
-	0x1ea1, 499,
-	0x1ea3, 499,
-	0x1ea5, 499,
-	0x1ea7, 499,
-	0x1ea9, 499,
-	0x1eab, 499,
-	0x1ead, 499,
-	0x1eaf, 499,
-	0x1eb1, 499,
-	0x1eb3, 499,
-	0x1eb5, 499,
-	0x1eb7, 499,
-	0x1eb9, 499,
-	0x1ebb, 499,
-	0x1ebd, 499,
-	0x1ebf, 499,
-	0x1ec1, 499,
-	0x1ec3, 499,
-	0x1ec5, 499,
-	0x1ec7, 499,
-	0x1ec9, 499,
-	0x1ecb, 499,
-	0x1ecd, 499,
-	0x1ecf, 499,
-	0x1ed1, 499,
-	0x1ed3, 499,
-	0x1ed5, 499,
-	0x1ed7, 499,
-	0x1ed9, 499,
-	0x1edb, 499,
-	0x1edd, 499,
-	0x1edf, 499,
-	0x1ee1, 499,
-	0x1ee3, 499,
-	0x1ee5, 499,
-	0x1ee7, 499,
-	0x1ee9, 499,
-	0x1eeb, 499,
-	0x1eed, 499,
-	0x1eef, 499,
-	0x1ef1, 499,
-	0x1ef3, 499,
-	0x1ef5, 499,
-	0x1ef7, 499,
-	0x1ef9, 499,
-	0x1f51, 508,
-	0x1f53, 508,
-	0x1f55, 508,
-	0x1f57, 508,
-	0x1fb3, 509,
-	0x1fc3, 509,
-	0x1fe5, 507,
-	0x1ff3, 509,
-};
+is_control :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pC != 0;
+	}
+	return false;
+}
 
-@(static)
-to_lower_ranges := [?]i32{
-	0x0041,  0x005a, 532, // A-Z a-z
-	0x00c0,  0x00d6, 532, // - -
-	0x00d8,  0x00de, 532, // - -
-	0x0189,  0x018a, 705, // - -
-	0x018e,  0x018f, 702, // - -
-	0x01b1,  0x01b2, 717, // - -
-	0x0388,  0x038a, 537, // - -
-	0x038e,  0x038f, 563, // - -
-	0x0391,  0x03a1, 532, // - -
-	0x03a3,  0x03ab, 532, // - -
-	0x0401,  0x040c, 580, // - -
-	0x040e,  0x040f, 580, // - -
-	0x0410,  0x042f, 532, // - -
-	0x0531,  0x0556, 548, // - -
-	0x10a0,  0x10c5, 548, // - -
-	0x1f08,  0x1f0f, 492, // - -
-	0x1f18,  0x1f1d, 492, // - -
-	0x1f28,  0x1f2f, 492, // - -
-	0x1f38,  0x1f3f, 492, // - -
-	0x1f48,  0x1f4d, 492, // - -
-	0x1f68,  0x1f6f, 492, // - -
-	0x1f88,  0x1f8f, 492, // - -
-	0x1f98,  0x1f9f, 492, // - -
-	0x1fa8,  0x1faf, 492, // - -
-	0x1fb8,  0x1fb9, 492, // - -
-	0x1fba,  0x1fbb, 426, // - -
-	0x1fc8,  0x1fcb, 414, // - -
-	0x1fd8,  0x1fd9, 492, // - -
-	0x1fda,  0x1fdb, 400, // - -
-	0x1fe8,  0x1fe9, 492, // - -
-	0x1fea,  0x1feb, 388, // - -
-	0x1ff8,  0x1ff9, 372, // - -
-	0x1ffa,  0x1ffb, 374, // - -
-	0x2160,  0x216f, 516, // - -
-	0x24b6,  0x24cf, 526, // - -
-	0xff21,  0xff3a, 532, // - -
-};
+is_number :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pN != 0;
+	}
+	return false;
+}
 
-@(static)
-to_lower_singlets := [?]i32{
-	0x0100, 501,
-	0x0102, 501,
-	0x0104, 501,
-	0x0106, 501,
-	0x0108, 501,
-	0x010a, 501,
-	0x010c, 501,
-	0x010e, 501,
-	0x0110, 501,
-	0x0112, 501,
-	0x0114, 501,
-	0x0116, 501,
-	0x0118, 501,
-	0x011a, 501,
-	0x011c, 501,
-	0x011e, 501,
-	0x0120, 501,
-	0x0122, 501,
-	0x0124, 501,
-	0x0126, 501,
-	0x0128, 501,
-	0x012a, 501,
-	0x012c, 501,
-	0x012e, 501,
-	0x0130, 301, //  i
-	0x0132, 501,
-	0x0134, 501,
-	0x0136, 501,
-	0x0139, 501,
-	0x013b, 501,
-	0x013d, 501,
-	0x013f, 501,
-	0x0141, 501,
-	0x0143, 501,
-	0x0145, 501,
-	0x0147, 501,
-	0x014a, 501,
-	0x014c, 501,
-	0x014e, 501,
-	0x0150, 501,
-	0x0152, 501,
-	0x0154, 501,
-	0x0156, 501,
-	0x0158, 501,
-	0x015a, 501,
-	0x015c, 501,
-	0x015e, 501,
-	0x0160, 501,
-	0x0162, 501,
-	0x0164, 501,
-	0x0166, 501,
-	0x0168, 501,
-	0x016a, 501,
-	0x016c, 501,
-	0x016e, 501,
-	0x0170, 501,
-	0x0172, 501,
-	0x0174, 501,
-	0x0176, 501,
-	0x0178, 379,
-	0x0179, 501,
-	0x017b, 501,
-	0x017d, 501,
-	0x0181, 710,
-	0x0182, 501,
-	0x0184, 501,
-	0x0186, 706,
-	0x0187, 501,
-	0x018b, 501,
-	0x0190, 703,
-	0x0191, 501,
-	0x0193, 705,
-	0x0194, 707,
-	0x0196, 711,
-	0x0197, 709,
-	0x0198, 501,
-	0x019c, 711,
-	0x019d, 713,
-	0x01a0, 501,
-	0x01a2, 501,
-	0x01a4, 501,
-	0x01a7, 501,
-	0x01a9, 718,
-	0x01ac, 501,
-	0x01ae, 718,
-	0x01af, 501,
-	0x01b3, 501,
-	0x01b5, 501,
-	0x01b7, 719,
-	0x01b8, 501,
-	0x01bc, 501,
-	0x01c4, 502,
-	0x01c5, 501,
-	0x01c7, 502,
-	0x01c8, 501,
-	0x01ca, 502,
-	0x01cb, 501,
-	0x01cd, 501,
-	0x01cf, 501,
-	0x01d1, 501,
-	0x01d3, 501,
-	0x01d5, 501,
-	0x01d7, 501,
-	0x01d9, 501,
-	0x01db, 501,
-	0x01de, 501,
-	0x01e0, 501,
-	0x01e2, 501,
-	0x01e4, 501,
-	0x01e6, 501,
-	0x01e8, 501,
-	0x01ea, 501,
-	0x01ec, 501,
-	0x01ee, 501,
-	0x01f1, 502,
-	0x01f2, 501,
-	0x01f4, 501,
-	0x01fa, 501,
-	0x01fc, 501,
-	0x01fe, 501,
-	0x0200, 501,
-	0x0202, 501,
-	0x0204, 501,
-	0x0206, 501,
-	0x0208, 501,
-	0x020a, 501,
-	0x020c, 501,
-	0x020e, 501,
-	0x0210, 501,
-	0x0212, 501,
-	0x0214, 501,
-	0x0216, 501,
-	0x0386, 538,
-	0x038c, 564,
-	0x03e2, 501,
-	0x03e4, 501,
-	0x03e6, 501,
-	0x03e8, 501,
-	0x03ea, 501,
-	0x03ec, 501,
-	0x03ee, 501,
-	0x0460, 501,
-	0x0462, 501,
-	0x0464, 501,
-	0x0466, 501,
-	0x0468, 501,
-	0x046a, 501,
-	0x046c, 501,
-	0x046e, 501,
-	0x0470, 501,
-	0x0472, 501,
-	0x0474, 501,
-	0x0476, 501,
-	0x0478, 501,
-	0x047a, 501,
-	0x047c, 501,
-	0x047e, 501,
-	0x0480, 501,
-	0x0490, 501,
-	0x0492, 501,
-	0x0494, 501,
-	0x0496, 501,
-	0x0498, 501,
-	0x049a, 501,
-	0x049c, 501,
-	0x049e, 501,
-	0x04a0, 501,
-	0x04a2, 501,
-	0x04a4, 501,
-	0x04a6, 501,
-	0x04a8, 501,
-	0x04aa, 501,
-	0x04ac, 501,
-	0x04ae, 501,
-	0x04b0, 501,
-	0x04b2, 501,
-	0x04b4, 501,
-	0x04b6, 501,
-	0x04b8, 501,
-	0x04ba, 501,
-	0x04bc, 501,
-	0x04be, 501,
-	0x04c1, 501,
-	0x04c3, 501,
-	0x04c7, 501,
-	0x04cb, 501,
-	0x04d0, 501,
-	0x04d2, 501,
-	0x04d4, 501,
-	0x04d6, 501,
-	0x04d8, 501,
-	0x04da, 501,
-	0x04dc, 501,
-	0x04de, 501,
-	0x04e0, 501,
-	0x04e2, 501,
-	0x04e4, 501,
-	0x04e6, 501,
-	0x04e8, 501,
-	0x04ea, 501,
-	0x04ee, 501,
-	0x04f0, 501,
-	0x04f2, 501,
-	0x04f4, 501,
-	0x04f8, 501,
-	0x1e00, 501,
-	0x1e02, 501,
-	0x1e04, 501,
-	0x1e06, 501,
-	0x1e08, 501,
-	0x1e0a, 501,
-	0x1e0c, 501,
-	0x1e0e, 501,
-	0x1e10, 501,
-	0x1e12, 501,
-	0x1e14, 501,
-	0x1e16, 501,
-	0x1e18, 501,
-	0x1e1a, 501,
-	0x1e1c, 501,
-	0x1e1e, 501,
-	0x1e20, 501,
-	0x1e22, 501,
-	0x1e24, 501,
-	0x1e26, 501,
-	0x1e28, 501,
-	0x1e2a, 501,
-	0x1e2c, 501,
-	0x1e2e, 501,
-	0x1e30, 501,
-	0x1e32, 501,
-	0x1e34, 501,
-	0x1e36, 501,
-	0x1e38, 501,
-	0x1e3a, 501,
-	0x1e3c, 501,
-	0x1e3e, 501,
-	0x1e40, 501,
-	0x1e42, 501,
-	0x1e44, 501,
-	0x1e46, 501,
-	0x1e48, 501,
-	0x1e4a, 501,
-	0x1e4c, 501,
-	0x1e4e, 501,
-	0x1e50, 501,
-	0x1e52, 501,
-	0x1e54, 501,
-	0x1e56, 501,
-	0x1e58, 501,
-	0x1e5a, 501,
-	0x1e5c, 501,
-	0x1e5e, 501,
-	0x1e60, 501,
-	0x1e62, 501,
-	0x1e64, 501,
-	0x1e66, 501,
-	0x1e68, 501,
-	0x1e6a, 501,
-	0x1e6c, 501,
-	0x1e6e, 501,
-	0x1e70, 501,
-	0x1e72, 501,
-	0x1e74, 501,
-	0x1e76, 501,
-	0x1e78, 501,
-	0x1e7a, 501,
-	0x1e7c, 501,
-	0x1e7e, 501,
-	0x1e80, 501,
-	0x1e82, 501,
-	0x1e84, 501,
-	0x1e86, 501,
-	0x1e88, 501,
-	0x1e8a, 501,
-	0x1e8c, 501,
-	0x1e8e, 501,
-	0x1e90, 501,
-	0x1e92, 501,
-	0x1e94, 501,
-	0x1ea0, 501,
-	0x1ea2, 501,
-	0x1ea4, 501,
-	0x1ea6, 501,
-	0x1ea8, 501,
-	0x1eaa, 501,
-	0x1eac, 501,
-	0x1eae, 501,
-	0x1eb0, 501,
-	0x1eb2, 501,
-	0x1eb4, 501,
-	0x1eb6, 501,
-	0x1eb8, 501,
-	0x1eba, 501,
-	0x1ebc, 501,
-	0x1ebe, 501,
-	0x1ec0, 501,
-	0x1ec2, 501,
-	0x1ec4, 501,
-	0x1ec6, 501,
-	0x1ec8, 501,
-	0x1eca, 501,
-	0x1ecc, 501,
-	0x1ece, 501,
-	0x1ed0, 501,
-	0x1ed2, 501,
-	0x1ed4, 501,
-	0x1ed6, 501,
-	0x1ed8, 501,
-	0x1eda, 501,
-	0x1edc, 501,
-	0x1ede, 501,
-	0x1ee0, 501,
-	0x1ee2, 501,
-	0x1ee4, 501,
-	0x1ee6, 501,
-	0x1ee8, 501,
-	0x1eea, 501,
-	0x1eec, 501,
-	0x1eee, 501,
-	0x1ef0, 501,
-	0x1ef2, 501,
-	0x1ef4, 501,
-	0x1ef6, 501,
-	0x1ef8, 501,
-	0x1f59, 492,
-	0x1f5b, 492,
-	0x1f5d, 492,
-	0x1f5f, 492,
-	0x1fbc, 491,
-	0x1fcc, 491,
-	0x1fec, 493,
-	0x1ffc, 491,
-};
+is_punct :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pP != 0;
+	}
+	return false;
+}
 
-@(static)
-to_title_singlets := [?]i32{
-	0x01c4, 501,
-	0x01c6, 499,
-	0x01c7, 501,
-	0x01c9, 499,
-	0x01ca, 501,
-	0x01cc, 499,
-	0x01f1, 501,
-	0x01f3, 499,
-};
+is_symbol :: proc(r: rune) -> bool {
+	if u32(r) <= MAX_LATIN1 {
+		return char_properties[u8(r)]&pS != 0;
+	}
+	return false;
+}

+ 1272 - 0
core/unicode/tables.odin

@@ -0,0 +1,1272 @@
+package unicode
+
+@(private) pC     :: 1<<0; // a control character.
+@(private) pP     :: 1<<1; // a punctuation character.
+@(private) pN     :: 1<<2; // a numeral.
+@(private) pS     :: 1<<3; // a symbolic character.
+@(private) pZ     :: 1<<4; // a spacing character.
+@(private) pLu    :: 1<<5; // an upper-case letter.
+@(private) pLl    :: 1<<6; // a lower-case letter.
+@(private) pp     :: 1<<7; // a printable character according to Go's definition.
+@(private) pg     :: pp | pZ;   // a graphical character according to the Unicode definition.
+@(private) pLo    :: pLl | pLu; // a letter that is neither upper nor lower case.
+@(private) pLmask :: pLo;
+
+@(static)
+char_properties := [MAX_LATIN1+1]u8{
+	0x00 = pC,       // '\x00'
+	0x01 = pC,       // '\x01'
+	0x02 = pC,       // '\x02'
+	0x03 = pC,       // '\x03'
+	0x04 = pC,       // '\x04'
+	0x05 = pC,       // '\x05'
+	0x06 = pC,       // '\x06'
+	0x07 = pC,       // '\a'
+	0x08 = pC,       // '\b'
+	0x09 = pC,       // '\t'
+	0x0A = pC,       // '\n'
+	0x0B = pC,       // '\v'
+	0x0C = pC,       // '\f'
+	0x0D = pC,       // '\r'
+	0x0E = pC,       // '\x0e'
+	0x0F = pC,       // '\x0f'
+	0x10 = pC,       // '\x10'
+	0x11 = pC,       // '\x11'
+	0x12 = pC,       // '\x12'
+	0x13 = pC,       // '\x13'
+	0x14 = pC,       // '\x14'
+	0x15 = pC,       // '\x15'
+	0x16 = pC,       // '\x16'
+	0x17 = pC,       // '\x17'
+	0x18 = pC,       // '\x18'
+	0x19 = pC,       // '\x19'
+	0x1A = pC,       // '\x1a'
+	0x1B = pC,       // '\x1b'
+	0x1C = pC,       // '\x1c'
+	0x1D = pC,       // '\x1d'
+	0x1E = pC,       // '\x1e'
+	0x1F = pC,       // '\x1f'
+	0x20 = pZ | pp,  // ' '
+	0x21 = pP | pp,  // '!'
+	0x22 = pP | pp,  // '"'
+	0x23 = pP | pp,  // '#'
+	0x24 = pS | pp,  // '$'
+	0x25 = pP | pp,  // '%'
+	0x26 = pP | pp,  // '&'
+	0x27 = pP | pp,  // '\''
+	0x28 = pP | pp,  // '('
+	0x29 = pP | pp,  // ')'
+	0x2A = pP | pp,  // '*'
+	0x2B = pS | pp,  // '+'
+	0x2C = pP | pp,  // ','
+	0x2D = pP | pp,  // '-'
+	0x2E = pP | pp,  // '.'
+	0x2F = pP | pp,  // '/'
+	0x30 = pN | pp,  // '0'
+	0x31 = pN | pp,  // '1'
+	0x32 = pN | pp,  // '2'
+	0x33 = pN | pp,  // '3'
+	0x34 = pN | pp,  // '4'
+	0x35 = pN | pp,  // '5'
+	0x36 = pN | pp,  // '6'
+	0x37 = pN | pp,  // '7'
+	0x38 = pN | pp,  // '8'
+	0x39 = pN | pp,  // '9'
+	0x3A = pP | pp,  // ':'
+	0x3B = pP | pp,  // ';'
+	0x3C = pS | pp,  // '<'
+	0x3D = pS | pp,  // '='
+	0x3E = pS | pp,  // '>'
+	0x3F = pP | pp,  // '?'
+	0x40 = pP | pp,  // '@'
+	0x41 = pLu | pp, // 'A'
+	0x42 = pLu | pp, // 'B'
+	0x43 = pLu | pp, // 'C'
+	0x44 = pLu | pp, // 'D'
+	0x45 = pLu | pp, // 'E'
+	0x46 = pLu | pp, // 'F'
+	0x47 = pLu | pp, // 'G'
+	0x48 = pLu | pp, // 'H'
+	0x49 = pLu | pp, // 'I'
+	0x4A = pLu | pp, // 'J'
+	0x4B = pLu | pp, // 'K'
+	0x4C = pLu | pp, // 'L'
+	0x4D = pLu | pp, // 'M'
+	0x4E = pLu | pp, // 'N'
+	0x4F = pLu | pp, // 'O'
+	0x50 = pLu | pp, // 'P'
+	0x51 = pLu | pp, // 'Q'
+	0x52 = pLu | pp, // 'R'
+	0x53 = pLu | pp, // 'S'
+	0x54 = pLu | pp, // 'T'
+	0x55 = pLu | pp, // 'U'
+	0x56 = pLu | pp, // 'V'
+	0x57 = pLu | pp, // 'W'
+	0x58 = pLu | pp, // 'X'
+	0x59 = pLu | pp, // 'Y'
+	0x5A = pLu | pp, // 'Z'
+	0x5B = pP | pp,  // '['
+	0x5C = pP | pp,  // '\\'
+	0x5D = pP | pp,  // ']'
+	0x5E = pS | pp,  // '^'
+	0x5F = pP | pp,  // '_'
+	0x60 = pS | pp,  // '`'
+	0x61 = pLl | pp, // 'a'
+	0x62 = pLl | pp, // 'b'
+	0x63 = pLl | pp, // 'c'
+	0x64 = pLl | pp, // 'd'
+	0x65 = pLl | pp, // 'e'
+	0x66 = pLl | pp, // 'f'
+	0x67 = pLl | pp, // 'g'
+	0x68 = pLl | pp, // 'h'
+	0x69 = pLl | pp, // 'i'
+	0x6A = pLl | pp, // 'j'
+	0x6B = pLl | pp, // 'k'
+	0x6C = pLl | pp, // 'l'
+	0x6D = pLl | pp, // 'm'
+	0x6E = pLl | pp, // 'n'
+	0x6F = pLl | pp, // 'o'
+	0x70 = pLl | pp, // 'p'
+	0x71 = pLl | pp, // 'q'
+	0x72 = pLl | pp, // 'r'
+	0x73 = pLl | pp, // 's'
+	0x74 = pLl | pp, // 't'
+	0x75 = pLl | pp, // 'u'
+	0x76 = pLl | pp, // 'v'
+	0x77 = pLl | pp, // 'w'
+	0x78 = pLl | pp, // 'x'
+	0x79 = pLl | pp, // 'y'
+	0x7A = pLl | pp, // 'z'
+	0x7B = pP | pp,  // '{'
+	0x7C = pS | pp,  // '|'
+	0x7D = pP | pp,  // '}'
+	0x7E = pS | pp,  // '~'
+	0x7F = pC,       // '\u007f'
+	0x80 = pC,       // '\u0080'
+	0x81 = pC,       // '\u0081'
+	0x82 = pC,       // '\u0082'
+	0x83 = pC,       // '\u0083'
+	0x84 = pC,       // '\u0084'
+	0x85 = pC,       // '\u0085'
+	0x86 = pC,       // '\u0086'
+	0x87 = pC,       // '\u0087'
+	0x88 = pC,       // '\u0088'
+	0x89 = pC,       // '\u0089'
+	0x8A = pC,       // '\u008a'
+	0x8B = pC,       // '\u008b'
+	0x8C = pC,       // '\u008c'
+	0x8D = pC,       // '\u008d'
+	0x8E = pC,       // '\u008e'
+	0x8F = pC,       // '\u008f'
+	0x90 = pC,       // '\u0090'
+	0x91 = pC,       // '\u0091'
+	0x92 = pC,       // '\u0092'
+	0x93 = pC,       // '\u0093'
+	0x94 = pC,       // '\u0094'
+	0x95 = pC,       // '\u0095'
+	0x96 = pC,       // '\u0096'
+	0x97 = pC,       // '\u0097'
+	0x98 = pC,       // '\u0098'
+	0x99 = pC,       // '\u0099'
+	0x9A = pC,       // '\u009a'
+	0x9B = pC,       // '\u009b'
+	0x9C = pC,       // '\u009c'
+	0x9D = pC,       // '\u009d'
+	0x9E = pC,       // '\u009e'
+	0x9F = pC,       // '\u009f'
+	0xA0 = pZ,       // '\u00a0'
+	0xA1 = pP | pp,  // '¡'
+	0xA2 = pS | pp,  // '¢'
+	0xA3 = pS | pp,  // '£'
+	0xA4 = pS | pp,  // '¤'
+	0xA5 = pS | pp,  // '¥'
+	0xA6 = pS | pp,  // '¦'
+	0xA7 = pP | pp,  // '§'
+	0xA8 = pS | pp,  // '¨'
+	0xA9 = pS | pp,  // '©'
+	0xAA = pLo | pp, // 'ª'
+	0xAB = pP | pp,  // '«'
+	0xAC = pS | pp,  // '¬'
+	0xAD = 0,        // '\u00ad'
+	0xAE = pS | pp,  // '®'
+	0xAF = pS | pp,  // '¯'
+	0xB0 = pS | pp,  // '°'
+	0xB1 = pS | pp,  // '±'
+	0xB2 = pN | pp,  // '²'
+	0xB3 = pN | pp,  // '³'
+	0xB4 = pS | pp,  // '´'
+	0xB5 = pLl | pp, // 'µ'
+	0xB6 = pP | pp,  // '¶'
+	0xB7 = pP | pp,  // '·'
+	0xB8 = pS | pp,  // '¸'
+	0xB9 = pN | pp,  // '¹'
+	0xBA = pLo | pp, // 'º'
+	0xBB = pP | pp,  // '»'
+	0xBC = pN | pp,  // '¼'
+	0xBD = pN | pp,  // '½'
+	0xBE = pN | pp,  // '¾'
+	0xBF = pP | pp,  // '¿'
+	0xC0 = pLu | pp, // 'À'
+	0xC1 = pLu | pp, // 'Á'
+	0xC2 = pLu | pp, // 'Â'
+	0xC3 = pLu | pp, // 'Ã'
+	0xC4 = pLu | pp, // 'Ä'
+	0xC5 = pLu | pp, // 'Å'
+	0xC6 = pLu | pp, // 'Æ'
+	0xC7 = pLu | pp, // 'Ç'
+	0xC8 = pLu | pp, // 'È'
+	0xC9 = pLu | pp, // 'É'
+	0xCA = pLu | pp, // 'Ê'
+	0xCB = pLu | pp, // 'Ë'
+	0xCC = pLu | pp, // 'Ì'
+	0xCD = pLu | pp, // 'Í'
+	0xCE = pLu | pp, // 'Î'
+	0xCF = pLu | pp, // 'Ï'
+	0xD0 = pLu | pp, // 'Ð'
+	0xD1 = pLu | pp, // 'Ñ'
+	0xD2 = pLu | pp, // 'Ò'
+	0xD3 = pLu | pp, // 'Ó'
+	0xD4 = pLu | pp, // 'Ô'
+	0xD5 = pLu | pp, // 'Õ'
+	0xD6 = pLu | pp, // 'Ö'
+	0xD7 = pS | pp,  // '×'
+	0xD8 = pLu | pp, // 'Ø'
+	0xD9 = pLu | pp, // 'Ù'
+	0xDA = pLu | pp, // 'Ú'
+	0xDB = pLu | pp, // 'Û'
+	0xDC = pLu | pp, // 'Ü'
+	0xDD = pLu | pp, // 'Ý'
+	0xDE = pLu | pp, // 'Þ'
+	0xDF = pLl | pp, // 'ß'
+	0xE0 = pLl | pp, // 'à'
+	0xE1 = pLl | pp, // 'á'
+	0xE2 = pLl | pp, // 'â'
+	0xE3 = pLl | pp, // 'ã'
+	0xE4 = pLl | pp, // 'ä'
+	0xE5 = pLl | pp, // 'å'
+	0xE6 = pLl | pp, // 'æ'
+	0xE7 = pLl | pp, // 'ç'
+	0xE8 = pLl | pp, // 'è'
+	0xE9 = pLl | pp, // 'é'
+	0xEA = pLl | pp, // 'ê'
+	0xEB = pLl | pp, // 'ë'
+	0xEC = pLl | pp, // 'ì'
+	0xED = pLl | pp, // 'í'
+	0xEE = pLl | pp, // 'î'
+	0xEF = pLl | pp, // 'ï'
+	0xF0 = pLl | pp, // 'ð'
+	0xF1 = pLl | pp, // 'ñ'
+	0xF2 = pLl | pp, // 'ò'
+	0xF3 = pLl | pp, // 'ó'
+	0xF4 = pLl | pp, // 'ô'
+	0xF5 = pLl | pp, // 'õ'
+	0xF6 = pLl | pp, // 'ö'
+	0xF7 = pS | pp,  // '÷'
+	0xF8 = pLl | pp, // 'ø'
+	0xF9 = pLl | pp, // 'ù'
+	0xFA = pLl | pp, // 'ú'
+	0xFB = pLl | pp, // 'û'
+	0xFC = pLl | pp, // 'ü'
+	0xFD = pLl | pp, // 'ý'
+	0xFE = pLl | pp, // 'þ'
+	0xFF = pLl | pp, // 'ÿ'
+};
+
+
+@(static)
+alpha_ranges := [?]i32{
+	0x00d8,  0x00f6,
+	0x00f8,  0x01f5,
+	0x0250,  0x02a8,
+	0x038e,  0x03a1,
+	0x03a3,  0x03ce,
+	0x03d0,  0x03d6,
+	0x03e2,  0x03f3,
+	0x0490,  0x04c4,
+	0x0561,  0x0587,
+	0x05d0,  0x05ea,
+	0x05f0,  0x05f2,
+	0x0621,  0x063a,
+	0x0640,  0x064a,
+	0x0671,  0x06b7,
+	0x06ba,  0x06be,
+	0x06c0,  0x06ce,
+	0x06d0,  0x06d3,
+	0x0905,  0x0939,
+	0x0958,  0x0961,
+	0x0985,  0x098c,
+	0x098f,  0x0990,
+	0x0993,  0x09a8,
+	0x09aa,  0x09b0,
+	0x09b6,  0x09b9,
+	0x09dc,  0x09dd,
+	0x09df,  0x09e1,
+	0x09f0,  0x09f1,
+	0x0a05,  0x0a0a,
+	0x0a0f,  0x0a10,
+	0x0a13,  0x0a28,
+	0x0a2a,  0x0a30,
+	0x0a32,  0x0a33,
+	0x0a35,  0x0a36,
+	0x0a38,  0x0a39,
+	0x0a59,  0x0a5c,
+	0x0a85,  0x0a8b,
+	0x0a8f,  0x0a91,
+	0x0a93,  0x0aa8,
+	0x0aaa,  0x0ab0,
+	0x0ab2,  0x0ab3,
+	0x0ab5,  0x0ab9,
+	0x0b05,  0x0b0c,
+	0x0b0f,  0x0b10,
+	0x0b13,  0x0b28,
+	0x0b2a,  0x0b30,
+	0x0b32,  0x0b33,
+	0x0b36,  0x0b39,
+	0x0b5c,  0x0b5d,
+	0x0b5f,  0x0b61,
+	0x0b85,  0x0b8a,
+	0x0b8e,  0x0b90,
+	0x0b92,  0x0b95,
+	0x0b99,  0x0b9a,
+	0x0b9e,  0x0b9f,
+	0x0ba3,  0x0ba4,
+	0x0ba8,  0x0baa,
+	0x0bae,  0x0bb5,
+	0x0bb7,  0x0bb9,
+	0x0c05,  0x0c0c,
+	0x0c0e,  0x0c10,
+	0x0c12,  0x0c28,
+	0x0c2a,  0x0c33,
+	0x0c35,  0x0c39,
+	0x0c60,  0x0c61,
+	0x0c85,  0x0c8c,
+	0x0c8e,  0x0c90,
+	0x0c92,  0x0ca8,
+	0x0caa,  0x0cb3,
+	0x0cb5,  0x0cb9,
+	0x0ce0,  0x0ce1,
+	0x0d05,  0x0d0c,
+	0x0d0e,  0x0d10,
+	0x0d12,  0x0d28,
+	0x0d2a,  0x0d39,
+	0x0d60,  0x0d61,
+	0x0e01,  0x0e30,
+	0x0e32,  0x0e33,
+	0x0e40,  0x0e46,
+	0x0e5a,  0x0e5b,
+	0x0e81,  0x0e82,
+	0x0e87,  0x0e88,
+	0x0e94,  0x0e97,
+	0x0e99,  0x0e9f,
+	0x0ea1,  0x0ea3,
+	0x0eaa,  0x0eab,
+	0x0ead,  0x0eae,
+	0x0eb2,  0x0eb3,
+	0x0ec0,  0x0ec4,
+	0x0edc,  0x0edd,
+	0x0f18,  0x0f19,
+	0x0f40,  0x0f47,
+	0x0f49,  0x0f69,
+	0x10d0,  0x10f6,
+	0x1100,  0x1159,
+	0x115f,  0x11a2,
+	0x11a8,  0x11f9,
+	0x1e00,  0x1e9b,
+	0x1f50,  0x1f57,
+	0x1f80,  0x1fb4,
+	0x1fb6,  0x1fbc,
+	0x1fc2,  0x1fc4,
+	0x1fc6,  0x1fcc,
+	0x1fd0,  0x1fd3,
+	0x1fd6,  0x1fdb,
+	0x1fe0,  0x1fec,
+	0x1ff2,  0x1ff4,
+	0x1ff6,  0x1ffc,
+	0x210a,  0x2113,
+	0x2115,  0x211d,
+	0x2120,  0x2122,
+	0x212a,  0x2131,
+	0x2133,  0x2138,
+	0x3041,  0x3094,
+	0x30a1,  0x30fa,
+	0x3105,  0x312c,
+	0x3131,  0x318e,
+	0x3192,  0x319f,
+	0x3260,  0x327b,
+	0x328a,  0x32b0,
+	0x32d0,  0x32fe,
+	0x3300,  0x3357,
+	0x3371,  0x3376,
+	0x337b,  0x3394,
+	0x3399,  0x339e,
+	0x33a9,  0x33ad,
+	0x33b0,  0x33c1,
+	0x33c3,  0x33c5,
+	0x33c7,  0x33d7,
+	0x33d9,  0x33dd,
+	0x4e00,  0x9fff,
+	0xac00,  0xd7a3,
+	0xf900,  0xfb06,
+	0xfb13,  0xfb17,
+	0xfb1f,  0xfb28,
+	0xfb2a,  0xfb36,
+	0xfb38,  0xfb3c,
+	0xfb40,  0xfb41,
+	0xfb43,  0xfb44,
+	0xfb46,  0xfbb1,
+	0xfbd3,  0xfd3d,
+	0xfd50,  0xfd8f,
+	0xfd92,  0xfdc7,
+	0xfdf0,  0xfdf9,
+	0xfe70,  0xfe72,
+	0xfe76,  0xfefc,
+	0xff66,  0xff6f,
+	0xff71,  0xff9d,
+	0xffa0,  0xffbe,
+	0xffc2,  0xffc7,
+	0xffca,  0xffcf,
+	0xffd2,  0xffd7,
+	0xffda,  0xffdc,
+};
+
+@(static)
+alpha_singlets := [?]i32{
+	0x00aa,
+	0x00b5,
+	0x00ba,
+	0x03da,
+	0x03dc,
+	0x03de,
+	0x03e0,
+	0x06d5,
+	0x09b2,
+	0x0a5e,
+	0x0a8d,
+	0x0ae0,
+	0x0b9c,
+	0x0cde,
+	0x0e4f,
+	0x0e84,
+	0x0e8a,
+	0x0e8d,
+	0x0ea5,
+	0x0ea7,
+	0x0eb0,
+	0x0ebd,
+	0x1fbe,
+	0x207f,
+	0x20a8,
+	0x2102,
+	0x2107,
+	0x2124,
+	0x2126,
+	0x2128,
+	0xfb3e,
+	0xfe74,
+};
+
+@(static)
+space_ranges := [?]i32{
+	0x0009,  0x000d, // tab and newline
+	0x0020,  0x0020, // space
+	0x0085,  0x0085, // next line
+	0x00a0,  0x00a0,
+	0x1680,  0x1680, // Ogham space mark
+	0x2000,  0x200b, // en dash .. zero-width space
+	0x200e,  0x200f, // LTR mark .. RTL mark (pattern whitespace)
+	0x2028,  0x2029,     0x3000,  0x3000,
+	0x202f,  0x202f, // narrow no-break space
+	0x205f,  0x205f, // medium mathematical space
+	0x3000,  0x3000, // ideographic space
+	0xfeff,  0xfeff,
+};
+
+@(static)
+unicode_spaces := [?]i32{
+	0x0009, // tab
+	0x000a, // LF
+	0x000d, // CR
+	0x0020, // space
+	0x0085, // next line
+	0x00a0, // unknown
+	0x1680, // Ogham space mark
+	0x2000, // en dash .. zero-width space
+	0x200e, 0x200f, // LTR mark .. RTL mark (pattern whitespace)
+	0x2028, 0x2029,  0x3000,  0x3000,
+	0x202f, // narrow no-break space
+	0x205f, // medium mathematical space
+	0x3000, // ideographic space
+	0xfeff, // unknown
+};
+
+@(static)
+to_upper_ranges := [?]i32{
+	0x0061,  0x007a, 468, // a-z A-Z
+	0x00e0,  0x00f6, 468,
+	0x00f8,  0x00fe, 468,
+	0x0256,  0x0257, 295,
+	0x0258,  0x0259, 298,
+	0x028a,  0x028b, 283,
+	0x03ad,  0x03af, 463,
+	0x03b1,  0x03c1, 468,
+	0x03c3,  0x03cb, 468,
+	0x03cd,  0x03ce, 437,
+	0x0430,  0x044f, 468,
+	0x0451,  0x045c, 420,
+	0x045e,  0x045f, 420,
+	0x0561,  0x0586, 452,
+	0x1f00,  0x1f07, 508,
+	0x1f10,  0x1f15, 508,
+	0x1f20,  0x1f27, 508,
+	0x1f30,  0x1f37, 508,
+	0x1f40,  0x1f45, 508,
+	0x1f60,  0x1f67, 508,
+	0x1f70,  0x1f71, 574,
+	0x1f72,  0x1f75, 586,
+	0x1f76,  0x1f77, 600,
+	0x1f78,  0x1f79, 628,
+	0x1f7a,  0x1f7b, 612,
+	0x1f7c,  0x1f7d, 626,
+	0x1f80,  0x1f87, 508,
+	0x1f90,  0x1f97, 508,
+	0x1fa0,  0x1fa7, 508,
+	0x1fb0,  0x1fb1, 508,
+	0x1fd0,  0x1fd1, 508,
+	0x1fe0,  0x1fe1, 508,
+	0x2170,  0x217f, 484,
+	0x24d0,  0x24e9, 474,
+	0xff41,  0xff5a, 468,
+};
+
+@(static)
+to_upper_singlets := [?]i32{
+	0x00ff, 621,
+	0x0101, 499,
+	0x0103, 499,
+	0x0105, 499,
+	0x0107, 499,
+	0x0109, 499,
+	0x010b, 499,
+	0x010d, 499,
+	0x010f, 499,
+	0x0111, 499,
+	0x0113, 499,
+	0x0115, 499,
+	0x0117, 499,
+	0x0119, 499,
+	0x011b, 499,
+	0x011d, 499,
+	0x011f, 499,
+	0x0121, 499,
+	0x0123, 499,
+	0x0125, 499,
+	0x0127, 499,
+	0x0129, 499,
+	0x012b, 499,
+	0x012d, 499,
+	0x012f, 499,
+	0x0131, 268, //  I
+	0x0133, 499,
+	0x0135, 499,
+	0x0137, 499,
+	0x013a, 499,
+	0x013c, 499,
+	0x013e, 499,
+	0x0140, 499,
+	0x0142, 499,
+	0x0144, 499,
+	0x0146, 499,
+	0x0148, 499,
+	0x014b, 499,
+	0x014d, 499,
+	0x014f, 499,
+	0x0151, 499,
+	0x0153, 499,
+	0x0155, 499,
+	0x0157, 499,
+	0x0159, 499,
+	0x015b, 499,
+	0x015d, 499,
+	0x015f, 499,
+	0x0161, 499,
+	0x0163, 499,
+	0x0165, 499,
+	0x0167, 499,
+	0x0169, 499,
+	0x016b, 499,
+	0x016d, 499,
+	0x016f, 499,
+	0x0171, 499,
+	0x0173, 499,
+	0x0175, 499,
+	0x0177, 499,
+	0x017a, 499,
+	0x017c, 499,
+	0x017e, 499,
+	0x017f, 200, //  S
+	0x0183, 499,
+	0x0185, 499,
+	0x0188, 499,
+	0x018c, 499,
+	0x0192, 499,
+	0x0199, 499,
+	0x01a1, 499,
+	0x01a3, 499,
+	0x01a5, 499,
+	0x01a8, 499,
+	0x01ad, 499,
+	0x01b0, 499,
+	0x01b4, 499,
+	0x01b6, 499,
+	0x01b9, 499,
+	0x01bd, 499,
+	0x01c5, 499,
+	0x01c6, 498,
+	0x01c8, 499,
+	0x01c9, 498,
+	0x01cb, 499,
+	0x01cc, 498,
+	0x01ce, 499,
+	0x01d0, 499,
+	0x01d2, 499,
+	0x01d4, 499,
+	0x01d6, 499,
+	0x01d8, 499,
+	0x01da, 499,
+	0x01dc, 499,
+	0x01df, 499,
+	0x01e1, 499,
+	0x01e3, 499,
+	0x01e5, 499,
+	0x01e7, 499,
+	0x01e9, 499,
+	0x01eb, 499,
+	0x01ed, 499,
+	0x01ef, 499,
+	0x01f2, 499,
+	0x01f3, 498,
+	0x01f5, 499,
+	0x01fb, 499,
+	0x01fd, 499,
+	0x01ff, 499,
+	0x0201, 499,
+	0x0203, 499,
+	0x0205, 499,
+	0x0207, 499,
+	0x0209, 499,
+	0x020b, 499,
+	0x020d, 499,
+	0x020f, 499,
+	0x0211, 499,
+	0x0213, 499,
+	0x0215, 499,
+	0x0217, 499,
+	0x0253, 290,
+	0x0254, 294,
+	0x025b, 297,
+	0x0260, 295,
+	0x0263, 293,
+	0x0268, 291,
+	0x0269, 289,
+	0x026f, 289,
+	0x0272, 287,
+	0x0283, 282,
+	0x0288, 282,
+	0x0292, 281,
+	0x03ac, 462,
+	0x03cc, 436,
+	0x03d0, 438,
+	0x03d1, 443,
+	0x03d5, 453,
+	0x03d6, 446,
+	0x03e3, 499,
+	0x03e5, 499,
+	0x03e7, 499,
+	0x03e9, 499,
+	0x03eb, 499,
+	0x03ed, 499,
+	0x03ef, 499,
+	0x03f0, 414,
+	0x03f1, 420,
+	0x0461, 499,
+	0x0463, 499,
+	0x0465, 499,
+	0x0467, 499,
+	0x0469, 499,
+	0x046b, 499,
+	0x046d, 499,
+	0x046f, 499,
+	0x0471, 499,
+	0x0473, 499,
+	0x0475, 499,
+	0x0477, 499,
+	0x0479, 499,
+	0x047b, 499,
+	0x047d, 499,
+	0x047f, 499,
+	0x0481, 499,
+	0x0491, 499,
+	0x0493, 499,
+	0x0495, 499,
+	0x0497, 499,
+	0x0499, 499,
+	0x049b, 499,
+	0x049d, 499,
+	0x049f, 499,
+	0x04a1, 499,
+	0x04a3, 499,
+	0x04a5, 499,
+	0x04a7, 499,
+	0x04a9, 499,
+	0x04ab, 499,
+	0x04ad, 499,
+	0x04af, 499,
+	0x04b1, 499,
+	0x04b3, 499,
+	0x04b5, 499,
+	0x04b7, 499,
+	0x04b9, 499,
+	0x04bb, 499,
+	0x04bd, 499,
+	0x04bf, 499,
+	0x04c2, 499,
+	0x04c4, 499,
+	0x04c8, 499,
+	0x04cc, 499,
+	0x04d1, 499,
+	0x04d3, 499,
+	0x04d5, 499,
+	0x04d7, 499,
+	0x04d9, 499,
+	0x04db, 499,
+	0x04dd, 499,
+	0x04df, 499,
+	0x04e1, 499,
+	0x04e3, 499,
+	0x04e5, 499,
+	0x04e7, 499,
+	0x04e9, 499,
+	0x04eb, 499,
+	0x04ef, 499,
+	0x04f1, 499,
+	0x04f3, 499,
+	0x04f5, 499,
+	0x04f9, 499,
+	0x1e01, 499,
+	0x1e03, 499,
+	0x1e05, 499,
+	0x1e07, 499,
+	0x1e09, 499,
+	0x1e0b, 499,
+	0x1e0d, 499,
+	0x1e0f, 499,
+	0x1e11, 499,
+	0x1e13, 499,
+	0x1e15, 499,
+	0x1e17, 499,
+	0x1e19, 499,
+	0x1e1b, 499,
+	0x1e1d, 499,
+	0x1e1f, 499,
+	0x1e21, 499,
+	0x1e23, 499,
+	0x1e25, 499,
+	0x1e27, 499,
+	0x1e29, 499,
+	0x1e2b, 499,
+	0x1e2d, 499,
+	0x1e2f, 499,
+	0x1e31, 499,
+	0x1e33, 499,
+	0x1e35, 499,
+	0x1e37, 499,
+	0x1e39, 499,
+	0x1e3b, 499,
+	0x1e3d, 499,
+	0x1e3f, 499,
+	0x1e41, 499,
+	0x1e43, 499,
+	0x1e45, 499,
+	0x1e47, 499,
+	0x1e49, 499,
+	0x1e4b, 499,
+	0x1e4d, 499,
+	0x1e4f, 499,
+	0x1e51, 499,
+	0x1e53, 499,
+	0x1e55, 499,
+	0x1e57, 499,
+	0x1e59, 499,
+	0x1e5b, 499,
+	0x1e5d, 499,
+	0x1e5f, 499,
+	0x1e61, 499,
+	0x1e63, 499,
+	0x1e65, 499,
+	0x1e67, 499,
+	0x1e69, 499,
+	0x1e6b, 499,
+	0x1e6d, 499,
+	0x1e6f, 499,
+	0x1e71, 499,
+	0x1e73, 499,
+	0x1e75, 499,
+	0x1e77, 499,
+	0x1e79, 499,
+	0x1e7b, 499,
+	0x1e7d, 499,
+	0x1e7f, 499,
+	0x1e81, 499,
+	0x1e83, 499,
+	0x1e85, 499,
+	0x1e87, 499,
+	0x1e89, 499,
+	0x1e8b, 499,
+	0x1e8d, 499,
+	0x1e8f, 499,
+	0x1e91, 499,
+	0x1e93, 499,
+	0x1e95, 499,
+	0x1ea1, 499,
+	0x1ea3, 499,
+	0x1ea5, 499,
+	0x1ea7, 499,
+	0x1ea9, 499,
+	0x1eab, 499,
+	0x1ead, 499,
+	0x1eaf, 499,
+	0x1eb1, 499,
+	0x1eb3, 499,
+	0x1eb5, 499,
+	0x1eb7, 499,
+	0x1eb9, 499,
+	0x1ebb, 499,
+	0x1ebd, 499,
+	0x1ebf, 499,
+	0x1ec1, 499,
+	0x1ec3, 499,
+	0x1ec5, 499,
+	0x1ec7, 499,
+	0x1ec9, 499,
+	0x1ecb, 499,
+	0x1ecd, 499,
+	0x1ecf, 499,
+	0x1ed1, 499,
+	0x1ed3, 499,
+	0x1ed5, 499,
+	0x1ed7, 499,
+	0x1ed9, 499,
+	0x1edb, 499,
+	0x1edd, 499,
+	0x1edf, 499,
+	0x1ee1, 499,
+	0x1ee3, 499,
+	0x1ee5, 499,
+	0x1ee7, 499,
+	0x1ee9, 499,
+	0x1eeb, 499,
+	0x1eed, 499,
+	0x1eef, 499,
+	0x1ef1, 499,
+	0x1ef3, 499,
+	0x1ef5, 499,
+	0x1ef7, 499,
+	0x1ef9, 499,
+	0x1f51, 508,
+	0x1f53, 508,
+	0x1f55, 508,
+	0x1f57, 508,
+	0x1fb3, 509,
+	0x1fc3, 509,
+	0x1fe5, 507,
+	0x1ff3, 509,
+};
+
+@(static)
+to_lower_ranges := [?]i32{
+	0x0041,  0x005a, 532, // A-Z a-z
+	0x00c0,  0x00d6, 532, // - -
+	0x00d8,  0x00de, 532, // - -
+	0x0189,  0x018a, 705, // - -
+	0x018e,  0x018f, 702, // - -
+	0x01b1,  0x01b2, 717, // - -
+	0x0388,  0x038a, 537, // - -
+	0x038e,  0x038f, 563, // - -
+	0x0391,  0x03a1, 532, // - -
+	0x03a3,  0x03ab, 532, // - -
+	0x0401,  0x040c, 580, // - -
+	0x040e,  0x040f, 580, // - -
+	0x0410,  0x042f, 532, // - -
+	0x0531,  0x0556, 548, // - -
+	0x10a0,  0x10c5, 548, // - -
+	0x1f08,  0x1f0f, 492, // - -
+	0x1f18,  0x1f1d, 492, // - -
+	0x1f28,  0x1f2f, 492, // - -
+	0x1f38,  0x1f3f, 492, // - -
+	0x1f48,  0x1f4d, 492, // - -
+	0x1f68,  0x1f6f, 492, // - -
+	0x1f88,  0x1f8f, 492, // - -
+	0x1f98,  0x1f9f, 492, // - -
+	0x1fa8,  0x1faf, 492, // - -
+	0x1fb8,  0x1fb9, 492, // - -
+	0x1fba,  0x1fbb, 426, // - -
+	0x1fc8,  0x1fcb, 414, // - -
+	0x1fd8,  0x1fd9, 492, // - -
+	0x1fda,  0x1fdb, 400, // - -
+	0x1fe8,  0x1fe9, 492, // - -
+	0x1fea,  0x1feb, 388, // - -
+	0x1ff8,  0x1ff9, 372, // - -
+	0x1ffa,  0x1ffb, 374, // - -
+	0x2160,  0x216f, 516, // - -
+	0x24b6,  0x24cf, 526, // - -
+	0xff21,  0xff3a, 532, // - -
+};
+
+@(static)
+to_lower_singlets := [?]i32{
+	0x0100, 501,
+	0x0102, 501,
+	0x0104, 501,
+	0x0106, 501,
+	0x0108, 501,
+	0x010a, 501,
+	0x010c, 501,
+	0x010e, 501,
+	0x0110, 501,
+	0x0112, 501,
+	0x0114, 501,
+	0x0116, 501,
+	0x0118, 501,
+	0x011a, 501,
+	0x011c, 501,
+	0x011e, 501,
+	0x0120, 501,
+	0x0122, 501,
+	0x0124, 501,
+	0x0126, 501,
+	0x0128, 501,
+	0x012a, 501,
+	0x012c, 501,
+	0x012e, 501,
+	0x0130, 301, //  i
+	0x0132, 501,
+	0x0134, 501,
+	0x0136, 501,
+	0x0139, 501,
+	0x013b, 501,
+	0x013d, 501,
+	0x013f, 501,
+	0x0141, 501,
+	0x0143, 501,
+	0x0145, 501,
+	0x0147, 501,
+	0x014a, 501,
+	0x014c, 501,
+	0x014e, 501,
+	0x0150, 501,
+	0x0152, 501,
+	0x0154, 501,
+	0x0156, 501,
+	0x0158, 501,
+	0x015a, 501,
+	0x015c, 501,
+	0x015e, 501,
+	0x0160, 501,
+	0x0162, 501,
+	0x0164, 501,
+	0x0166, 501,
+	0x0168, 501,
+	0x016a, 501,
+	0x016c, 501,
+	0x016e, 501,
+	0x0170, 501,
+	0x0172, 501,
+	0x0174, 501,
+	0x0176, 501,
+	0x0178, 379,
+	0x0179, 501,
+	0x017b, 501,
+	0x017d, 501,
+	0x0181, 710,
+	0x0182, 501,
+	0x0184, 501,
+	0x0186, 706,
+	0x0187, 501,
+	0x018b, 501,
+	0x0190, 703,
+	0x0191, 501,
+	0x0193, 705,
+	0x0194, 707,
+	0x0196, 711,
+	0x0197, 709,
+	0x0198, 501,
+	0x019c, 711,
+	0x019d, 713,
+	0x01a0, 501,
+	0x01a2, 501,
+	0x01a4, 501,
+	0x01a7, 501,
+	0x01a9, 718,
+	0x01ac, 501,
+	0x01ae, 718,
+	0x01af, 501,
+	0x01b3, 501,
+	0x01b5, 501,
+	0x01b7, 719,
+	0x01b8, 501,
+	0x01bc, 501,
+	0x01c4, 502,
+	0x01c5, 501,
+	0x01c7, 502,
+	0x01c8, 501,
+	0x01ca, 502,
+	0x01cb, 501,
+	0x01cd, 501,
+	0x01cf, 501,
+	0x01d1, 501,
+	0x01d3, 501,
+	0x01d5, 501,
+	0x01d7, 501,
+	0x01d9, 501,
+	0x01db, 501,
+	0x01de, 501,
+	0x01e0, 501,
+	0x01e2, 501,
+	0x01e4, 501,
+	0x01e6, 501,
+	0x01e8, 501,
+	0x01ea, 501,
+	0x01ec, 501,
+	0x01ee, 501,
+	0x01f1, 502,
+	0x01f2, 501,
+	0x01f4, 501,
+	0x01fa, 501,
+	0x01fc, 501,
+	0x01fe, 501,
+	0x0200, 501,
+	0x0202, 501,
+	0x0204, 501,
+	0x0206, 501,
+	0x0208, 501,
+	0x020a, 501,
+	0x020c, 501,
+	0x020e, 501,
+	0x0210, 501,
+	0x0212, 501,
+	0x0214, 501,
+	0x0216, 501,
+	0x0386, 538,
+	0x038c, 564,
+	0x03e2, 501,
+	0x03e4, 501,
+	0x03e6, 501,
+	0x03e8, 501,
+	0x03ea, 501,
+	0x03ec, 501,
+	0x03ee, 501,
+	0x0460, 501,
+	0x0462, 501,
+	0x0464, 501,
+	0x0466, 501,
+	0x0468, 501,
+	0x046a, 501,
+	0x046c, 501,
+	0x046e, 501,
+	0x0470, 501,
+	0x0472, 501,
+	0x0474, 501,
+	0x0476, 501,
+	0x0478, 501,
+	0x047a, 501,
+	0x047c, 501,
+	0x047e, 501,
+	0x0480, 501,
+	0x0490, 501,
+	0x0492, 501,
+	0x0494, 501,
+	0x0496, 501,
+	0x0498, 501,
+	0x049a, 501,
+	0x049c, 501,
+	0x049e, 501,
+	0x04a0, 501,
+	0x04a2, 501,
+	0x04a4, 501,
+	0x04a6, 501,
+	0x04a8, 501,
+	0x04aa, 501,
+	0x04ac, 501,
+	0x04ae, 501,
+	0x04b0, 501,
+	0x04b2, 501,
+	0x04b4, 501,
+	0x04b6, 501,
+	0x04b8, 501,
+	0x04ba, 501,
+	0x04bc, 501,
+	0x04be, 501,
+	0x04c1, 501,
+	0x04c3, 501,
+	0x04c7, 501,
+	0x04cb, 501,
+	0x04d0, 501,
+	0x04d2, 501,
+	0x04d4, 501,
+	0x04d6, 501,
+	0x04d8, 501,
+	0x04da, 501,
+	0x04dc, 501,
+	0x04de, 501,
+	0x04e0, 501,
+	0x04e2, 501,
+	0x04e4, 501,
+	0x04e6, 501,
+	0x04e8, 501,
+	0x04ea, 501,
+	0x04ee, 501,
+	0x04f0, 501,
+	0x04f2, 501,
+	0x04f4, 501,
+	0x04f8, 501,
+	0x1e00, 501,
+	0x1e02, 501,
+	0x1e04, 501,
+	0x1e06, 501,
+	0x1e08, 501,
+	0x1e0a, 501,
+	0x1e0c, 501,
+	0x1e0e, 501,
+	0x1e10, 501,
+	0x1e12, 501,
+	0x1e14, 501,
+	0x1e16, 501,
+	0x1e18, 501,
+	0x1e1a, 501,
+	0x1e1c, 501,
+	0x1e1e, 501,
+	0x1e20, 501,
+	0x1e22, 501,
+	0x1e24, 501,
+	0x1e26, 501,
+	0x1e28, 501,
+	0x1e2a, 501,
+	0x1e2c, 501,
+	0x1e2e, 501,
+	0x1e30, 501,
+	0x1e32, 501,
+	0x1e34, 501,
+	0x1e36, 501,
+	0x1e38, 501,
+	0x1e3a, 501,
+	0x1e3c, 501,
+	0x1e3e, 501,
+	0x1e40, 501,
+	0x1e42, 501,
+	0x1e44, 501,
+	0x1e46, 501,
+	0x1e48, 501,
+	0x1e4a, 501,
+	0x1e4c, 501,
+	0x1e4e, 501,
+	0x1e50, 501,
+	0x1e52, 501,
+	0x1e54, 501,
+	0x1e56, 501,
+	0x1e58, 501,
+	0x1e5a, 501,
+	0x1e5c, 501,
+	0x1e5e, 501,
+	0x1e60, 501,
+	0x1e62, 501,
+	0x1e64, 501,
+	0x1e66, 501,
+	0x1e68, 501,
+	0x1e6a, 501,
+	0x1e6c, 501,
+	0x1e6e, 501,
+	0x1e70, 501,
+	0x1e72, 501,
+	0x1e74, 501,
+	0x1e76, 501,
+	0x1e78, 501,
+	0x1e7a, 501,
+	0x1e7c, 501,
+	0x1e7e, 501,
+	0x1e80, 501,
+	0x1e82, 501,
+	0x1e84, 501,
+	0x1e86, 501,
+	0x1e88, 501,
+	0x1e8a, 501,
+	0x1e8c, 501,
+	0x1e8e, 501,
+	0x1e90, 501,
+	0x1e92, 501,
+	0x1e94, 501,
+	0x1ea0, 501,
+	0x1ea2, 501,
+	0x1ea4, 501,
+	0x1ea6, 501,
+	0x1ea8, 501,
+	0x1eaa, 501,
+	0x1eac, 501,
+	0x1eae, 501,
+	0x1eb0, 501,
+	0x1eb2, 501,
+	0x1eb4, 501,
+	0x1eb6, 501,
+	0x1eb8, 501,
+	0x1eba, 501,
+	0x1ebc, 501,
+	0x1ebe, 501,
+	0x1ec0, 501,
+	0x1ec2, 501,
+	0x1ec4, 501,
+	0x1ec6, 501,
+	0x1ec8, 501,
+	0x1eca, 501,
+	0x1ecc, 501,
+	0x1ece, 501,
+	0x1ed0, 501,
+	0x1ed2, 501,
+	0x1ed4, 501,
+	0x1ed6, 501,
+	0x1ed8, 501,
+	0x1eda, 501,
+	0x1edc, 501,
+	0x1ede, 501,
+	0x1ee0, 501,
+	0x1ee2, 501,
+	0x1ee4, 501,
+	0x1ee6, 501,
+	0x1ee8, 501,
+	0x1eea, 501,
+	0x1eec, 501,
+	0x1eee, 501,
+	0x1ef0, 501,
+	0x1ef2, 501,
+	0x1ef4, 501,
+	0x1ef6, 501,
+	0x1ef8, 501,
+	0x1f59, 492,
+	0x1f5b, 492,
+	0x1f5d, 492,
+	0x1f5f, 492,
+	0x1fbc, 491,
+	0x1fcc, 491,
+	0x1fec, 493,
+	0x1ffc, 491,
+};
+
+@(static)
+to_title_singlets := [?]i32{
+	0x01c4, 501,
+	0x01c6, 499,
+	0x01c7, 501,
+	0x01c9, 499,
+	0x01ca, 501,
+	0x01cc, 499,
+	0x01f1, 501,
+	0x01f3, 499,
+};

+ 41 - 0
core/unicode/utf8/utf8.odin

@@ -350,3 +350,44 @@ rune_size :: proc(r: rune) -> int {
 	}
 	return -1;
 }
+
+// full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not
+// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
+full_rune :: proc(b: []byte) -> bool {
+	n := len(b);
+	if n == 0 {
+		return false;
+	}
+	x := _first[b[0]];
+	if n >= int(x & 7) {
+		return true;
+	}
+	accept := accept_ranges[x>>4];
+	if n > 1 && (b[1] < accept.lo || accept.hi < b[1]) {
+		return true;
+	} else if n > 2 && (b[2] < LOCB || HICB < b[2]) {
+		return true;
+	}
+	return false;
+}
+
+// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not
+// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
+full_rune_in_string :: proc(s: string) -> bool {
+	return full_rune(transmute([]byte)s);
+}
+
+
+_first := [256]u8{
+	0x00..0x7f = 0xf0, // ascii,    size 1
+	0x80..0xc1 = 0xf1, // invalid,  size 1
+	0xc2..0xdf = 0x02, // accept 1, size 2
+	0xe0       = 0x13, // accept 1, size 3
+	0xe1..0xec = 0x03, // accept 0, size 3
+	0xed       = 0x23, // accept 2, size 3
+	0xee..0xef = 0x03, // accept 0, size 3
+	0xf0       = 0x34, // accept 3, size 4
+	0xf1..0xf3 = 0x04, // accept 0, size 4
+	0xf4       = 0x44, // accept 4, size 4
+	0xf5..0xff = 0xf1, // ascii,    size 1
+};

+ 3 - 3
examples/demo/demo.odin

@@ -75,7 +75,7 @@ the_basics :: proc() {
 		// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
 		// literals 0x. A leading zero does not produce an octal constant (unlike C).
 
-		// In Odin, if a number constant is possible to be represented by a type without
+		// In Odin, if a numeric constant can be represented by a type without
 		// precision loss, it will automatically convert to that type.
 
 		x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
@@ -85,7 +85,7 @@ the_basics :: proc() {
 		y = 1;  // `1` is an untyped integer literal which can implicitly convert to `int`
 
 		z: f64; // `z` is typed of type `f64` (64-bit floating point number)
-		z = 1;  // `1` is an untyped integer literals which can be implicity conver to `f64`
+		z = 1;  // `1` is an untyped integer literal which can be implicitly converted to `f64`
 				// No need for any suffixes or decimal places like in other languages
 				// CONSTANTS JUST WORK!!!
 
@@ -150,7 +150,7 @@ control_flow :: proc() {
 			i += 1;
 		}
 
-		// If the condition is omitted, this produces an infinite loop:
+		// If the condition is omitted, an infinite loop is produced:
 		for {
 			break;
 		}

+ 87 - 2
src/array.cpp

@@ -43,11 +43,96 @@ template <typename T> void     array_set_capacity  (Array<T> *array, isize capac
 template <typename T> Array<T> array_slice         (Array<T> const &array, isize lo, isize hi);
 template <typename T> Array<T> array_clone         (gbAllocator const &a, Array<T> const &array);
 
-
-
 template <typename T> void array_ordered_remove  (Array<T> *array, isize index);
 template <typename T> void array_unordered_remove(Array<T> *array, isize index);
 
+template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset);
+template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count);
+
+template <typename T> T *array_end_ptr(Array<T> *array);
+
+
+template <typename T>
+struct Slice {
+	T *data;
+	isize count;
+
+	T &operator[](isize index) {
+		#if !defined(NO_ARRAY_BOUNDS_CHECK)
+			GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+		#endif
+		return data[index];
+	}
+
+	T const &operator[](isize index) const {
+		#if !defined(NO_ARRAY_BOUNDS_CHECK)
+			GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+		#endif
+		return data[index];
+	}
+};
+
+template <typename T> Slice<T> slice_from_array(Array<T> const &a);
+
+
+
+template <typename T>
+Slice<T> slice_make(gbAllocator const &allocator, isize count) {
+	Slice<T> s = {};
+	s.data = gb_alloc_array(allocator, T, count);
+	s.count = count;
+	return s;
+}
+
+
+template <typename T>
+Slice<T> slice_from_array(Array<T> const &a) {
+	return {a.data, a.count};
+}
+template <typename T>
+Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
+	T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T));
+	return {data, a.count};
+}
+
+template <typename T>
+Slice<T> slice_clone_from_array(gbAllocator const &allocator, Array<T> const &a) {
+	auto c = array_clone(allocator, a);
+	return {c.data, c.count};
+}
+
+
+template <typename T>
+void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset) {
+	gb_memmove(slice->data+offset, data.data, gb_size_of(T)*data.count);
+}
+template <typename T>
+void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset, isize count) {
+	gb_memmove(slice->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count));
+}
+
+
+
+template <typename T>
+void slice_ordered_remove(Slice<T> *array, isize index) {
+	GB_ASSERT(0 <= index && index < array->count);
+
+	isize bytes = gb_size_of(T) * (array->count-(index+1));
+	gb_memmove(array->data+index, array->data+index+1, bytes);
+	array->count -= 1;
+}
+
+template <typename T>
+void slice_unordered_remove(Slice<T> *array, isize index) {
+	GB_ASSERT(0 <= index && index < array->count);
+
+	isize n = array->count-1;
+	if (index != n) {
+		gb_memmove(array->data+index, array->data+n, gb_size_of(T));
+	}
+	array->count -= 1;
+}
+
 
 template <typename T>
 void array_copy(Array<T> *array, Array<T> const &data, isize offset) {

+ 51 - 0
src/build_settings.cpp

@@ -104,6 +104,37 @@ enum BuildModeKind {
 	BuildMode_Assembly,
 };
 
+enum CommandKind : u32 {
+	Command_run     = 1<<0,
+	Command_build   = 1<<1,
+	Command_check   = 1<<3,
+	Command_query   = 1<<4,
+	Command_doc     = 1<<5,
+	Command_version = 1<<6,
+	Command_test     = 1<<7,
+
+	Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
+	Command__does_build = Command_run|Command_build|Command_test,
+	Command_all = ~(u32)0,
+};
+
+char const *odin_command_strings[32] = {
+	"run",
+	"build",
+	"check",
+	"query",
+	"doc",
+	"version",
+};
+
+
+
+enum CmdDocFlag : u32 {
+	CmdDocFlag_Short       = 1<<0,
+	CmdDocFlag_AllPackages = 1<<1,
+};
+
+
 
 // This stores the information for the specify architecture of this build
 struct BuildContext {
@@ -124,6 +155,7 @@ struct BuildContext {
 	i64    word_size; // Size of a pointer, must be >= 4
 	i64    max_align; // max alignment, must be >= 1 (and typically >= word_size)
 
+	CommandKind command_kind;
 	String command;
 
 	TargetMetrics metrics;
@@ -143,6 +175,8 @@ struct BuildContext {
 	bool   generate_docs;
 	i32    optimization_level;
 	bool   show_timings;
+	bool   show_unused;
+	bool   show_unused_with_location;
 	bool   show_more_timings;
 	bool   show_system_calls;
 	bool   keep_temp_files;
@@ -151,6 +185,7 @@ struct BuildContext {
 	bool   no_dynamic_literals;
 	bool   no_output_files;
 	bool   no_crt;
+	bool   no_entry_point;
 	bool   use_lld;
 	bool   vet;
 	bool   cross_compiling;
@@ -165,6 +200,9 @@ struct BuildContext {
 	bool   ignore_microsoft_magic;
 	bool   linker_map_file;
 
+	u32 cmd_doc_flags;
+	Array<String> extra_packages;
+
 	QueryDataSetSettings query_data_set_settings;
 
 	gbAffinity affinity;
@@ -297,6 +335,19 @@ bool is_excluded_target_filename(String name) {
 	String original_name = name;
 	name = remove_extension_from_path(name);
 
+	if (string_starts_with(name, str_lit("."))) {
+		// Ignore .*.odin files
+		return true;
+	}
+
+	String test_suffix = str_lit("_test");
+	if (build_context.command_kind != Command_test) {
+		if (string_ends_with(name, test_suffix) && name != test_suffix) {
+			// Ignore *_test.odin files
+			return true;
+		}
+	}
+
 	String str1 = {};
 	String str2 = {};
 	isize n = 0;

+ 5 - 8
src/check_decl.cpp

@@ -113,7 +113,7 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
 	return e->type;
 }
 
-void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Ast *> const &inits, String context_name) {
+void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice<Ast *> const &inits, String context_name) {
 	if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
 		return;
 	}
@@ -121,8 +121,7 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar
 
 	// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
 	// an extra allocation
-	auto operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
-	defer (array_free(&operands));
+	auto operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
 	check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
 
 	isize rhs_count = operands.count;
@@ -317,7 +316,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 				break;
 			default:
 				error(e->token, "Only struct types can have custom atom operations");
-				gb_free(heap_allocator(), ac.atom_op_table);
 				break;
 			}
 		}
@@ -638,7 +636,7 @@ String handle_link_name(CheckerContext *ctx, Token token, String link_name, Stri
 			error(token, "'link_name' and 'link_prefix' cannot be used together");
 		} else {
 			isize len = link_prefix.len + token.string.len;
-			u8 *name = gb_alloc_array(ctx->allocator, u8, len+1);
+			u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1);
 			gb_memmove(name, &link_prefix[0], link_prefix.len);
 			gb_memmove(name+link_prefix.len, &token.string[0], token.string.len);
 			name[len] = 0;
@@ -975,7 +973,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
 
 	ast_node(pg, ProcGroup, d->init_expr);
 
-	pge->entities = array_make<Entity*>(ctx->allocator, 0, pg->args.count);
+	pge->entities = array_make<Entity*>(permanent_allocator(), 0, pg->args.count);
 
 	// NOTE(bill): This must be set here to prevent cycles in checking if someone
 	// places the entity within itself
@@ -1009,11 +1007,10 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
 			continue;
 		}
 
-		if (ptr_set_exists(&entity_set, e)) {
+		if (ptr_set_update(&entity_set, e)) {
 			error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string));
 			continue;
 		}
-		ptr_set_add(&entity_set, e);
 		array_add(&pge->entities, e);
 	}
 

Fișier diff suprimat deoarece este prea mare
+ 290 - 129
src/check_expr.cpp


+ 14 - 21
src/check_stmt.cpp

@@ -15,7 +15,7 @@ bool is_divigering_stmt(Ast *stmt) {
 	return t->kind == Type_Proc && t->Proc.diverging;
 }
 
-void check_stmt_list(CheckerContext *ctx, Array<Ast *> const &stmts, u32 flags) {
+void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) {
 	if (stmts.count == 0) {
 		return;
 	}
@@ -78,7 +78,7 @@ void check_stmt_list(CheckerContext *ctx, Array<Ast *> const &stmts, u32 flags)
 	}
 }
 
-bool check_is_terminating_list(Array<Ast *> const &stmts, String const &label) {
+bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) {
 	// Iterate backwards
 	for (isize n = stmts.count-1; n >= 0; n--) {
 		Ast *stmt = stmts[n];
@@ -96,7 +96,7 @@ bool check_is_terminating_list(Array<Ast *> const &stmts, String const &label) {
 	return false;
 }
 
-bool check_has_break_list(Array<Ast *> const &stmts, String const &label, bool implicit) {
+bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) {
 	for_array(i, stmts) {
 		Ast *stmt = stmts[i];
 		if (check_has_break(stmt, label, implicit)) {
@@ -641,8 +641,7 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper
 	TypeAndToken *found = map_get(seen, key);
 	if (found != nullptr) {
 		isize count = multi_map_count(seen, key);
-		TypeAndToken *taps = gb_alloc_array(ctx->allocator, TypeAndToken, count);
-		defer (gb_free(ctx->allocator, taps));
+		TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
 
 		multi_map_get_all(seen, key, taps);
 		for (isize i = 0; i < count; i++) {
@@ -859,8 +858,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		token.pos    = ast_token(ss->body).pos;
 		token.string = str_lit("true");
 
-		x.expr = gb_alloc_item(ctx->allocator, Ast);
-		x.expr->kind = Ast_Ident;
+		x.expr = alloc_ast_node(nullptr, Ast_Ident);
 		x.expr->Ident.token = token;
 	}
 
@@ -1025,8 +1023,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		GB_ASSERT(is_type_enum(et));
 		auto fields = et->Enum.fields;
 
-		auto unhandled = array_make<Entity *>(ctx->allocator, 0, fields.count);
-		defer (array_free(&unhandled));
+		auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count);
 
 		for_array(i, fields) {
 			Entity *f = fields[i];
@@ -1265,8 +1262,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		GB_ASSERT(is_type_union(ut));
 		auto variants = ut->Union.variants;
 
-		auto unhandled = array_make<Type *>(ctx->allocator, 0, variants.count);
-		defer (array_free(&unhandled));
+		auto unhandled = array_make<Type *>(temporary_allocator(), 0, variants.count);
 
 		for_array(i, variants) {
 			Type *t = variants[i];
@@ -1433,12 +1429,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 				return;
 			}
 
+
 			// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
 			// an extra allocation
-			auto lhs_operands = array_make<Operand>(ctx->allocator, lhs_count);
-			auto rhs_operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
-			defer (array_free(&lhs_operands));
-			defer (array_free(&rhs_operands));
+			auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count);
+			auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
 
 			for_array(i, as->lhs) {
 				if (is_blank_ident(as->lhs[i])) {
@@ -1462,8 +1457,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 				}
 			}
 
-			auto lhs_to_ignore = array_make<bool>(ctx->allocator, lhs_count);
-			defer (array_free(&lhs_to_ignore));
+			auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
 
 			isize max = gb_min(lhs_count, rhs_count);
 			// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
@@ -1642,8 +1636,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 		} else if (operands.count != result_count) {
 			error(node, "Expected %td return values, got %td", result_count, operands.count);
 		} else {
-			isize max_count = rs->results.count;
-			for (isize i = 0; i < max_count; i++) {
+			for (isize i = 0; i < result_count; i++) {
 				Entity *e = pt->results->Tuple.variables[i];
 				check_assignment(ctx, &operands[i], e->type, str_lit("return statement"));
 			}
@@ -1878,7 +1871,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 			DeclInfo *d = decl_info_of_entity(e);
 			GB_ASSERT(d == nullptr);
 			add_entity(ctx->checker, ctx->scope, e->identifier, e);
-			d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl);
+			d = make_decl_info(ctx->scope, ctx->decl);
 			add_entity_and_decl_info(ctx, e->identifier, e, d);
 		}
 
@@ -2036,7 +2029,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 
 	case_ast_node(vd, ValueDecl, node);
 		if (vd->is_mutable) {
-			Entity **entities = gb_alloc_array(ctx->allocator, Entity *, vd->names.count);
+			Entity **entities = gb_alloc_array(permanent_allocator(), Entity *, vd->names.count);
 			isize entity_count = 0;
 
 			isize new_name_count = 0;

+ 196 - 84
src/check_type.cpp

@@ -1,3 +1,4 @@
+ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
 
 void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
 	t = base_type(t);
@@ -116,7 +117,7 @@ bool does_field_type_allow_using(Type *t) {
 	return false;
 }
 
-void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const &params,
+void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Slice<Ast *> const &params,
                          isize init_field_capacity, Type *struct_type, String context) {
 	*fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity);
 	*tags   = array_make<String>(heap_allocator(), 0, init_field_capacity);
@@ -388,7 +389,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 
 	if (st->polymorphic_params != nullptr) {
 		ast_node(field_list, FieldList, st->polymorphic_params);
-		Array<Ast *> params = field_list->list;
+		Slice<Ast *> params = field_list->list;
 		if (params.count != 0) {
 			isize variable_count = 0;
 			for_array(i, params) {
@@ -399,7 +400,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 				}
 			}
 
-			auto entities = array_make<Entity *>(ctx->allocator, 0, variable_count);
+			auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
 
 			for_array(i, params) {
 				Ast *param = params[i];
@@ -408,32 +409,50 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 				}
 				ast_node(p, Field, param);
 				Ast *type_expr = p->type;
+				Ast *default_value = unparen_expr(p->default_value);
 				Type *type = nullptr;
 				bool is_type_param = false;
 				bool is_type_polymorphic_type = false;
-				if (type_expr == nullptr) {
+				if (type_expr == nullptr && default_value == nullptr) {
 					error(param, "Expected a type for this parameter");
 					continue;
 				}
-				if (type_expr->kind == Ast_Ellipsis) {
-					type_expr = type_expr->Ellipsis.expr;
-					error(param, "A polymorphic parameter cannot be variadic");
+
+				if (type_expr != nullptr) {
+					if (type_expr->kind == Ast_Ellipsis) {
+						type_expr = type_expr->Ellipsis.expr;
+						error(param, "A polymorphic parameter cannot be variadic");
+					}
+					if (type_expr->kind == Ast_TypeidType) {
+						is_type_param = true;
+						Type *specialization = nullptr;
+						if (type_expr->TypeidType.specialization != nullptr) {
+							Ast *s = type_expr->TypeidType.specialization;
+							specialization = check_type(ctx, s);
+						}
+						type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
+					} else {
+						type = check_type(ctx, type_expr);
+						if (is_type_polymorphic(type)) {
+							is_type_polymorphic_type = true;
+						}
+					}
 				}
-				if (type_expr->kind == Ast_TypeidType) {
-					is_type_param = true;
-					Type *specialization = nullptr;
-					if (type_expr->TypeidType.specialization != nullptr) {
-						Ast *s = type_expr->TypeidType.specialization;
-						specialization = check_type(ctx, s);
+
+				ParameterValue param_value = {};
+				if (default_value != nullptr)  {
+					Type *out_type = nullptr;
+					param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
+					if (type == nullptr && out_type != nullptr) {
+						type = out_type;
 					}
-					type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
-				} else {
-					type = check_type(ctx, type_expr);
-					if (is_type_polymorphic(type)) {
-						is_type_polymorphic_type = true;
+					if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
+						error(default_value, "Invalid parameter value");
+						param_value = {};
 					}
 				}
 
+
 				if (type == nullptr) {
 					error(params[i], "Invalid parameter type");
 					type = t_invalid;
@@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 					Token token = name->Ident.token;
 
 					if (poly_operands != nullptr) {
-						Operand operand = (*poly_operands)[entities.count];
+						Operand operand = {};
+						operand.type = t_invalid;
+						if (entities.count < poly_operands->count) {
+							operand = (*poly_operands)[entities.count];
+						} else if (param_value.kind != ParameterValue_Invalid) {
+							operand.mode = Addressing_Constant;
+							operand.value = param_value.value;
+						}
 						if (is_type_param) {
 							if (is_type_polymorphic(base_type(operand.type))) {
 								is_polymorphic = true;
@@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 							}
 							if (e == nullptr) {
 								e = alloc_entity_constant(scope, token, operand.type, operand.value);
+								e->Constant.param_value = param_value;
 							}
 						}
 					} else {
@@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 							e = alloc_entity_type_name(scope, token, type);
 							e->TypeName.is_type_alias = true;
 						} else {
-							e = alloc_entity_constant(scope, token, type, empty_exact_value);
+							e = alloc_entity_constant(scope, token, type, param_value.value);
+							e->Constant.param_value = param_value;
 						}
 					}
 
@@ -568,7 +596,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 
 	Entity *using_index_expr = nullptr;
 
-	auto variants = array_make<Type *>(ctx->allocator, 0, variant_count);
+	auto variants = array_make<Type *>(permanent_allocator(), 0, variant_count);
 
 	union_type->Union.scope = ctx->scope;
 
@@ -579,7 +607,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 
 	if (ut->polymorphic_params != nullptr) {
 		ast_node(field_list, FieldList, ut->polymorphic_params);
-		Array<Ast *> params = field_list->list;
+		Slice<Ast *> params = field_list->list;
 		if (params.count != 0) {
 			isize variable_count = 0;
 			for_array(i, params) {
@@ -590,7 +618,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 				}
 			}
 
-			auto entities = array_make<Entity *>(ctx->allocator, 0, variable_count);
+			auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
 
 			for_array(i, params) {
 				Ast *param = params[i];
@@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 				}
 				ast_node(p, Field, param);
 				Ast *type_expr = p->type;
+				Ast *default_value = unparen_expr(p->default_value);
 				Type *type = nullptr;
 				bool is_type_param = false;
 				bool is_type_polymorphic_type = false;
-				if (type_expr == nullptr) {
+				if (type_expr == nullptr && default_value == nullptr) {
 					error(param, "Expected a type for this parameter");
 					continue;
 				}
-				if (type_expr->kind == Ast_Ellipsis) {
-					type_expr = type_expr->Ellipsis.expr;
-					error(param, "A polymorphic parameter cannot be variadic");
+				if (type_expr != nullptr) {
+					if (type_expr->kind == Ast_Ellipsis) {
+						type_expr = type_expr->Ellipsis.expr;
+						error(param, "A polymorphic parameter cannot be variadic");
+					}
+					if (type_expr->kind == Ast_TypeidType) {
+						is_type_param = true;
+						Type *specialization = nullptr;
+						if (type_expr->TypeidType.specialization != nullptr) {
+							Ast *s = type_expr->TypeidType.specialization;
+							specialization = check_type(ctx, s);
+						}
+						type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
+					} else {
+						type = check_type(ctx, type_expr);
+						if (is_type_polymorphic(type)) {
+							is_type_polymorphic_type = true;
+						}
+					}
 				}
-				if (type_expr->kind == Ast_TypeidType) {
-					is_type_param = true;
-					Type *specialization = nullptr;
-					if (type_expr->TypeidType.specialization != nullptr) {
-						Ast *s = type_expr->TypeidType.specialization;
-						specialization = check_type(ctx, s);
+
+				ParameterValue param_value = {};
+				if (default_value != nullptr)  {
+					Type *out_type = nullptr;
+					param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
+					if (type == nullptr && out_type != nullptr) {
+						type = out_type;
 					}
-					type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
-				} else {
-					type = check_type(ctx, type_expr);
-					if (is_type_polymorphic(type)) {
-						is_type_polymorphic_type = true;
+					if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
+						error(default_value, "Invalid parameter value");
+						param_value = {};
 					}
 				}
 
@@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 					Token token = name->Ident.token;
 
 					if (poly_operands != nullptr) {
-						Operand operand = (*poly_operands)[entities.count];
+						Operand operand = {};
+						operand.type = t_invalid;
+						if (entities.count < poly_operands->count) {
+							operand = (*poly_operands)[entities.count];
+						} else if (param_value.kind != ParameterValue_Invalid) {
+							operand.mode = Addressing_Constant;
+							operand.value = param_value.value;
+						}
 						if (is_type_param) {
 							GB_ASSERT(operand.mode == Addressing_Type ||
 							          operand.mode == Addressing_Invalid);
@@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 						} else {
 							// GB_ASSERT(operand.mode == Addressing_Constant);
 							e = alloc_entity_constant(scope, token, operand.type, operand.value);
+							e->Constant.param_value = param_value;
 						}
 					} else {
 						if (is_type_param) {
@@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 							e->TypeName.is_type_alias = true;
 						} else {
 							e = alloc_entity_constant(scope, token, type, empty_exact_value);
+							e->Constant.param_value = param_value;
 						}
 					}
 
@@ -816,7 +869,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
 	enum_type->Enum.base_type = base_type;
 	enum_type->Enum.scope = ctx->scope;
 
-	auto fields = array_make<Entity *>(ctx->allocator, 0, et->fields.count);
+	auto fields = array_make<Entity *>(permanent_allocator(), 0, et->fields.count);
 
 	Type *constant_type = enum_type;
 	if (named_type != nullptr) {
@@ -933,9 +986,9 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node)
 	ast_node(bft, BitFieldType, node);
 	GB_ASSERT(is_type_bit_field(bit_field_type));
 
-	auto fields  = array_make<Entity*>(ctx->allocator, 0, bft->fields.count);
-	auto sizes   = array_make<u32>    (ctx->allocator, 0, bft->fields.count);
-	auto offsets = array_make<u32>    (ctx->allocator, 0, bft->fields.count);
+	auto fields  = array_make<Entity*>(permanent_allocator(), 0, bft->fields.count);
+	auto sizes   = array_make<u32>    (permanent_allocator(), 0, bft->fields.count);
+	auto offsets = array_make<u32>    (permanent_allocator(), 0, bft->fields.count);
 
 	scope_reserve(ctx->scope, bft->fields.count);
 
@@ -1337,7 +1390,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper
 
 	if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) {
 		if (show_error) {
-			set_procedure_abi_types(ctx->allocator, poly_type);
+			set_procedure_abi_types(poly_type);
 		}
 		return poly_type;
 	}
@@ -1463,7 +1516,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 
 	bool success = true;
 	ast_node(field_list, FieldList, _params);
-	Array<Ast *> params = field_list->list;
+	Slice<Ast *> params = field_list->list;
 
 	if (params.count == 0) {
 		if (success_) *success_ = success;
@@ -1496,7 +1549,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 	bool is_variadic = false;
 	isize variadic_index = -1;
 	bool is_c_vararg = false;
-	auto variables = array_make<Entity *>(ctx->allocator, 0, variable_count);
+	auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
 	for_array(i, params) {
 		Ast *param = params[i];
 		if (param->kind != Ast_Field) {
@@ -1822,7 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
 		return nullptr;
 	}
 	ast_node(field_list, FieldList, _results);
-	Array<Ast *> results = field_list->list;
+	Slice<Ast *> results = field_list->list;
 
 	if (results.count == 0) {
 		return nullptr;
@@ -1838,7 +1891,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
 		}
 	}
 
-	auto variables = array_make<Entity *>(ctx->allocator, 0, variable_count);
+	auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
 	for_array(i, results) {
 		ast_node(field, Field, results[i]);
 		Ast *default_value = unparen_expr(field->default_value);
@@ -2209,6 +2262,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
 		return new_type;
 	}
 
+	if (is_type_proc(original_type)) {
+		// NOTE(bill): Force a cast to prevent a possible type cycle
+		return t_rawptr;
+	}
+
 	if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) {
 		return new_type;
 	}
@@ -2221,7 +2279,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
 		return new_type;
 	}
 	if (build_context.ODIN_ARCH == "amd64") {
-		if (is_type_integer_128bit(original_type)) {
+		bool is_128 = is_type_integer_128bit(original_type);
+		if (!is_128 && is_type_bit_set(original_type) && type_size_of(original_type) == 16) {
+			// is_128 = true;
+		}
+		if (is_128) {
 			if (build_context.ODIN_OS == "windows") {
 				return alloc_type_simd_vector(2, t_u64);
 			} else {
@@ -2332,6 +2394,11 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
 		return new_type;
 	}
 
+	if (is_type_proc(single_type)) {
+		// NOTE(bill): Force a cast to prevent a possible type cycle
+		return t_rawptr;
+	}
+
 	if (is_type_simd_vector(single_type)) {
 		return new_type;
 	}
@@ -2445,16 +2512,21 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type
 	return false;
 }
 
-void set_procedure_abi_types(gbAllocator allocator, Type *type) {
+void set_procedure_abi_types(Type *type) {
 	type = base_type(type);
 	if (type->kind != Type_Proc) {
 		return;
 	}
 
-	if (type->Proc.abi_types_set) {
+	if (type->Proc.abi_types_set || type->flags & TypeFlag_InProcessOfCheckingABI) {
 		return;
 	}
 
+	gbAllocator allocator = permanent_allocator();
+
+	u32 flags = type->flags;
+	type->flags |= TypeFlag_InProcessOfCheckingABI;
+
 	type->Proc.abi_compat_params = array_make<Type *>(allocator, cast(isize)type->Proc.param_count);
 	for (i32 i = 0; i < type->Proc.param_count; i++) {
 		Entity *e = type->Proc.params->Tuple.variables[i];
@@ -2466,7 +2538,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
 			case ProcCC_Odin:
 			case ProcCC_Contextless:
 			case ProcCC_Pure:
-				if (is_type_pointer(new_type) & !is_type_pointer(e->type)) {
+				if (is_type_pointer(new_type) && !is_type_pointer(e->type) && !is_type_proc(e->type)) {
 					e->flags |= EntityFlag_ImplicitReference;
 				}
 				break;
@@ -2474,7 +2546,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
 
 			if (build_context.ODIN_OS == "linux" ||
 			    build_context.ODIN_OS == "darwin") {
-				if (is_type_pointer(new_type) & !is_type_pointer(e->type)) {
+				if (is_type_pointer(new_type) & !is_type_pointer(e->type) && !is_type_proc(e->type)) {
 					e->flags |= EntityFlag_ByVal;
 				}
 			}
@@ -2484,13 +2556,13 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
 	for (i32 i = 0; i < type->Proc.param_count; i++) {
 		Entity *e = type->Proc.params->Tuple.variables[i];
 		if (e->kind == Entity_Variable) {
-			set_procedure_abi_types(allocator, e->type);
+			set_procedure_abi_types(e->type);
 		}
 	}
 	for (i32 i = 0; i < type->Proc.result_count; i++) {
 		Entity *e = type->Proc.results->Tuple.variables[i];
 		if (e->kind == Entity_Variable) {
-			set_procedure_abi_types(allocator, e->type);
+			set_procedure_abi_types(e->type);
 		}
 	}
 
@@ -2499,6 +2571,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
 	type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type);
 
 	type->Proc.abi_types_set = true;
+	type->flags = flags;
 }
 
 // NOTE(bill): 'operands' is for generating non generic procedure type
@@ -2711,30 +2784,29 @@ void init_map_entry_type(Type *type) {
 	if (type->Map.entry_type != nullptr) return;
 
 	// NOTE(bill): The preload types may have not been set yet
-	GB_ASSERT(t_map_key != nullptr);
-	gbAllocator a = heap_allocator();
+	GB_ASSERT(t_map_hash != nullptr);
 	Type *entry_type = alloc_type_struct();
 
 	/*
 	struct {
-		hash:  __MapKey;
-		next:  int;
-		key:   Key;
-		value: Value;
+		hash:  runtime.Map_Hash,
+		next:  int,
+		key:   Key,
+		value: Value,
 	}
 	*/
 	Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
-	Scope *s = create_scope(builtin_pkg->scope, a);
+	Scope *s = create_scope(builtin_pkg->scope);
 
-	auto fields = array_make<Entity *>(a, 0, 3);
-	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")),   t_map_key,       false, 0, EntityState_Resolved));
-	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")),  t_int,           false, 1, EntityState_Resolved));
-	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 2, EntityState_Resolved));
+	auto fields = array_make<Entity *>(permanent_allocator(), 0, 4);
+	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")),  t_uintptr,       false, cast(i32)fields.count, EntityState_Resolved));
+	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")),  t_int,           false, cast(i32)fields.count, EntityState_Resolved));
+	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")),   type->Map.key,   false, cast(i32)fields.count, EntityState_Resolved));
+	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved));
 
 
 	entry_type->Struct.fields = fields;
 
-	// type_set_offsets(a, entry_type);
 	type->Map.entry_type = entry_type;
 }
 
@@ -2757,15 +2829,14 @@ void init_map_internal_types(Type *type) {
 		entries: [dynamic]EntryType;
 	}
 	*/
-	gbAllocator a = heap_allocator();
 	Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
-	Scope *s = create_scope(builtin_pkg->scope, a);
+	Scope *s = create_scope(builtin_pkg->scope);
 
 	Type *hashes_type  = alloc_type_slice(t_int);
 	Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
 
 
-	auto fields = array_make<Entity *>(a, 0, 2);
+	auto fields = array_make<Entity *>(permanent_allocator(), 0, 2);
 	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hashes")),  hashes_type,  false, 0, EntityState_Resolved));
 	array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved));
 
@@ -2777,6 +2848,47 @@ void init_map_internal_types(Type *type) {
 	type->Map.lookup_result_type    = make_optional_ok_type(value);
 }
 
+void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
+	key = core_type(key);
+
+	if (is_type_cstring(key)) {
+		add_package_dependency(ctx, "runtime", "default_hasher_cstring");
+	} else if (is_type_string(key)) {
+		add_package_dependency(ctx, "runtime", "default_hasher_string");
+	} else if (!is_type_polymorphic(key)) {
+		if (!is_type_comparable(key)) {
+			return;
+		}
+
+		if (is_type_simple_compare(key)) {
+			i64 sz = type_size_of(key);
+			if (1 <= sz && sz <= 16) {
+				char buf[20] = {};
+				gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
+				add_package_dependency(ctx, "runtime", buf);
+				return;
+			} else {
+				add_package_dependency(ctx, "runtime", "default_hasher_n");
+				return;
+			}
+		}
+
+		if (key->kind == Type_Struct) {
+			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			for_array(i, key->Struct.fields) {
+				Entity *field = key->Struct.fields[i];
+				add_map_key_type_dependencies(ctx, field->type);
+			}
+		} else if (key->kind == Type_EnumeratedArray) {
+			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
+		} else if (key->kind == Type_Array) {
+			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			add_map_key_type_dependencies(ctx, key->Array.elem);
+		}
+	}
+}
+
 void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
 	GB_ASSERT(type->kind == Type_Map);
 	ast_node(mt, MapType, node);
@@ -2793,16 +2905,16 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
 			gb_string_free(str);
 		}
 	}
+	if (type_size_of(key) == 0) {
+		gbString str = type_to_string(key);
+		error(node, "Invalid type of a key for a map of size 0, got '%s'", str);
+		gb_string_free(str);
+	}
 
 	type->Map.key   = key;
 	type->Map.value = value;
 
-	if (is_type_string(key)) {
-		add_package_dependency(ctx, "runtime", "default_hash_string");
-	} else {
-		add_package_dependency(ctx, "runtime", "default_hash_ptr");
-	}
-
+	add_map_key_type_dependencies(ctx, key);
 
 	init_core_map_type(ctx->checker);
 	init_map_internal_types(type);
@@ -2833,7 +2945,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 		soa_struct->Struct.soa_elem = elem;
 		soa_struct->Struct.soa_count = count;
 
-		scope = create_scope(ctx->scope, ctx->allocator);
+		scope = create_scope(ctx->scope);
 		soa_struct->Struct.scope = scope;
 
 		String params_xyzw[4] = {
@@ -2866,7 +2978,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 		soa_struct->Struct.soa_elem = elem;
 		soa_struct->Struct.soa_count = count;
 
-		scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+		scope = create_scope(old_struct->Struct.scope->parent);
 		soa_struct->Struct.scope = scope;
 
 		for_array(i, old_struct->Struct.fields) {
@@ -2927,7 +3039,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 		soa_struct->Struct.soa_count = 0;
 		soa_struct->Struct.is_polymorphic = true;
 
-		scope = create_scope(ctx->scope, ctx->allocator);
+		scope = create_scope(ctx->scope);
 		soa_struct->Struct.scope = scope;
 	} else if (is_type_array(elem)) {
 		Type *old_array = base_type(elem);
@@ -2941,7 +3053,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 		soa_struct->Struct.soa_elem = elem;
 		soa_struct->Struct.soa_count = 0;
 
-		scope = create_scope(ctx->scope, ctx->allocator);
+		scope = create_scope(ctx->scope);
 		soa_struct->Struct.scope = scope;
 
 		String params_xyzw[4] = {
@@ -2977,7 +3089,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 		soa_struct->Struct.soa_elem = elem;
 		soa_struct->Struct.soa_count = 0;
 
-		scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+		scope = create_scope(old_struct->Struct.scope->parent);
 		soa_struct->Struct.scope = scope;
 
 		for_array(i, old_struct->Struct.fields) {
@@ -3044,7 +3156,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
 		soa_struct->Struct.soa_count = 0;
 		soa_struct->Struct.is_polymorphic = true;
 
-		scope = create_scope(ctx->scope, ctx->allocator);
+		scope = create_scope(ctx->scope);
 		soa_struct->Struct.scope = scope;
 	} else if (is_type_array(elem)) {
 		Type *old_array = base_type(elem);
@@ -3058,7 +3170,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
 		soa_struct->Struct.soa_elem = elem;
 		soa_struct->Struct.soa_count = 0;
 
-		scope = create_scope(ctx->scope, ctx->allocator);
+		scope = create_scope(ctx->scope);
 		soa_struct->Struct.scope = scope;
 
 		String params_xyzw[4] = {
@@ -3093,7 +3205,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
 		soa_struct->Struct.soa_elem = elem;
 		soa_struct->Struct.soa_count = 0;
 
-		scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+		scope = create_scope(old_struct->Struct.scope->parent);
 		soa_struct->Struct.scope = scope;
 
 		for_array(i, old_struct->Struct.fields) {

+ 188 - 79
src/checker.cpp

@@ -187,8 +187,8 @@ void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
 	array_init  (&d->labels,         heap_allocator());
 }
 
-DeclInfo *make_decl_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
-	DeclInfo *d = gb_alloc_item(a, DeclInfo);
+DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
+	DeclInfo *d = gb_alloc_item(permanent_allocator(), DeclInfo);
 	init_decl_info(d, scope, parent);
 	return d;
 }
@@ -219,8 +219,8 @@ bool decl_info_has_init(DeclInfo *d) {
 
 
 
-Scope *create_scope(Scope *parent, gbAllocator allocator, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
-	Scope *s = gb_alloc_item(allocator, Scope);
+Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
+	Scope *s = gb_alloc_item(permanent_allocator(), Scope);
 	s->parent = parent;
 	string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
 	ptr_set_init(&s->imported, heap_allocator(), 0);
@@ -244,7 +244,7 @@ Scope *create_scope_from_file(CheckerContext *c, AstFile *f) {
 	GB_ASSERT(f->pkg != nullptr);
 	GB_ASSERT(f->pkg->scope != nullptr);
 
-	Scope *s = create_scope(f->pkg->scope, c->allocator);
+	Scope *s = create_scope(f->pkg->scope);
 
 	array_reserve(&s->delayed_imports, f->imports.count);
 	array_reserve(&s->delayed_directives, f->directive_count);
@@ -264,7 +264,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) {
 		decl_count += pkg->files[i]->decls.count;
 	}
 	isize init_elements_capacity = 2*decl_count;
-	Scope *s = create_scope(builtin_pkg->scope, c->allocator, init_elements_capacity);
+	Scope *s = create_scope(builtin_pkg->scope, init_elements_capacity);
 
 	s->flags |= ScopeFlag_Pkg;
 	s->pkg = pkg;
@@ -324,7 +324,7 @@ void check_open_scope(CheckerContext *c, Ast *node) {
 	GB_ASSERT(node->kind == Ast_Invalid ||
 	          is_ast_stmt(node) ||
 	          is_ast_type(node));
-	Scope *scope = create_scope(c->scope, c->allocator);
+	Scope *scope = create_scope(c->scope);
 	add_scope(c, node, scope);
 	switch (node->kind) {
 	case Ast_ProcType:
@@ -368,9 +368,14 @@ void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entit
 				if (e->kind == Entity_Label) {
 					continue;
 				}
-				if (e->kind == Entity_Variable &&
-				    !(e->scope->flags&ScopeFlag_File)) {
-					continue;
+				if (e->kind == Entity_Variable) {
+					if (e->scope->flags&ScopeFlag_File) {
+						// Global variables are file to access
+					} else if (e->flags&EntityFlag_Static) {
+						// Allow static/thread_local variables to be referenced
+					} else {
+						continue;
+					}
 				}
 			}
 
@@ -690,31 +695,33 @@ void add_global_type_entity(String name, Type *type) {
 
 void init_universal(void) {
 	BuildContext *bc = &build_context;
+
 	// NOTE(bill): No need to free these
-	gbAllocator a = heap_allocator();
+	// gbAllocator a = heap_allocator();
+	gbAllocator a = permanent_allocator();
 
 	builtin_pkg = gb_alloc_item(a, AstPackage);
 	builtin_pkg->name = str_lit("builtin");
 	builtin_pkg->kind = Package_Normal;
 
-	builtin_pkg->scope = create_scope(nullptr, a);
-	builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+	builtin_pkg->scope = create_scope(nullptr);
+	builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
 	builtin_pkg->scope->pkg = builtin_pkg;
 
 	intrinsics_pkg = gb_alloc_item(a, AstPackage);
 	intrinsics_pkg->name = str_lit("intrinsics");
 	intrinsics_pkg->kind = Package_Normal;
 
-	intrinsics_pkg->scope = create_scope(nullptr, a);
-	intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+	intrinsics_pkg->scope = create_scope(nullptr);
+	intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
 	intrinsics_pkg->scope->pkg = intrinsics_pkg;
 
 	config_pkg = gb_alloc_item(a, AstPackage);
 	config_pkg->name = str_lit("config");
 	config_pkg->kind = Package_Normal;
 
-	config_pkg->scope = create_scope(nullptr, a);
-	config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+	config_pkg->scope = create_scope(nullptr);
+	config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
 	config_pkg->scope->pkg = config_pkg;
 
 
@@ -724,6 +731,18 @@ void init_universal(void) {
 	}
 	add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
 
+	{
+		void set_procedure_abi_types(Type *type);
+
+		Type *equal_args[2] = {t_rawptr, t_rawptr};
+		t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless);
+		set_procedure_abi_types(t_equal_proc);
+
+		Type *hasher_args[2] = {t_rawptr, t_uintptr};
+		t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless);
+		set_procedure_abi_types(t_hasher_proc);
+	}
+
 // Constants
 	add_global_constant(str_lit("true"),  t_untyped_bool, exact_value_bool(true));
 	add_global_constant(str_lit("false"), t_untyped_bool, exact_value_bool(false));
@@ -742,6 +761,7 @@ void init_universal(void) {
 	add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR));
 	add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api));
 	add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals));
+	add_global_constant(str_lit("ODIN_TEST"), t_untyped_bool, exact_value_bool(bc->command_kind == Command_test));
 
 
 // Builtin Procedures
@@ -837,6 +857,8 @@ void init_checker_info(CheckerInfo *i) {
 	array_init(&i->variable_init_order, a);
 	array_init(&i->required_foreign_imports_through_force, a);
 	array_init(&i->required_global_variables, a);
+	array_init(&i->testing_procedures, a, 0, 0);
+
 
 	i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions;
 	if (i->allow_identifier_uses) {
@@ -870,7 +892,6 @@ CheckerContext make_checker_context(Checker *c) {
 	CheckerContext ctx = c->init_ctx;
 	ctx.checker   = c;
 	ctx.info      = &c->info;
-	ctx.allocator = c->allocator;
 	ctx.scope     = builtin_pkg->scope;
 	ctx.pkg       = builtin_pkg;
 
@@ -895,6 +916,7 @@ bool init_checker(Checker *c, Parser *parser) {
 	gbAllocator a = heap_allocator();
 
 	init_checker_info(&c->info);
+	c->info.checker = c;
 
 	array_init(&c->procs_to_check, a);
 	array_init(&c->procs_with_deferred_to_check, a);
@@ -904,8 +926,6 @@ bool init_checker(Checker *c, Parser *parser) {
 	isize total_token_count = c->parser->total_token_count;
 	isize arena_size = 2 * item_size * total_token_count;
 
-	c->allocator = heap_allocator();
-
 	c->init_ctx = make_checker_context(c);
 	return true;
 }
@@ -1502,11 +1522,10 @@ void add_min_dep_type_info(Checker *c, Type *t) {
 		ti_index = type_info_index(&c->info, t, false);
 	}
 	GB_ASSERT(ti_index >= 0);
-	if (ptr_set_exists(set, ti_index)) {
+	if (ptr_set_update(set, ti_index)) {
 		// Type Already exists
 		return;
 	}
-	ptr_set_add(set, ti_index);
 
 	// Add nested types
 	if (t->kind == Type_Named) {
@@ -1680,8 +1699,6 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
 	CheckerInfo *info = &c->info;
 	auto *set = &info->minimum_dependency_set;
 
-	String name = entity->token.string;
-
 	if (entity->type != nullptr &&
 	    is_type_polymorphic(entity->type)) {
 
@@ -1691,12 +1708,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
 		}
 	}
 
-	if (ptr_set_exists(set, entity)) {
+	if (ptr_set_update(set, entity)) {
 		return;
 	}
 
-
-	ptr_set_add(set, entity);
 	DeclInfo *decl = decl_info_of_entity(entity);
 	if (decl == nullptr) {
 		return;
@@ -1715,16 +1730,15 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
 			if (fl != nullptr) {
 				GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
 				              (fl->flags&EntityFlag_Used),
-				              "%.*s", LIT(name));
+				              "%.*s", LIT(entity->token.string));
 				add_dependency_to_set(c, fl);
 			}
-		}
-		if (e->kind == Entity_Variable && e->Variable.is_foreign) {
+		} else if (e->kind == Entity_Variable && e->Variable.is_foreign) {
 			Entity *fl = e->Variable.foreign_library;
 			if (fl != nullptr) {
 				GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
 				              (fl->flags&EntityFlag_Used),
-				              "%.*s", LIT(name));
+				              "%.*s", LIT(entity->token.string));
 				add_dependency_to_set(c, fl);
 			}
 		}
@@ -1733,7 +1747,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
 
 
 void generate_minimum_dependency_set(Checker *c, Entity *start) {
-	ptr_set_init(&c->info.minimum_dependency_set, heap_allocator());
+	isize entity_count = c->info.entities.count;
+	isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
+
+	ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap);
 	ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
 
 	String required_runtime_entities[] = {
@@ -1769,6 +1786,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("memcpy"),
 		str_lit("memmove"),
 
+		str_lit("memory_equal"),
 		str_lit("memory_compare"),
 		str_lit("memory_compare_zero"),
 
@@ -1842,7 +1860,68 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		add_dependency_to_set(c, e);
 	}
 
-	add_dependency_to_set(c, start);
+	for_array(i, c->info.entities) {
+		Entity *e = c->info.entities[i];
+		switch (e->kind) {
+		case Entity_Variable:
+			if (e->Variable.is_export) {
+				add_dependency_to_set(c, e);
+			}
+			break;
+		case Entity_Procedure:
+			if (e->Procedure.is_export) {
+				add_dependency_to_set(c, e);
+			}
+			break;
+		}
+	}
+
+	if (build_context.command_kind == Command_test) {
+		AstPackage *pkg = c->info.init_package;
+		Scope *s = pkg->scope;
+		for_array(i, s->elements.entries) {
+			Entity *e = s->elements.entries[i].value;
+			if (e->kind != Entity_Procedure) {
+				continue;
+			}
+
+			if (e->file == nullptr || !e->file->is_test) {
+				continue;
+			}
+
+			String name = e->token.string;
+			String prefix = str_lit("test_");
+
+			if (!string_starts_with(name, prefix)) {
+				continue;
+			}
+
+			bool is_tester = false;
+			if (name != prefix) {
+				is_tester = true;
+			} else {
+				error(e->token, "Invalid testing procedure name: %.*s", LIT(name));
+			}
+
+			Type *t = base_type(e->type);
+			GB_ASSERT(t->kind == Type_Proc);
+			if (t->Proc.param_count == 0 && t->Proc.result_count == 0) {
+				// Good
+			} else {
+				gbString str = type_to_string(t);
+				error(e->token, "Testing procedures must have a signature type of proc(), got %s", str);
+				gb_string_free(str);
+				is_tester = false;
+			}
+
+			if (is_tester) {
+				add_dependency_to_set(c, e);
+				array_add(&c->info.testing_procedures, e);
+			}
+		}
+	} else {
+		add_dependency_to_set(c, start);
+	}
 }
 
 bool is_entity_a_dependency(Entity *e) {
@@ -1881,19 +1960,17 @@ void add_entity_dependency_from_procedure_parameters(Map<EntityGraphNode *> *M,
 
 }
 
-Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
+Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) {
 #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
 
-	gbAllocator a = heap_allocator();
-
 	Map<EntityGraphNode *> M = {}; // Key: Entity *
-	map_init(&M, a, info->entities.count);
+	map_init(&M, allocator, info->entities.count);
 	defer (map_destroy(&M));
 	for_array(i, info->entities) {
 		Entity *e = info->entities[i];
 		DeclInfo *d = e->decl_info;
 		if (is_entity_a_dependency(e)) {
-			EntityGraphNode *n = gb_alloc_item(a, EntityGraphNode);
+			EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode);
 			n->entity = e;
 			map_set(&M, hash_pointer(e), n);
 		}
@@ -1928,7 +2005,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
 	// 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");
-	auto G = array_make<EntityGraphNode *>(a, 0, M.entries.count);
+	auto G = array_make<EntityGraphNode *>(allocator, 0, M.entries.count);
 
 	for_array(i, M.entries) {
 		auto *entry = &M.entries[i];
@@ -1949,17 +2026,27 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
 						EntityGraphNode *s = n->succ.entries[k].ptr;
 						// Ignore self-cycles
 						if (s != n) {
+							if (p->entity->kind == Entity_Procedure &&
+							    s->entity->kind == Entity_Procedure) {
+							    	// NOTE(bill, 2020-11-15): Only care about variable initialization ordering
+							    	// TODO(bill): This is probably wrong!!!!
+								continue;
+							}
+							// IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
+							// the time to process
+
 							entity_graph_node_set_add(&p->succ, s);
 							entity_graph_node_set_add(&s->pred, p);
 							// Remove edge to 'n'
 							entity_graph_node_set_remove(&s->pred, n);
 						}
 					}
+
 					// Remove edge to 'n'
 					entity_graph_node_set_remove(&p->succ, n);
 				}
 			}
-		} else {
+		} else if (e->kind == Entity_Variable) {
 			array_add(&G, n);
 		}
 	}
@@ -1972,6 +2059,28 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
 		GB_ASSERT(n->dep_count >= 0);
 	}
 
+	// f64 succ_count = 0.0;
+	// f64 pred_count = 0.0;
+	// f64 succ_capacity = 0.0;
+	// f64 pred_capacity = 0.0;
+	// f64 succ_max = 0.0;
+	// f64 pred_max = 0.0;
+	// for_array(i, G) {
+	// 	EntityGraphNode *n = G[i];
+	// 	succ_count += n->succ.entries.count;
+	// 	pred_count += n->pred.entries.count;
+	// 	succ_capacity += n->succ.entries.capacity;
+	// 	pred_capacity += n->pred.entries.capacity;
+
+	// 	succ_max = gb_max(succ_max, n->succ.entries.capacity);
+	// 	pred_max = gb_max(pred_max, n->pred.entries.capacity);
+
+	// }
+	// f64 count = cast(f64)G.count;
+	// gb_printf_err(">>>count    pred: %f succ: %f\n", pred_count/count, succ_count/count);
+	// gb_printf_err(">>>capacity pred: %f succ: %f\n", pred_capacity/count, succ_capacity/count);
+	// gb_printf_err(">>>max      pred: %f succ: %f\n", pred_max, succ_max);
+
 	return G;
 
 #undef TIME_SECTION
@@ -2088,9 +2197,9 @@ void init_core_type_info(Checker *c) {
 	t_type_info_enum_value = type_info_enum_value->type;
 	t_type_info_enum_value_ptr = alloc_type_pointer(t_type_info_enum_value);
 
-	GB_ASSERT(tis->fields.count == 4);
+	GB_ASSERT(tis->fields.count == 5);
 
-	Entity *type_info_variant = tis->fields[3];
+	Entity *type_info_variant = tis->fields[4];
 	Type *tiv_type = type_info_variant->type;
 	GB_ASSERT(is_type_union(tiv_type));
 
@@ -2186,14 +2295,14 @@ void init_core_source_code_location(Checker *c) {
 }
 
 void init_core_map_type(Checker *c) {
-	if (t_map_key == nullptr) {
-		Entity *e = find_core_entity(c, str_lit("Map_Key"));
+	if (t_map_hash == nullptr) {
+		Entity *e = find_core_entity(c, str_lit("Map_Hash"));
 		if (e->state == EntityState_Unresolved) {
 			auto ctx = c->init_ctx;
 			check_entity_decl(&ctx, e, nullptr, nullptr);
 		}
-		t_map_key = e->type;
-		GB_ASSERT(t_map_key != nullptr);
+		t_map_hash = e->type;
+		GB_ASSERT(t_map_hash != nullptr);
 	}
 
 	if (t_map_header == nullptr) {
@@ -2579,7 +2688,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
 
 				if (valid && build_context.use_llvm_api) {
 					if (ac->atom_op_table == nullptr) {
-						ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+						ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
 					}
 					ac->atom_op_table->op[TypeAtomOp_index_get] = e;
 				}
@@ -2638,7 +2747,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
 
 				if (valid && build_context.use_llvm_api) {
 					if (ac->atom_op_table == nullptr) {
-						ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+						ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
 					}
 					ac->atom_op_table->op[TypeAtomOp_index_set] = e;
 				}
@@ -2720,7 +2829,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
 
 				if (valid && build_context.use_llvm_api) {
 					if (ac->atom_op_table == nullptr) {
-						ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+						ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
 					}
 					ac->atom_op_table->op[TypeAtomOp_slice] = e;
 				}
@@ -2809,7 +2918,7 @@ void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, De
 }
 
 
-isize get_total_value_count(Array<Ast *> const &values) {
+isize get_total_value_count(Slice<Ast *> const &values) {
 	isize count = 0;
 	for_array(i, values) {
 		Type *t = type_of_expr(values[i]);
@@ -2967,8 +3076,8 @@ void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<Ast *> *attr
 }
 
 void check_collect_value_decl(CheckerContext *c, Ast *decl) {
-	if (decl->been_handled) return;
-	decl->been_handled = true;
+	if (decl->state_flags & StateFlag_BeenHandled) return;
+	decl->state_flags |= StateFlag_BeenHandled;
 
 	ast_node(vd, ValueDecl, decl);
 
@@ -3023,7 +3132,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 				} else {
 					entity_visibility_kind = kind;
 				}
-				array_unordered_remove(elems, j);
+				slice_unordered_remove(elems, j);
 				j -= 1;
 			}
 		}
@@ -3072,7 +3181,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 			}
 
 			Ast *init_expr = value;
-			DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl);
+			DeclInfo *d = make_decl_info(c->scope, c->decl);
+			d->decl_node = decl;
+			d->comment = vd->comment;
+			d->docs    = vd->docs;
 			d->entity    = e;
 			d->type_expr = vd->type;
 			d->init_expr = init_expr;
@@ -3100,9 +3212,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 			Token token = name->Ident.token;
 
 			Ast *fl = c->foreign_context.curr_library;
-			DeclInfo *d = make_decl_info(c->allocator, c->scope, c->decl);
 			Entity *e = nullptr;
+			DeclInfo *d = make_decl_info(c->scope, c->decl);
 
+			d->decl_node = decl;
+			d->comment = vd->comment;
+			d->docs    = vd->docs;
 			d->attributes = vd->attributes;
 			d->type_expr = vd->type;
 			d->init_expr = init;
@@ -3186,8 +3301,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 }
 
 void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
-	if (decl->been_handled) return;
-	decl->been_handled = true;
+	if (decl->state_flags & StateFlag_BeenHandled) return;
+	decl->state_flags |= StateFlag_BeenHandled;
 
 	ast_node(fb, ForeignBlockDecl, decl);
 	Ast *foreign_library = fb->foreign_library;
@@ -3207,7 +3322,7 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
 }
 
 // NOTE(bill): If file_scopes == nullptr, this will act like a local scope
-void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes) {
+void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
 	for_array(decl_index, nodes) {
 		Ast *decl = nodes[decl_index];
 		if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) {
@@ -3522,11 +3637,9 @@ struct ImportPathItem {
 Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited) {
 	Array<ImportPathItem> empty_path = {};
 
-	if (ptr_set_exists(visited, start)) {
+	if (ptr_set_update(visited, start)) {
 		return empty_path;
 	}
-	ptr_set_add(visited, start);
-
 
 	String path = start->fullpath;
 	AstPackage **found = string_map_get(&c->info.packages, path);
@@ -3571,8 +3684,8 @@ Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage
 }
 #endif
 void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
-	if (decl->been_handled) return;
-	decl->been_handled = true;
+	if (decl->state_flags & StateFlag_BeenHandled) return;
+	decl->state_flags |= StateFlag_BeenHandled;
 
 	ast_node(id, ImportDecl, decl);
 	Token token = id->relpath;
@@ -3612,10 +3725,8 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
 	GB_ASSERT(scope->flags&ScopeFlag_Pkg);
 
 
-	if (ptr_set_exists(&parent_scope->imported, scope)) {
+	if (ptr_set_update(&parent_scope->imported, scope)) {
 		// error(token, "Multiple import of the same file within this scope");
-	} else {
-		ptr_set_add(&parent_scope->imported, scope);
 	}
 
 	String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
@@ -3686,8 +3797,8 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
 }
 
 void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
-	if (decl->been_handled) return;
-	decl->been_handled = true;
+	if (decl->state_flags & StateFlag_BeenHandled) return;
+	decl->state_flags |= StateFlag_BeenHandled;
 
 	ast_node(fl, ForeignImportDecl, decl);
 
@@ -3738,7 +3849,7 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
 	}
 }
 
-bool collect_checked_packages_from_decl_list(Checker *c, Array<Ast *> const &decls) {
+bool collect_checked_packages_from_decl_list(Checker *c, Slice<Ast *> const &decls) {
 	bool new_files = false;
 	for_array(i, decls) {
 		Ast *decl = decls[i];
@@ -3760,7 +3871,7 @@ bool collect_checked_packages_from_decl_list(Checker *c, Array<Ast *> const &dec
 }
 
 // Returns true if a new package is present
-bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls);
+bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls);
 bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws);
 
 bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) {
@@ -3835,7 +3946,7 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) {
 	return false;
 }
 
-bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls) {
+bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
 	GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
 
 	if (collect_checked_packages_from_decl_list(ctx->checker, decls)) {
@@ -3968,10 +4079,9 @@ void check_import_entities(Checker *c) {
 		if (pkg == nullptr) {
 			continue;
 		}
-		if (ptr_set_exists(&emitted, pkg)) {
+		if (ptr_set_update(&emitted, pkg)) {
 			continue;
 		}
-		ptr_set_add(&emitted, pkg);
 
 		array_add(&package_order, n);
 	}
@@ -4162,7 +4272,7 @@ void calculate_global_init_order(Checker *c) {
 	CheckerInfo *info = &c->info;
 
 	TIME_SECTION("calculate_global_init_order: generate entity dependency graph");
-	Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info);
+	Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, heap_allocator());
 	defer ({
 		for_array(i, dep_graph) {
 			entity_graph_node_destroy(dep_graph[i], heap_allocator());
@@ -4214,11 +4324,9 @@ void calculate_global_init_order(Checker *c) {
 		// if (!decl_info_has_init(d)) {
 		// 	continue;
 		// }
-		if (ptr_set_exists(&emitted, d)) {
+		if (ptr_set_update(&emitted, d)) {
 			continue;
 		}
-		ptr_set_add(&emitted, d);
-
 
 		array_add(&info->variable_init_order, d);
 	}
@@ -4299,10 +4407,11 @@ void check_parsed_files(Checker *c) {
 	for_array(i, c->parser->packages) {
 		AstPackage *p = c->parser->packages[i];
 		Scope *scope = create_scope_from_package(&c->init_ctx, p);
-		p->decl_info = make_decl_info(c->allocator, scope, c->init_ctx.decl);
+		p->decl_info = make_decl_info(scope, c->init_ctx.decl);
 		string_map_set(&c->info.packages, p->fullpath, p);
 
 		if (scope->flags&ScopeFlag_Init) {
+			c->info.init_package = p;
 			c->info.init_scope = scope;
 		}
 		if (p->kind == Package_Runtime) {
@@ -4572,7 +4681,7 @@ void check_parsed_files(Checker *c) {
 
 
 	TIME_SECTION("check entry point");
-	if (build_context.build_mode == BuildMode_Executable) {
+	if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
 		Scope *s = c->info.init_scope;
 		GB_ASSERT(s != nullptr);
 		GB_ASSERT(s->flags&ScopeFlag_Init);

+ 20 - 12
src/checker.hpp

@@ -45,7 +45,7 @@ enum StmtFlag {
 
 	Stmt_TypeSwitch = 1<<4,
 
-	Stmt_CheckScopeDecls    = 1<<5,
+	Stmt_CheckScopeDecls = 1<<5,
 };
 
 enum BuiltinProcPkg {
@@ -132,6 +132,7 @@ struct DeclInfo {
 
 	Entity *entity;
 
+	Ast *         decl_node;
 	Ast *         type_expr;
 	Ast *         init_expr;
 	Array<Ast *>  attributes;
@@ -140,6 +141,9 @@ struct DeclInfo {
 	bool          is_using;
 	bool          where_clauses_evaluated;
 
+	CommentGroup *comment;
+	CommentGroup *docs;
+
 	PtrSet<Entity *>  deps;
 	PtrSet<Type *>    type_info_deps;
 	Array<BlockLabel> labels;
@@ -160,12 +164,13 @@ struct ProcInfo {
 
 
 enum ScopeFlag : i32 {
-	ScopeFlag_Pkg    = 1<<1,
-	ScopeFlag_Global = 1<<2,
-	ScopeFlag_File   = 1<<3,
-	ScopeFlag_Init   = 1<<4,
-	ScopeFlag_Proc   = 1<<5,
-	ScopeFlag_Type   = 1<<6,
+	ScopeFlag_Pkg     = 1<<1,
+	ScopeFlag_Builtin = 1<<2,
+	ScopeFlag_Global  = 1<<3,
+	ScopeFlag_File    = 1<<4,
+	ScopeFlag_Init    = 1<<5,
+	ScopeFlag_Proc    = 1<<6,
+	ScopeFlag_Type    = 1<<7,
 
 	ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
 
@@ -247,8 +252,12 @@ struct AtomOpMapEntry {
 };
 
 
+struct CheckerContext;
+
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
+	Checker *checker;
+
 	Map<ExprInfo>         untyped; // Key: Ast * | Expression -> ExprInfo
 	                               // NOTE(bill): This needs to be a map and not on the Ast
 	                               // as it needs to be iterated across
@@ -268,6 +277,7 @@ struct CheckerInfo {
 
 	AstPackage *          builtin_package;
 	AstPackage *          runtime_package;
+	AstPackage *          init_package;
 	Scope *               init_scope;
 	Entity *              entry_point;
 	PtrSet<Entity *>      minimum_dependency_set;
@@ -278,6 +288,7 @@ struct CheckerInfo {
 
 	Map<AtomOpMapEntry>   atom_op_map; // Key: Ast *
 
+	Array<Entity *> testing_procedures;
 
 	bool allow_identifier_uses;
 	Array<Ast *> identifier_uses; // only used by 'odin query'
@@ -301,7 +312,6 @@ struct CheckerContext {
 	ProcCallingConvention curr_proc_calling_convention;
 	bool           in_proc_sig;
 	ForeignContext foreign_context;
-	gbAllocator    allocator;
 
 	CheckerTypePath *type_path;
 	isize            type_level; // TODO(bill): Actually handle correctly
@@ -317,6 +327,7 @@ struct CheckerContext {
 	bool       no_polymorphic_errors;
 	bool       hide_polymorphic_errors;
 	bool       in_polymorphic_specialization;
+	bool       allow_arrow_right_selector_expr;
 	Scope *    polymorphic_scope;
 
 	Ast *assignment_lhs_hint;
@@ -331,14 +342,11 @@ struct Checker {
 	Array<Entity *> procs_with_deferred_to_check;
 
 	CheckerContext *curr_ctx;
-	gbAllocator    allocator;
 	CheckerContext init_ctx;
 };
 
 
 
-
-
 gb_global AstPackage *builtin_pkg    = nullptr;
 gb_global AstPackage *intrinsics_pkg = nullptr;
 gb_global AstPackage *config_pkg      = nullptr;
@@ -387,7 +395,7 @@ void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
 
 
 bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
-void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes);
+void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
 void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
 void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
 

+ 6 - 0
src/checker_builtin_procs.hpp

@@ -183,6 +183,9 @@ BuiltinProc__type_simple_boolean_end,
 
 	BuiltinProc_type_field_index_of,
 
+	BuiltinProc_type_equal_proc,
+	BuiltinProc_type_hasher_proc,
+
 BuiltinProc__type_end,
 
 
@@ -367,5 +370,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 	{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
+	{STR_LIT("type_equal_proc"),  1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 };

+ 132 - 9
src/common.cpp

@@ -56,6 +56,14 @@ gb_inline isize align_formula_isize(isize size, isize align) {
 	}
 	return size;
 }
+gb_inline void *align_formula_ptr(void *ptr, isize align) {
+	if (align > 0) {
+		uintptr result = (cast(uintptr)ptr) + align-1;
+		return (void *)(result - result%align);
+	}
+	return ptr;
+}
+
 
 GB_ALLOCATOR_PROC(heap_allocator_proc);
 
@@ -373,13 +381,16 @@ typedef struct Arena {
 	gbAllocator backing;
 	isize       block_size;
 	gbMutex     mutex;
-
 	isize total_used;
+	bool   use_mutex;
 } Arena;
 
 #define ARENA_MIN_ALIGNMENT 16
 #define ARENA_DEFAULT_BLOCK_SIZE (8*1024*1024)
 
+
+gb_global Arena permanent_arena = {};
+
 void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
 	arena->backing = backing;
 	arena->block_size = block_size;
@@ -388,8 +399,9 @@ void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAUL
 }
 
 void arena_grow(Arena *arena, isize min_size) {
-	// gb_mutex_lock(&arena->mutex);
-	// defer (gb_mutex_unlock(&arena->mutex));
+	if (arena->use_mutex) {
+		gb_mutex_lock(&arena->mutex);
+	}
 
 	isize size = gb_max(arena->block_size, min_size);
 	size = ALIGN_UP(size, ARENA_MIN_ALIGNMENT);
@@ -399,11 +411,16 @@ void arena_grow(Arena *arena, isize min_size) {
 	GB_ASSERT(arena->ptr == ALIGN_DOWN_PTR(arena->ptr, ARENA_MIN_ALIGNMENT));
 	arena->end = arena->ptr + size;
 	array_add(&arena->blocks, arena->ptr);
+
+	if (arena->use_mutex) {
+		gb_mutex_unlock(&arena->mutex);
+	}
 }
 
 void *arena_alloc(Arena *arena, isize size, isize alignment) {
-	// gb_mutex_lock(&arena->mutex);
-	// defer (gb_mutex_unlock(&arena->mutex));
+	if (arena->use_mutex) {
+		gb_mutex_lock(&arena->mutex);
+	}
 
 	arena->total_used += size;
 
@@ -419,12 +436,17 @@ void *arena_alloc(Arena *arena, isize size, isize alignment) {
 	GB_ASSERT(arena->ptr <= arena->end);
 	GB_ASSERT(ptr == ALIGN_DOWN_PTR(ptr, align));
 	// zero_size(ptr, size);
+
+	if (arena->use_mutex) {
+		gb_mutex_unlock(&arena->mutex);
+	}
 	return ptr;
 }
 
 void arena_free_all(Arena *arena) {
-	// gb_mutex_lock(&arena->mutex);
-	// defer (gb_mutex_unlock(&arena->mutex));
+	if (arena->use_mutex) {
+		gb_mutex_lock(&arena->mutex);
+	}
 
 	for_array(i, arena->blocks) {
 		gb_free(arena->backing, arena->blocks[i]);
@@ -432,8 +454,11 @@ void arena_free_all(Arena *arena) {
 	array_clear(&arena->blocks);
 	arena->ptr = nullptr;
 	arena->end = nullptr;
-}
 
+	if (arena->use_mutex) {
+		gb_mutex_unlock(&arena->mutex);
+	}
+}
 
 
 
@@ -460,7 +485,14 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
 		// GB_PANIC("gbAllocation_Free not supported");
 		break;
 	case gbAllocation_Resize:
-		GB_PANIC("gbAllocation_Resize: not supported");
+		if (size == 0) {
+			ptr = nullptr;
+		} else if (size <= old_size) {
+			ptr = old_memory;
+		} else {
+			ptr = arena_alloc(arena, size, alignment);
+			gb_memmove(ptr, old_memory, old_size);
+		}
 		break;
 	case gbAllocation_FreeAll:
 		arena_free_all(arena);
@@ -471,6 +503,97 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
 }
 
 
+gbAllocator permanent_allocator() {
+	return arena_allocator(&permanent_arena);
+	// return heap_allocator();
+}
+
+
+
+struct Temp_Allocator {
+	u8 *data;
+	isize len;
+	isize curr_offset;
+	gbAllocator backup_allocator;
+	Array<void *> leaked_allocations;
+};
+
+gb_global Temp_Allocator temporary_allocator_data = {};
+
+void temp_allocator_init(Temp_Allocator *s, isize size) {
+	s->backup_allocator = heap_allocator();
+	s->data = cast(u8 *)gb_alloc_align(s->backup_allocator, size, 16);
+	s->curr_offset = 0;
+	s->leaked_allocations.allocator = s->backup_allocator;
+}
+
+void *temp_allocator_alloc(Temp_Allocator *s, isize size, isize alignment) {
+	size = align_formula_isize(size, alignment);
+	if (s->curr_offset+size <= s->len) {
+		u8 *start = s->data;
+		u8 *ptr = start + s->curr_offset;
+		ptr = cast(u8 *)align_formula_ptr(ptr, alignment);
+		// assume memory is zero
+
+		isize offset = ptr - start;
+		s->curr_offset = offset + size;
+		return ptr;
+	} else if (size <= s->len) {
+		u8 *start = s->data;
+		u8 *ptr = cast(u8 *)align_formula_ptr(start, alignment);
+		// assume memory is zero
+
+		isize offset = ptr - start;
+		s->curr_offset = offset + size;
+		return ptr;
+	}
+
+	void *ptr = gb_alloc_align(s->backup_allocator, size, alignment);
+	array_add(&s->leaked_allocations, ptr);
+	return ptr;
+}
+
+void temp_allocator_free_all(Temp_Allocator *s) {
+	s->curr_offset = 0;
+	for_array(i, s->leaked_allocations) {
+		gb_free(s->backup_allocator, s->leaked_allocations[i]);
+	}
+	array_clear(&s->leaked_allocations);
+	gb_zero_size(s->data, s->len);
+}
+
+GB_ALLOCATOR_PROC(temp_allocator_proc) {
+	void *ptr = nullptr;
+	Temp_Allocator *s = cast(Temp_Allocator *)allocator_data;
+	GB_ASSERT_NOT_NULL(s);
+
+	switch (type) {
+	case gbAllocation_Alloc:
+		return temp_allocator_alloc(s, size, alignment);
+	case gbAllocation_Free:
+		break;
+	case gbAllocation_Resize:
+		if (size == 0) {
+			ptr = nullptr;
+		} else if (size <= old_size) {
+			ptr = old_memory;
+		} else {
+			ptr = temp_allocator_alloc(s, size, alignment);
+			gb_memmove(ptr, old_memory, old_size);
+		}
+		break;
+	case gbAllocation_FreeAll:
+		temp_allocator_free_all(s);
+		break;
+	}
+
+	return ptr;
+}
+
+
+gbAllocator temporary_allocator() {
+	return {temp_allocator_proc, &temporary_allocator_data};
+}
 
 
 

+ 284 - 72
src/docs.cpp

@@ -1,22 +1,102 @@
 // Generates Documentation
 
-gbString expr_to_string(Ast *expression);
+gb_global int print_entity_kind_ordering[Entity_Count] = {
+	/*Invalid*/     -1,
+	/*Constant*/    0,
+	/*Variable*/    1,
+	/*TypeName*/    4,
+	/*Procedure*/   2,
+	/*ProcGroup*/   3,
+	/*Builtin*/     -1,
+	/*ImportName*/  -1,
+	/*LibraryName*/ -1,
+	/*Nil*/         -1,
+	/*Label*/       -1,
+};
+gb_global char const *print_entity_names[Entity_Count] = {
+	/*Invalid*/     "",
+	/*Constant*/    "constants",
+	/*Variable*/    "variables",
+	/*TypeName*/    "types",
+	/*Procedure*/   "procedures",
+	/*ProcGroup*/   "proc_group",
+	/*Builtin*/     "",
+	/*ImportName*/  "import names",
+	/*LibraryName*/ "library names",
+	/*Nil*/         "",
+	/*Label*/       "",
+};
 
-String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
+
+GB_COMPARE_PROC(cmp_entities_for_printing) {
+	GB_ASSERT(a != nullptr);
+	GB_ASSERT(b != nullptr);
+	Entity *x = *cast(Entity **)a;
+	Entity *y = *cast(Entity **)b;
+	int res = 0;
+	res = string_compare(x->pkg->name, y->pkg->name);
+	if (res != 0) {
+		return res;
+	}
+	int ox = print_entity_kind_ordering[x->kind];
+	int oy = print_entity_kind_ordering[y->kind];
+	res = ox - oy;
+	if (res != 0) {
+		return res;
+	}
+	res = string_compare(x->token.string, y->token.string);
+	return res;
+}
+
+GB_COMPARE_PROC(cmp_ast_package_by_name) {
+	GB_ASSERT(a != nullptr);
+	GB_ASSERT(b != nullptr);
+	AstPackage *x = *cast(AstPackage **)a;
+	AstPackage *y = *cast(AstPackage **)b;
+	return string_compare(x->name, y->name);
+}
+
+void print_doc_line(i32 indent, char const *fmt, ...) {
+	while (indent --> 0) {
+		gb_printf("\t");
+	}
+	va_list va;
+	va_start(va, fmt);
+	gb_printf_va(fmt, va);
+	va_end(va);
+	gb_printf("\n");
+}
+void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
+	while (indent --> 0) {
+		gb_printf("\t");
+	}
+	va_list va;
+	va_start(va, fmt);
+	gb_printf_va(fmt, va);
+	va_end(va);
+}
+
+bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
+	if (g == nullptr) {
+		return false;
+	}
 	isize len = 0;
-	for_array(i, g.list) {
-		String comment = g.list[i].string;
+	for_array(i, g->list) {
+		String comment = g->list[i].string;
 		len += comment.len;
 		len += 1; // for \n
 	}
-	if (len == 0) {
-		return make_string(nullptr, 0);
+	if (len <= g->list.count) {
+		return false;
 	}
 
-	u8 *text = gb_alloc_array(a, u8, len+1);
-	len = 0;
-	for_array(i, g.list) {
-		String comment = g.list[i].string;
+	isize count = 0;
+	for_array(i, g->list) {
+		String comment = g->list[i].string;
+		String original_comment = comment;
+
+		bool slash_slash = comment[1] == '/';
+		bool slash_star = comment[1] == '*';
 		if (comment[1] == '/') {
 			comment.text += 2;
 			comment.len  -= 2;
@@ -24,84 +104,216 @@ String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
 			comment.text += 2;
 			comment.len  -= 4;
 		}
-		comment = string_trim_whitespace(comment);
-		gb_memmove(text+len, comment.text, comment.len);
-		len += comment.len;
-		text[len++] = '\n';
-	}
-	return make_string(text, len);
-}
 
-#if 0
-void print_type_spec(Ast *spec) {
-	ast_node(ts, TypeSpec, spec);
-	GB_ASSERT(ts->name->kind == Ast_Ident);
-	String name = ts->name->Ident.string;
-	if (name.len == 0) {
-		return;
+		// Ignore the first space
+		if (comment.len > 0 && comment[0] == ' ') {
+			comment.text += 1;
+			comment.len  -= 1;
+		}
+
+		if (slash_slash) {
+			if (string_starts_with(comment, str_lit("+"))) {
+				continue;
+			}
+			if (string_starts_with(comment, str_lit("@("))) {
+				continue;
+			}
+		}
+
+		if (slash_slash) {
+			print_doc_line(indent, "%.*s", LIT(comment));
+			count += 1;
+		} else {
+			isize pos = 0;
+			for (; pos < comment.len; pos++) {
+				isize end = pos;
+				for (; end < comment.len; end++) {
+					if (comment[end] == '\n') {
+						break;
+					}
+				}
+				String line = substring(comment, pos, end);
+				pos = end+1;
+				String trimmed_line = string_trim_whitespace(line);
+				if (trimmed_line.len == 0) {
+					if (count == 0) {
+						continue;
+					}
+				}
+				/*
+				 * Remove comments with
+				 * styles
+				 * like this
+				 */
+				if (string_starts_with(line, str_lit("* "))) {
+					line = substring(line, 2, line.len);
+				}
+
+				print_doc_line(indent, "%.*s", LIT(line));
+				count += 1;
+			}
+		}
 	}
-	if (name[0] == '_') {
-		return;
+
+	if (count > 0) {
+		print_doc_line(0, "");
+		return true;
 	}
-	gb_printf("type %.*s\n", LIT(name));
+	return false;
 }
 
-void print_proc_decl(AstProcDecl *pd) {
-	GB_ASSERT(pd->name->kind == Ast_Ident);
-	String name = pd->name->Ident.string;
-	if (name.len == 0) {
-		return;
+
+
+
+void print_doc_expr(Ast *expr) {
+	gbString s = nullptr;
+	if (build_context.cmd_doc_flags & CmdDocFlag_Short) {
+		s = expr_to_string_shorthand(expr);
+	} else {
+		s = expr_to_string(expr);
 	}
-	if (name[0] == '_') {
+	gb_file_write(gb_file_get_standard(gbFileStandard_Output), s, gb_string_length(s));
+	gb_string_free(s);
+}
+
+
+void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
+	if (pkg == nullptr) {
 		return;
 	}
 
-	String docs = alloc_comment_group_string(heap_allocator(), pd->docs);
-	defer (gb_free(heap_allocator(), docs.text));
+	print_doc_line(0, "package %.*s", LIT(pkg->name));
 
-	if (docs.len > 0) {
-		gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len);
-	} else {
-		return;
+
+	for_array(i, pkg->files) {
+		AstFile *f = pkg->files[i];
+		if (f->pkg_decl) {
+			GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl);
+			print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs);
+		}
 	}
 
-	ast_node(proc_type, ProcType, pd->type);
-
-	gbString params = expr_to_string(proc_type->params);
-	defer (gb_string_free(params));
-	gb_printf("proc %.*s(%s)", LIT(name), params);
-	if (proc_type->results != nullptr)  {
-		ast_node(fl, FieldList, proc_type->results);
-		isize count = fl->list.count;
-		if (count > 0) {
-			gbString results = expr_to_string(proc_type->results);
-			defer (gb_string_free(results));
-			gb_printf(" -> ");
-			if (count != 1) {
-				gb_printf("(");
+	if (pkg->scope != nullptr) {
+		auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
+		defer (array_free(&entities));
+		for_array(i, pkg->scope->elements.entries) {
+			Entity *e = pkg->scope->elements.entries[i].value;
+			switch (e->kind) {
+			case Entity_Invalid:
+			case Entity_Builtin:
+			case Entity_Nil:
+			case Entity_Label:
+				continue;
+			case Entity_Constant:
+			case Entity_Variable:
+			case Entity_TypeName:
+			case Entity_Procedure:
+			case Entity_ProcGroup:
+			case Entity_ImportName:
+			case Entity_LibraryName:
+				// Fine
+				break;
+			}
+			array_add(&entities, e);
+		}
+		gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
+
+		bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
+
+		EntityKind curr_entity_kind = Entity_Invalid;
+		for_array(i, entities) {
+			Entity *e = entities[i];
+			if (e->pkg != pkg) {
+				continue;
+			}
+			if (!is_entity_exported(e)) {
+				continue;
+			}
+
+
+			if (curr_entity_kind != e->kind) {
+				if (curr_entity_kind != Entity_Invalid) {
+					print_doc_line(0, "");
+				}
+				curr_entity_kind = e->kind;
+				print_doc_line(1, "%s", print_entity_names[e->kind]);
+			}
+
+			Ast *type_expr = nullptr;
+			Ast *init_expr = nullptr;
+			Ast *decl_node = nullptr;
+			CommentGroup *comment = nullptr;
+			CommentGroup *docs = nullptr;
+			if (e->decl_info != nullptr) {
+				type_expr = e->decl_info->type_expr;
+				init_expr = e->decl_info->init_expr;
+				decl_node = e->decl_info->decl_node;
+				comment = e->decl_info->comment;
+				docs = e->decl_info->docs;
 			}
-			gb_printf("%s", results);
-			if (count != 1) {
-				gb_printf(")");
+			GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
+
+			print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
+			if (type_expr != nullptr) {
+				gbString t = expr_to_string(type_expr);
+				gb_printf(": %s ", t);
+				gb_string_free(t);
+			} else {
+				gb_printf(" :");
+			}
+			if (e->kind == Entity_Variable) {
+				if (init_expr != nullptr) {
+					gb_printf("= ");
+					print_doc_expr(init_expr);
+				}
+			} else {
+				gb_printf(": ");
+				print_doc_expr(init_expr);
+			}
+
+			gb_printf(";\n");
+
+			if (show_docs) {
+				print_doc_comment_group_string(3, docs);
 			}
 		}
+		print_doc_line(0, "");
 	}
-	gb_printf("\n\n");
-}
-#endif
-void print_declaration(Ast *decl) {
+
+	if (pkg->fullpath.len != 0) {
+		print_doc_line(0, "");
+		print_doc_line(1, "fullpath:");
+		print_doc_line(2, "%.*s", LIT(pkg->fullpath));
+		print_doc_line(1, "files:");
+		for_array(i, pkg->files) {
+			AstFile *f = pkg->files[i];
+			String filename = remove_directory_from_path(f->fullpath);
+			print_doc_line(2, "%.*s", LIT(filename));
+		}
+	}
+
 }
 
-void generate_documentation(Parser *parser) {
-	// for_array(file_index, parser->files) {
-	// 	AstFile *file = parser->files[file_index];
-	// 	Tokenizer *tokenizer = &file->tokenizer;
-	// 	String fullpath = tokenizer->fullpath;
-	// 	gb_printf("%.*s\n", LIT(fullpath));
-
-	// 	for_array(decl_index, file->decls) {
-	// 		Ast *decl = file->decls[decl_index];
-	// 		print_declaration(decl);
-	// 	}
-	// }
+void generate_documentation(Checker *c) {
+	CheckerInfo *info = &c->info;
+
+	auto pkgs = array_make<AstPackage *>(permanent_allocator(), 0, info->packages.entries.count);
+	for_array(i, info->packages.entries) {
+		AstPackage *pkg = info->packages.entries[i].value;
+		if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
+			array_add(&pkgs, pkg);
+		} else {
+			if (pkg->kind == Package_Init) {
+				array_add(&pkgs, pkg);
+			} else if (pkg->is_extra) {
+				array_add(&pkgs, pkg);
+			}
+		}
+	}
+
+	gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
+
+	for_array(i, pkgs) {
+		print_doc_package(info, pkgs[i]);
+	}
 }

+ 4 - 3
src/entity.cpp

@@ -120,6 +120,7 @@ struct Entity {
 	union {
 		struct {
 			ExactValue value;
+			ParameterValue param_value;
 		} Constant;
 		struct {
 			Ast *init_expr; // only used for some variables within procedure bodies
@@ -164,7 +165,7 @@ struct Entity {
 			Scope *scope;
 		} ImportName;
 		struct {
-			Array<String> paths;
+			Slice<String> paths;
 			String name;
 		} LibraryName;
 		i32 Nil;
@@ -219,7 +220,7 @@ bool entity_has_deferred_procedure(Entity *e) {
 gb_global u64 global_entity_id = 0;
 
 Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
-	gbAllocator a = heap_allocator();
+	gbAllocator a = permanent_allocator();
 	Entity *entity = gb_alloc_item(a, Entity);
 	entity->kind   = kind;
 	entity->state  = EntityState_Unresolved;
@@ -332,7 +333,7 @@ Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type,
 }
 
 Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
-                                  Array<String> paths, String name) {
+                                  Slice<String> paths, String name) {
 	Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
 	entity->LibraryName.paths = paths;
 	entity->LibraryName.name = name;

+ 59 - 51
src/exact_value.cpp

@@ -46,16 +46,16 @@ enum ExactValueKind {
 struct ExactValue {
 	ExactValueKind kind;
 	union {
-		bool          value_bool;
-		String        value_string;
-		BigInt        value_integer; // NOTE(bill): This must be an integer and not a pointer
-		f64           value_float;
-		i64           value_pointer;
-		Complex128    value_complex;
-		Quaternion256 value_quaternion;
-		Ast *         value_compound;
-		Ast *         value_procedure;
-		Type *        value_typeid;
+		bool           value_bool;
+		String         value_string;
+		BigInt         value_integer; // NOTE(bill): This must be an integer and not a pointer
+		f64            value_float;
+		i64            value_pointer;
+		Complex128    *value_complex;
+		Quaternion256 *value_quaternion;
+		Ast *          value_compound;
+		Ast *          value_procedure;
+		Type *         value_typeid;
 	};
 };
 
@@ -85,9 +85,9 @@ HashKey hash_exact_value(ExactValue v) {
 	case ExactValue_Pointer:
 		return hash_integer(v.value_pointer);
 	case ExactValue_Complex:
-		return hashing_proc(&v.value_complex, gb_size_of(Complex128));
+		return hashing_proc(v.value_complex, gb_size_of(Complex128));
 	case ExactValue_Quaternion:
-		return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256));
+		return hashing_proc(v.value_quaternion, gb_size_of(Quaternion256));
 	case ExactValue_Compound:
 		return hash_pointer(v.value_compound);
 	case ExactValue_Procedure:
@@ -139,17 +139,19 @@ ExactValue exact_value_float(f64 f) {
 
 ExactValue exact_value_complex(f64 real, f64 imag) {
 	ExactValue result = {ExactValue_Complex};
-	result.value_complex.real = real;
-	result.value_complex.imag = imag;
+	result.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
+	result.value_complex->real = real;
+	result.value_complex->imag = imag;
 	return result;
 }
 
 ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
 	ExactValue result = {ExactValue_Quaternion};
-	result.value_quaternion.real = real;
-	result.value_quaternion.imag = imag;
-	result.value_quaternion.jmag = jmag;
-	result.value_quaternion.kmag = kmag;
+	result.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
+	result.value_quaternion->real = real;
+	result.value_quaternion->imag = imag;
+	result.value_quaternion->jmag = jmag;
+	result.value_quaternion->kmag = kmag;
 	return result;
 }
 
@@ -373,6 +375,7 @@ ExactValue exact_value_to_complex(ExactValue v) {
 		// return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag);
 	}
 	ExactValue r = {ExactValue_Invalid};
+	v.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
 	return r;
 }
 ExactValue exact_value_to_quaternion(ExactValue v) {
@@ -382,11 +385,12 @@ ExactValue exact_value_to_quaternion(ExactValue v) {
 	case ExactValue_Float:
 		return exact_value_quaternion(v.value_float, 0, 0, 0);
 	case ExactValue_Complex:
-		return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0);
+		return exact_value_quaternion(v.value_complex->real, v.value_complex->imag, 0, 0);
 	case ExactValue_Quaternion:
 		return v;
 	}
 	ExactValue r = {ExactValue_Invalid};
+	v.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
 	return r;
 }
 
@@ -396,9 +400,9 @@ ExactValue exact_value_real(ExactValue v) {
 	case ExactValue_Float:
 		return v;
 	case ExactValue_Complex:
-		return exact_value_float(v.value_complex.real);
+		return exact_value_float(v.value_complex->real);
 	case ExactValue_Quaternion:
-		return exact_value_float(v.value_quaternion.real);
+		return exact_value_float(v.value_quaternion->real);
 	}
 	ExactValue r = {ExactValue_Invalid};
 	return r;
@@ -410,9 +414,9 @@ ExactValue exact_value_imag(ExactValue v) {
 	case ExactValue_Float:
 		return exact_value_i64(0);
 	case ExactValue_Complex:
-		return exact_value_float(v.value_complex.imag);
+		return exact_value_float(v.value_complex->imag);
 	case ExactValue_Quaternion:
-		return exact_value_float(v.value_quaternion.imag);
+		return exact_value_float(v.value_quaternion->imag);
 	}
 	ExactValue r = {ExactValue_Invalid};
 	return r;
@@ -425,7 +429,7 @@ ExactValue exact_value_jmag(ExactValue v) {
 	case ExactValue_Complex:
 		return exact_value_i64(0);
 	case ExactValue_Quaternion:
-		return exact_value_float(v.value_quaternion.jmag);
+		return exact_value_float(v.value_quaternion->jmag);
 	}
 	ExactValue r = {ExactValue_Invalid};
 	return r;
@@ -438,7 +442,7 @@ ExactValue exact_value_kmag(ExactValue v) {
 	case ExactValue_Complex:
 		return exact_value_i64(0);
 	case ExactValue_Quaternion:
-		return exact_value_float(v.value_quaternion.kmag);
+		return exact_value_float(v.value_quaternion->kmag);
 	}
 	ExactValue r = {ExactValue_Invalid};
 	return r;
@@ -532,15 +536,15 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision,
 			return i;
 		}
 		case ExactValue_Complex: {
-			f64 real = v.value_complex.real;
-			f64 imag = v.value_complex.imag;
+			f64 real = v.value_complex->real;
+			f64 imag = v.value_complex->imag;
 			return exact_value_complex(-real, -imag);
 		}
 		case ExactValue_Quaternion: {
-			f64 real = v.value_quaternion.real;
-			f64 imag = v.value_quaternion.imag;
-			f64 jmag = v.value_quaternion.jmag;
-			f64 kmag = v.value_quaternion.kmag;
+			f64 real = v.value_quaternion->real;
+			f64 imag = v.value_quaternion->imag;
+			f64 jmag = v.value_quaternion->jmag;
+			f64 kmag = v.value_quaternion->kmag;
 			return exact_value_quaternion(-real, -imag, -jmag, -kmag);
 		}
 		}
@@ -685,6 +689,8 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 		case Token_CmpOr:  return exact_value_bool(x.value_bool || y.value_bool);
 		case Token_And:    return exact_value_bool(x.value_bool & y.value_bool);
 		case Token_Or:     return exact_value_bool(x.value_bool | y.value_bool);
+		case Token_AndNot: return exact_value_bool(x.value_bool & !y.value_bool);
+		case Token_Xor:    return exact_value_bool((x.value_bool && !y.value_bool) || (!x.value_bool && y.value_bool));
 		default: goto error;
 		}
 		break;
@@ -730,10 +736,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 
 	case ExactValue_Complex: {
 		y = exact_value_to_complex(y);
-		f64 a = x.value_complex.real;
-		f64 b = x.value_complex.imag;
-		f64 c = y.value_complex.real;
-		f64 d = y.value_complex.imag;
+		f64 a = x.value_complex->real;
+		f64 b = x.value_complex->imag;
+		f64 c = y.value_complex->real;
+		f64 d = y.value_complex->imag;
 		f64 real = 0;
 		f64 imag = 0;
 		switch (op) {
@@ -763,14 +769,14 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 
 	case ExactValue_Quaternion: {
 		y = exact_value_to_quaternion(y);
-		f64 xr = x.value_quaternion.real;
-		f64 xi = x.value_quaternion.imag;
-		f64 xj = x.value_quaternion.jmag;
-		f64 xk = x.value_quaternion.kmag;
-		f64 yr = y.value_quaternion.real;
-		f64 yi = y.value_quaternion.imag;
-		f64 yj = y.value_quaternion.jmag;
-		f64 yk = y.value_quaternion.kmag;
+		f64 xr = x.value_quaternion->real;
+		f64 xi = x.value_quaternion->imag;
+		f64 xj = x.value_quaternion->jmag;
+		f64 xk = x.value_quaternion->kmag;
+		f64 yr = y.value_quaternion->real;
+		f64 yi = y.value_quaternion->imag;
+		f64 yj = y.value_quaternion->jmag;
+		f64 yk = y.value_quaternion->kmag;
 
 
 		f64 real = 0;
@@ -897,10 +903,10 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 	}
 
 	case ExactValue_Complex: {
-		f64 a = x.value_complex.real;
-		f64 b = x.value_complex.imag;
-		f64 c = y.value_complex.real;
-		f64 d = y.value_complex.imag;
+		f64 a = x.value_complex->real;
+		f64 b = x.value_complex->imag;
+		f64 c = y.value_complex->real;
+		f64 d = y.value_complex->imag;
 		switch (op) {
 		case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
 		case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
@@ -945,7 +951,7 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 Entity *strip_entity_wrapping(Ast *expr);
 Entity *strip_entity_wrapping(Entity *e);
 
-gbString write_expr_to_string(gbString str, Ast *node);
+gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
 
 gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) {
 	switch (v.kind) {
@@ -976,14 +982,16 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st
 	case ExactValue_Float:
 		return gb_string_append_fmt(str, "%f", v.value_float);
 	case ExactValue_Complex:
-		return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag);
+		return gb_string_append_fmt(str, "%f+%fi", v.value_complex->real, v.value_complex->imag);
+	case ExactValue_Quaternion:
+		return gb_string_append_fmt(str, "%f+%fi+%fj+%fk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag);
 
 	case ExactValue_Pointer:
 		return str;
 	case ExactValue_Compound:
-		return write_expr_to_string(str, v.value_compound);
+		return write_expr_to_string(str, v.value_compound, false);
 	case ExactValue_Procedure:
-		return write_expr_to_string(str, v.value_procedure);
+		return write_expr_to_string(str, v.value_procedure, false);
 	}
 	return str;
 };

+ 101 - 4
src/gb/gb.h

@@ -157,7 +157,7 @@ extern "C" {
 	#endif
 #endif
 
-#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__)
+#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__)
 	#ifndef GB_ARCH_64_BIT
 	#define GB_ARCH_64_BIT 1
 	#endif
@@ -230,7 +230,7 @@ extern "C" {
 	#define GB_CACHE_LINE_SIZE 128
 	#endif
 
-#elif defined(__arm__)
+#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
 	#ifndef GB_CPU_ARM
 	#define GB_CPU_ARM 1
 	#endif
@@ -3702,6 +3702,12 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
 
 	void *dest_copy = dest;
 	__asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory");
+#elif defined(GB_CPU_ARM)
+	u8 *s = cast(u8 *)source;
+	u8 *d = cast(u8 *)dest;
+	for (isize i = 0; i < n; i++) {
+		*d++ = *s++;
+	}
 #else
 	u8 *d = cast(u8 *)dest;
 	u8 const *s = cast(u8 const *)source;
@@ -4438,6 +4444,76 @@ gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
 #endif
 }
 
+#elif defined(GB_CPU_ARM)
+
+gb_inline i32  gb_atomic32_load (gbAtomic32 const volatile *a) { 
+	return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
+}
+gb_inline void gb_atomic32_store(gbAtomic32 volatile *a, i32 value) { 
+	__atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired) {
+	i32 expected_copy = expected;
+	auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+	if (result) {
+		return expected;
+	} else {
+		return expected_copy;
+	}
+}
+
+gb_inline i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) {
+	return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) {
+	return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) {
+	return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) {
+	return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_load(gbAtomic64 const volatile *a) {
+	return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
+}
+
+gb_inline void gb_atomic64_store(gbAtomic64 volatile *a, i64 value) {
+	__atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired) {
+	i64 expected_copy = expected;
+	auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+	if (result) {
+		return expected;
+	} else {
+		return expected_copy;
+	}
+}
+
+gb_inline i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) {
+	return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) {
+	return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) {
+	return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
+	return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST);
+}
+
+
 #else
 #error TODO(bill): Implement Atomics for this CPU
 #endif
@@ -4563,7 +4639,11 @@ gb_inline void gb_yield_thread(void) {
 #if defined(GB_SYSTEM_WINDOWS)
 	_mm_pause();
 #elif defined(GB_SYSTEM_OSX)
+	#if defined(GB_CPU_X86)
 	__asm__ volatile ("" : : : "memory");
+	#elif defined(GB_CPU_ARM)
+	__asm__ volatile ("yield" : : : "memory");
+	#endif
 #elif defined(GB_CPU_X86)
 	_mm_pause();
 #else
@@ -4575,7 +4655,11 @@ gb_inline void gb_mfence(void) {
 #if defined(GB_SYSTEM_WINDOWS)
 	_ReadWriteBarrier();
 #elif defined(GB_SYSTEM_OSX)
+	#if defined(GB_CPU_X86)
 	__sync_synchronize();
+	#elif defined(GB_CPU_ARM)
+	__atomic_thread_fence(__ATOMIC_SEQ_CST);
+	#endif
 #elif defined(GB_CPU_X86)
 	_mm_mfence();
 #else
@@ -4587,7 +4671,12 @@ gb_inline void gb_sfence(void) {
 #if defined(GB_SYSTEM_WINDOWS)
 	_WriteBarrier();
 #elif defined(GB_SYSTEM_OSX)
+	#if defined(GB_CPU_X86)
 	__asm__ volatile ("" : : : "memory");
+	#elif defined(GB_CPU_ARM)
+	// TODO(bill): is this correct?
+	__atomic_thread_fence(__ATOMIC_SEQ_CST);
+	#endif
 #elif defined(GB_CPU_X86)
 	_mm_sfence();
 #else
@@ -5156,7 +5245,7 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
 
 	index = core * a->threads_per_core + thread_index;
 	thread = pthread_self();
-	
+
 
 	cpuset_t mn;
 	CPU_ZERO(&mn);
@@ -5202,7 +5291,7 @@ void gb_affinity_init(gbAffinity *a) {
 		for (;;) {
 			// The 'temporary char'. Everything goes into this char,
 			// so that we can check against EOF at the end of this loop.
-			char c;
+			int c;
 
 #define AF__CHECK(letter) ((c = getc(cpu_info)) == letter)
 			if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') &&
@@ -8808,6 +8897,14 @@ gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name)
 
 		return result;
 	}
+#elif defined(__aarch64__)
+	gb_inline u64 gb_rdtsc(void) {
+		int64_t virtual_timer_value;
+ 		asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
+ 		return virtual_timer_value;
+	}
+#else 
+#error "gb_rdtsc not supported"
 #endif
 
 #if defined(GB_SYSTEM_WINDOWS)

+ 413 - 107
src/ir.cpp

@@ -25,6 +25,9 @@ struct irModule {
 	Map<irDebugInfo *>    debug_info;          // Key: Unique pointer
 	Map<irValue *>        anonymous_proc_lits; // Key: Ast *
 
+	Map<irValue *>        equal_procs;  // Key: Type *
+	Map<irValue *>        hasher_procs; // Key: Type *
+
 	irDebugInfo *         debug_compile_unit;
 	Array<irDebugInfo *>  debug_location_stack;
 
@@ -161,6 +164,7 @@ struct irProcedure {
 	Ast *    return_ptr_hint_ast;
 	bool     return_ptr_hint_used;
 
+	bool ignore_dead_instr;
 
 	Array<irBranchBlocks> branch_blocks;
 
@@ -454,7 +458,6 @@ struct irValueSourceCodeLocation {
 	irValue *line;
 	irValue *column;
 	irValue *procedure;
-	u64      hash;
 };
 
 
@@ -525,6 +528,11 @@ struct irAddr {
 
 Type *ir_type(irValue *value);
 irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr);
+void ir_begin_procedure_body(irProcedure *proc);
+void ir_end_procedure_body(irProcedure *proc);
+irValue *ir_get_equal_proc_for_type(irModule *m, Type *type);
+irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type);
+
 
 irAddr ir_addr(irValue *addr) {
 	irAddr v = {irAddr_Default, addr};
@@ -1159,7 +1167,7 @@ irValue *ir_instr_atomic_cxchg(irProcedure *p, Type *type, irValue *address, irV
 		GB_ASSERT(type->Tuple.variables.count == 2);
 		Type *elem = type->Tuple.variables[0]->type;
 		// LEAK TODO(bill): LLVM returns {T, i1} whilst Odin does {T, bool}, fix this mapping hack
-		gbAllocator a = heap_allocator();
+		gbAllocator a = permanent_allocator();
 		Type *llvm_type = alloc_type_tuple();
 		array_init(&llvm_type->Tuple.variables, a, 0, 2);
 		array_add (&llvm_type->Tuple.variables, alloc_entity_field(nullptr, blank_token, elem, false, 0));
@@ -1799,7 +1807,7 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
 	if (zero_initialized) {
 		ir_emit_zero_init(proc, instr, expr);
 	}
-	set_procedure_abi_types(heap_allocator(), e->type);
+	set_procedure_abi_types(e->type);
 
 	// if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
 	// if (proc->module->generate_debug_info && proc->entity != nullptr) {
@@ -2132,7 +2140,7 @@ irDebugInfo *ir_add_debug_info_field(irModule *module, irDebugInfo *scope, Entit
 				if (e->token.string.len == 0) {
 					// If no name available for field, use its field index as its name.
 					isize max_len = 8;
-					u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len);
+					u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len);
 					isize len = gb_snprintf(cast(char *)str, 8, "%d", index);
 					di->DerivedType.name = make_string(str, len-1);
 				}
@@ -3282,7 +3290,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
 		context_ptr = ir_find_or_generate_context_ptr(p);
 	}
 
-	set_procedure_abi_types(heap_allocator(), pt);
+	set_procedure_abi_types(pt);
 
 	bool is_c_vararg = pt->Proc.c_vararg;
 	isize param_count = pt->Proc.param_count;
@@ -3293,7 +3301,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
 		GB_ASSERT_MSG(param_count == args.count, "%.*s %td == %td", LIT(p->entity->token.string), param_count, args.count);
 	}
 
-	auto processed_args = array_make<irValue *>(heap_allocator(), 0, args.count);
+	auto processed_args = array_make<irValue *>(permanent_allocator(), 0, args.count);
 
 	for (isize i = 0; i < param_count; i++) {
 		Entity *e = pt->Proc.params->Tuple.variables[i];
@@ -3416,7 +3424,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
 			case DeferredProcedure_in_out:
 				{
 					auto out_args = ir_value_to_array(p, result);
-					array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count);
+					array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count);
 					array_copy(&result_as_args, in_args, 0);
 					array_copy(&result_as_args, out_args, in_args.count);
 				}
@@ -3587,65 +3595,69 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty
 	irValue *m = ir_emit_conv(proc, map_val_ptr, type_deref(ir_type(gep0)));
 	ir_emit_store(proc, gep0, m);
 
-	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_const_bool(is_type_string(key_type)));
 
 	i64 entry_size   = type_size_of  (map_type->Map.entry_type);
 	i64 entry_align  = type_align_of (map_type->Map.entry_type);
-	i64 value_offset = type_offset_of(map_type->Map.entry_type, 2);
+	i64 key_offset   = type_offset_of(map_type->Map.entry_type, 2);
+	i64 key_size     = type_size_of  (map_type->Map.key);
+	i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
 	i64 value_size   = type_size_of  (map_type->Map.value);
 
+	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_get_equal_proc_for_type(proc->module, key_type));
 	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 2), ir_const_int(entry_size));
 	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 3), ir_const_int(entry_align));
-	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(value_offset));
-	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(value_size));
+	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(key_offset));
+	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(key_size));
+	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 6), ir_const_uintptr(value_offset));
+	ir_emit_store(proc, ir_emit_struct_ep(proc, h, 7), ir_const_int(value_size));
 
 	return ir_emit_load(proc, h);
 }
 
-irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) {
-	Type *hash_type = t_u64;
-	irValue *v = ir_add_local_generated(proc, t_map_key, true);
-	Type *t = base_type(ir_type(key));
-	key = ir_emit_conv(proc, key, key_type);
-
-	if (is_type_string(t)) {
-		irValue *str = ir_emit_conv(proc, key, t_string);
-		irValue *hashed_str = nullptr;
+irValue *ir_const_hash(irModule *m, irValue *key, Type *key_type) {
+	irValue *hashed_key = nullptr;
 
-		if (str->kind == irValue_Constant) {
-			ExactValue ev = str->Constant.value;
-			GB_ASSERT(ev.kind == ExactValue_String);
-			u64 hs = fnv64a(ev.value_string.text, ev.value_string.len);
-			hashed_str = ir_value_constant(t_u64, exact_value_u64(hs));
+	if (key->kind == irValue_Constant) {
+		u64 hash = 0xcbf29ce484222325;
+		if (is_type_string(key_type)) {
+			GB_ASSERT(key->Constant.value.kind == ExactValue_String);
+			String s = key->Constant.value.value_string;
+			hash = fnv64a(s.text, s.len);
 		} else {
-			auto args = array_make<irValue *>(ir_allocator(), 1);
-			args[0] = str;
-			hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args);
+			return nullptr;
 		}
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str);
+		// TODO(bill): other const hash types
 
-		irValue *key_data = ir_emit_struct_ep(proc, v, 1);
-		key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
-		ir_emit_store(proc, key_data, str);
-	} else {
-		i64 sz = type_size_of(t);
-		GB_ASSERT(sz <= 8);
-		if (sz != 0) {
-			auto args = array_make<irValue *>(ir_allocator(), 2);
-			args[0] = ir_address_from_load_or_generate_local(proc, key);
-			args[1] = ir_const_int(sz);
-			irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args);
+		if (build_context.word_size == 4) {
+			hash &= 0xffffffffull;
+		}
+		hashed_key = ir_const_uintptr(hash);
+	}
 
+	return hashed_key;
+}
 
-			irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0);
-			irValue *key_data = ir_emit_struct_ep(proc, v, 1);
-			key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
+irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) {
+	Type *hash_type = t_u64;
+	irValue *v = ir_add_local_generated(proc, t_map_hash, true);
+	Type *t = base_type(ir_type(key));
+	key = ir_emit_conv(proc, key, key_type);
 
-			ir_emit_store(proc, hash_ptr, hash);
-			ir_emit_store(proc, key_data, key);
-		}
+	irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key);
+	key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr);
+
+	irValue *hashed_key = ir_const_hash(proc->module, key, key_type);
+	if (hashed_key == nullptr) {
+		irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type);
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		args[0] = key_ptr;
+		args[1] = ir_value_constant(t_uintptr, exact_value_i64(0));
+		hashed_key = ir_emit_call(proc, hasher, args);
 	}
 
+	ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_key);
+	ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), key_ptr);
+
 	return ir_emit_load(proc, v);
 }
 
@@ -3701,7 +3713,7 @@ irValue *ir_insert_dynamic_map_key_and_value(irProcedure *proc, irValue *addr, T
 	map_type = base_type(map_type);
 
 	irValue *h = ir_gen_map_header(proc, addr, map_type);
-	irValue *key = ir_gen_map_key(proc, map_key, map_type->Map.key);
+	irValue *key = ir_gen_map_hash(proc, map_key, map_type->Map.key);
 	irValue *v = ir_emit_conv(proc, map_value, map_type->Map.value);
 
 	irValue *ptr = ir_add_local_generated(proc, ir_type(v), false);
@@ -4058,7 +4070,7 @@ irValue *ir_addr_load(irProcedure *proc, irAddr const &addr) {
 		Type *map_type = base_type(addr.map_type);
 		irValue *v = ir_add_local_generated(proc, map_type->Map.lookup_result_type, true);
 		irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
-		irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key);
+		irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key);
 
 		auto args = array_make<irValue *>(ir_allocator(), 2);
 		args[0] = h;
@@ -4226,7 +4238,7 @@ irValue *ir_addr_get_ptr(irProcedure *proc, irAddr const &addr, bool allow_refer
 		if (allow_reference) {
 			Type *map_type = base_type(addr.map_type);
 			irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
-			irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key);
+			irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key);
 
 			auto args = array_make<irValue *>(ir_allocator(), 2);
 			args[0] = h;
@@ -4537,7 +4549,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
 		Type *ft = base_complex_elem_type(t_left);
 
 		if (op == Token_Quo) {
-			auto args = array_make<irValue *>(heap_allocator(), 2);
+			auto args = array_make<irValue *>(permanent_allocator(), 2);
 			args[0] = left;
 			args[1] = right;
 
@@ -4615,7 +4627,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
 
 			return ir_emit_load(proc, res);
 		} else if (op == Token_Mul) {
-			auto args = array_make<irValue *>(heap_allocator(), 2);
+			auto args = array_make<irValue *>(permanent_allocator(), 2);
 			args[0] = left;
 			args[1] = right;
 
@@ -4625,7 +4637,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
 			default: GB_PANIC("Unknown float type"); break;
 			}
 		} else if (op == Token_Quo) {
-			auto args = array_make<irValue *>(heap_allocator(), 2);
+			auto args = array_make<irValue *>(permanent_allocator(), 2);
 			args[0] = left;
 			args[1] = right;
 
@@ -4828,7 +4840,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
 		irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0));
 		return ir_emit_comp(proc, op_kind, x, invalid_typeid);
 	} else if (is_type_bit_field(t)) {
-		auto args = array_make<irValue *>(heap_allocator(), 2);
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
 		irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
 		args[0] = ir_emit_conv(proc, lhs, t_rawptr);
 		args[1] = ir_const_int(type_size_of(t));
@@ -4848,7 +4860,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
 			return ir_emit_comp(proc, op_kind, cap, v_zero);
 		}
 	} else if (is_type_struct(t) && type_has_nil(t)) {
-		auto args = array_make<irValue *>(heap_allocator(), 2);
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
 		irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
 		args[0] = ir_emit_conv(proc, lhs, t_rawptr);
 		args[1] = ir_const_int(type_size_of(t));
@@ -4859,6 +4871,244 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
 	return nullptr;
 }
 
+irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) {
+	Type *original_type = type;
+	type = base_type(type);
+	Type *pt = alloc_type_pointer(type);
+
+	auto key = hash_type(type);
+	irValue **found = map_get(&m->equal_procs, key);
+	if (found) {
+		return *found;
+	}
+
+	static u32 proc_index = 0;
+
+	char buf[16] = {};
+	isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+
+	Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
+	Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_equal_proc, 0);
+	e->Procedure.link_name = proc_name;
+	irValue *p = ir_value_procedure(m, e, t_equal_proc, nullptr, body, proc_name);
+	map_set(&m->values, hash_entity(e), p);
+	string_map_set(&m->members, proc_name, p);
+	map_set(&m->equal_procs, key, p);
+
+	irProcedure *proc = &p->Proc;
+	proc->is_startup = true;
+	proc->ignore_dead_instr = true;
+	ir_begin_procedure_body(proc);
+	// ir_start_block(proc, proc->decl_block);
+	GB_ASSERT(proc->curr_block != nullptr);
+
+	irValue *x = proc->params[0];
+	irValue *y = proc->params[1];
+	irValue *lhs = ir_emit_conv(proc, x, pt);
+	irValue *rhs = ir_emit_conv(proc, y, pt);
+
+	irBlock *block_same_ptr = ir_new_block(proc, nullptr, "same_ptr");
+	irBlock *block_diff_ptr = ir_new_block(proc, nullptr, "diff_ptr");
+
+	irValue *same_ptr = ir_emit_comp(proc, Token_CmpEq, lhs, rhs);
+	ir_emit_if(proc, same_ptr, block_same_ptr, block_diff_ptr);
+	ir_start_block(proc, block_same_ptr);
+	ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
+
+	ir_start_block(proc, block_diff_ptr);
+
+	if (type->kind == Type_Struct) {
+		type_set_offsets(type);
+
+		irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later
+
+		irBlock *block_false = ir_new_block(proc, nullptr, "bfalse");
+
+		for_array(i, type->Struct.fields) {
+			irBlock *next_block = ir_new_block(proc, nullptr, "btrue");
+
+			irValue *pleft  = ir_emit_struct_ep(proc, lhs, cast(i32)i);
+			irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i);
+			irValue *left = ir_emit_load(proc, pleft);
+			irValue *right = ir_emit_load(proc, pright);
+			irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
+
+			ir_emit_if(proc, ok, next_block, block_false);
+
+			ir_emit_jump(proc, next_block);
+			ir_start_block(proc, next_block);
+		}
+
+		ir_emit_jump(proc, done);
+		ir_start_block(proc, block_false);
+
+		ir_emit(proc, ir_instr_return(proc, ir_const_bool(false)));
+
+		ir_emit_jump(proc, done);
+		ir_start_block(proc, done);
+		ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
+	} else {
+		irValue *left = ir_emit_load(proc, lhs);
+		irValue *right = ir_emit_load(proc, rhs);
+		irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
+		ok = ir_emit_conv(proc, ok, t_bool);
+		ir_emit(proc, ir_instr_return(proc, ok));
+	}
+
+	ir_end_procedure_body(proc);
+
+	return p;
+}
+
+
+irValue *ir_simple_compare_hash(irProcedure *p, Type *type, irValue *data, irValue *seed) {
+	GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
+
+	i64 sz = type_size_of(type);
+	if (1 <= sz && sz <= 16) {
+		char name[20] = {};
+		gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
+
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		args[0] = data;
+		args[1] = seed;
+		return ir_emit_runtime_call(p, name, args);
+	}
+
+	auto args = array_make<irValue *>(permanent_allocator(), 3);
+	args[0] = data;
+	args[1] = seed;
+	args[2] = ir_const_int(type_size_of(type));
+	return ir_emit_runtime_call(p, "default_hasher_n", args);
+}
+
+irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) {
+	Type *original_type = type;
+	type = core_type(type);
+	Type *pt = alloc_type_pointer(type);
+
+	GB_ASSERT(is_type_valid_for_keys(type));
+
+	auto key = hash_type(type);
+	irValue **found = map_get(&m->hasher_procs, key);
+	if (found) {
+		return *found;
+	}
+
+	static u32 proc_index = 0;
+
+	char buf[16] = {};
+	isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+
+	Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
+	Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_hasher_proc, 0);
+	e->Procedure.link_name = proc_name;
+	irValue *p = ir_value_procedure(m, e, t_hasher_proc, nullptr, body, proc_name);
+	map_set(&m->values, hash_entity(e), p);
+	string_map_set(&m->members, proc_name, p);
+	map_set(&m->hasher_procs, key, p);
+
+	irProcedure *proc = &p->Proc;
+	proc->is_startup = true;
+	proc->ignore_dead_instr = true;
+	ir_begin_procedure_body(proc);
+	defer (ir_end_procedure_body(proc));
+
+	// ir_start_block(proc, proc->decl_block);
+	GB_ASSERT(proc->curr_block != nullptr);
+
+	irValue *data = proc->params[0];
+	irValue *seed = proc->params[1];
+
+	if (is_type_simple_compare(type)) {
+		irValue *res = ir_simple_compare_hash(proc, type, data, seed);
+		ir_emit(proc, ir_instr_return(proc, res));
+		return p;
+	}
+
+	if (is_type_cstring(type)) {
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		args[0] = data;
+		args[1] = seed;
+		irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args);
+		ir_emit(proc, ir_instr_return(proc, res));
+	} else if (is_type_string(type)) {
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		args[0] = data;
+		args[1] = seed;
+		irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args);
+		ir_emit(proc, ir_instr_return(proc, res));
+	} else if (type->kind == Type_Struct) {
+		type_set_offsets(type);
+		data = ir_emit_conv(proc, data, t_u8_ptr);
+
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		for_array(i, type->Struct.fields) {
+			i64 offset = type->Struct.offsets[i];
+			Entity *field = type->Struct.fields[i];
+			irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type);
+			irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset));
+
+			args[0] = ptr;
+			args[1] = seed;
+			seed = ir_emit_call(proc, field_hasher, args);
+		}
+		ir_emit(proc, ir_instr_return(proc, seed));
+	} else if (type->kind == Type_Array) {
+		irValue *pres = ir_add_local_generated(proc, t_uintptr, false);
+		ir_emit_store(proc, pres, seed);
+
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem);
+
+		auto loop_data = ir_loop_start(proc, type->Array.count, t_i32);
+
+		data = ir_emit_conv(proc, data, pt);
+
+		irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx);
+		args[0] = ptr;
+		args[1] = ir_emit_load(proc, pres);
+		irValue *new_seed = ir_emit_call(proc, elem_hasher, args);
+		ir_emit_store(proc, pres, new_seed);
+
+		ir_loop_end(proc, loop_data);
+
+		irValue *res = ir_emit_load(proc, pres);
+		ir_emit(proc, ir_instr_return(proc, res));
+	} else if (type->kind == Type_EnumeratedArray) {
+		irValue *pres = ir_add_local_generated(proc, t_uintptr, false);
+		ir_emit_store(proc, pres, seed);
+
+		auto args = array_make<irValue *>(permanent_allocator(), 2);
+		irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem);
+
+		auto loop_data = ir_loop_start(proc, type->Array.count, t_i32);
+
+		data = ir_emit_conv(proc, data, pt);
+
+		irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx);
+		args[0] = ptr;
+		args[1] = ir_emit_load(proc, pres);
+		irValue *new_seed = ir_emit_call(proc, elem_hasher, args);
+		ir_emit_store(proc, pres, new_seed);
+
+		ir_loop_end(proc, loop_data);
+
+		irValue *res = ir_emit_load(proc, pres);
+		ir_emit(proc, ir_instr_return(proc, res));
+	} else {
+		GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
+	}
+
+	return p;
+}
+
 irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) {
 	Type *a = base_type(ir_type(left));
 	Type *b = base_type(ir_type(right));
@@ -4966,7 +5216,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
 		} 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<irValue *>(heap_allocator(), 3);
+				auto args = array_make<irValue *>(permanent_allocator(), 3);
 				args[0] = ir_emit_conv(proc, lhs, t_rawptr);
 				args[1] = ir_emit_conv(proc, rhs, t_rawptr);
 				args[2] = ir_const_int(type_size_of(tl));
@@ -4992,6 +5242,30 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
 		}
 	}
 
+	if (is_type_struct(a) && is_type_comparable(a)) {
+		irValue *left_ptr  = ir_address_from_load_or_generate_local(proc, left);
+		irValue *right_ptr = ir_address_from_load_or_generate_local(proc, right);
+		irValue *res = {};
+		if (is_type_simple_compare(a)) {
+			// TODO(bill): Test to see if this is actually faster!!!!
+			auto args = array_make<irValue *>(permanent_allocator(), 3);
+			args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
+			args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
+			args[2] = ir_const_int(type_size_of(a));
+			res = ir_emit_runtime_call(proc, "memory_equal", args);
+		} else {
+			irValue *value = ir_get_equal_proc_for_type(proc->module, a);
+			auto args = array_make<irValue *>(permanent_allocator(), 2);
+			args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
+			args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
+			res = ir_emit_call(proc, value, args);
+		}
+		if (op_kind == Token_NotEq) {
+			res = ir_emit_unary_arith(proc, Token_Not, res, ir_type(res));
+		}
+		return res;
+	}
+
 	if (is_type_string(a)) {
 		if (is_type_cstring(a)) {
 			left  = ir_emit_conv(proc, left, t_string);
@@ -6636,7 +6910,7 @@ void ir_mangle_add_sub_type_name(irModule *m, Entity *field, String parent) {
 		return;
 	}
 	if (is_type_proc(field->type)) {
-		set_procedure_abi_types(heap_allocator(), field->type);
+		set_procedure_abi_types(field->type);
 	}
 
 	String cn = field->token.string;
@@ -6733,7 +7007,7 @@ irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, i
 	String name = make_string(name_text, name_len-1);
 
 	Type *type = type_of_expr(expr);
-	set_procedure_abi_types(heap_allocator(), type);
+	set_procedure_abi_types(type);
 	irValue *value = ir_value_procedure(m, nullptr, type, pl->type, pl->body, name);
 
 	value->Proc.tags = pl->tags;
@@ -6789,6 +7063,9 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
 	if (!ir_min_dep_entity(m, e)) {
 		return;
 	}
+	if (is_type_proc(e->type)) {
+		return;
+	}
 	irValue *t = ir_value_type_name(name, e->type);
 	ir_module_add_value(m, e, t);
 	string_map_set(&m->members, name, t);
@@ -6884,7 +7161,7 @@ irValue *ir_find_global_variable(irProcedure *proc, String name) {
 	return *value;
 }
 
-void ir_build_stmt_list(irProcedure *proc, Array<Ast *> stmts);
+void ir_build_stmt_list(irProcedure *proc, Slice<Ast *> stmts);
 void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op);
 
 bool is_double_pointer(Type *t) {
@@ -6898,17 +7175,6 @@ bool is_double_pointer(Type *t) {
 	return is_type_pointer(td);
 }
 
-
-u64 ir_generate_source_code_location_hash(TokenPos pos) {
-	u64 h = 0xcbf29ce484222325;
-	for (isize i = 0; i < pos.file.len; i++) {
-		h = (h ^ u64(pos.file[i])) * 0x100000001b3;
-	}
-	h = h ^ (u64(pos.line) * 0x100000001b3);
-	h = h ^ (u64(pos.column) * 0x100000001b3);
-	return h;
-}
-
 irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, TokenPos pos) {
 	gbAllocator a = ir_allocator();
 	irValue *v = ir_alloc_value(irValue_SourceCodeLocation);
@@ -6916,7 +7182,6 @@ irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, Token
 	v->SourceCodeLocation.line      = ir_const_int(pos.line);
 	v->SourceCodeLocation.column    = ir_const_int(pos.column);
 	v->SourceCodeLocation.procedure = ir_find_or_add_entity_string(proc->module, procedure);
-	v->SourceCodeLocation.hash      = ir_generate_source_code_location_hash(pos);
 	return v;
 }
 
@@ -7355,7 +7620,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 	// "Intrinsics"
 	case BuiltinProc_alloca:
 		{
-			auto args = array_make<irValue *>(heap_allocator(), 2);
+			auto args = array_make<irValue *>(permanent_allocator(), 2);
 			args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32);
 			args[1] = ir_build_expr(proc, ce->args[1]);
 			return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr));
@@ -7459,7 +7724,11 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 		return ir_emit(proc, ir_instr_atomic_cxchg(proc, type, address, old_value, new_value, id));
 	}
 
+	case BuiltinProc_type_equal_proc:
+		return ir_get_equal_proc_for_type(proc->module, ce->args[0]->tav.type);
 
+	case BuiltinProc_type_hasher_proc:
+		return ir_get_hasher_proc_for_type(proc->module, ce->args[0]->tav.type);
 	}
 
 	GB_PANIC("Unhandled built-in procedure");
@@ -7584,7 +7853,7 @@ irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) {
 	Type *proc_type_ = base_type(ir_type(value));
 	GB_ASSERT(proc_type_->kind == Type_Proc);
 	TypeProc *pt = &proc_type_->Proc;
-	set_procedure_abi_types(heap_allocator(), proc_type_);
+	set_procedure_abi_types(proc_type_);
 
 	if (is_call_expr_field_value(ce)) {
 		auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
@@ -7801,7 +8070,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 
 	if (tv.value.kind != ExactValue_Invalid) {
 		// NOTE(bill): Edge case
-		if (tv.value.kind != ExactValue_Compound &&
+		if (is_type_u8_array(tv.type) && tv.value.kind == ExactValue_String) {
+			return ir_add_module_constant(proc->module, tv.type, tv.value);
+		} else if (is_type_rune_array(tv.type) && tv.value.kind == ExactValue_String) {
+			return ir_add_module_constant(proc->module, tv.type, tv.value);
+		} else if (tv.value.kind != ExactValue_Compound &&
 		    is_type_array(tv.type)) {
 			Type *elem = core_array_type(tv.type);
 			ExactValue value = convert_exact_value_for_type(tv.value, elem);
@@ -8207,7 +8480,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 
 					irValue *addr = ir_address_from_load_or_generate_local(proc, right);
 					irValue *h = ir_gen_map_header(proc, addr, rt);
-					irValue *key = ir_gen_map_key(proc, left, rt->Map.key);
+					irValue *key = ir_gen_map_hash(proc, left, rt->Map.key);
 
 					auto args = array_make<irValue *>(ir_allocator(), 2);
 					args[0] = h;
@@ -9024,8 +9297,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
 			if (cl->elems.count > 0) {
 				ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
 
-				auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
-				defer (array_free(&temp_data));
+				auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
 
 				// NOTE(bill): Separate value, gep, store into their own chunks
 				for_array(i, cl->elems) {
@@ -9123,8 +9395,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
 			if (cl->elems.count > 0) {
 				ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
 
-				auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
-				defer (array_free(&temp_data));
+				auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
 
 				// NOTE(bill): Separate value, gep, store into their own chunks
 				for_array(i, cl->elems) {
@@ -9232,8 +9503,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
 
 				irValue *data = ir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
 
-				auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
-				defer (array_free(&temp_data));
+				auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
 
 				for_array(i, cl->elems) {
 					Ast *elem = cl->elems[i];
@@ -9574,7 +9844,7 @@ void ir_build_nested_proc(irProcedure *proc, AstProcLit *pd, Entity *e) {
 	name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
 	String name = make_string(name_text, name_len-1);
 
-	set_procedure_abi_types(heap_allocator(), e->type);
+	set_procedure_abi_types(e->type);
 	irValue *value = ir_value_procedure(proc->module, e, e->type, pd->type, pd->body, name);
 
 	value->Proc.tags = pd->tags;
@@ -9673,7 +9943,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) {
 					return;
 				}
 
-				set_procedure_abi_types(heap_allocator(), e->type);
+				set_procedure_abi_types(e->type);
 				irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name);
 
 				value->Proc.tags = pl->tags;
@@ -9692,7 +9962,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) {
 	}
 }
 
-void ir_build_stmt_list(irProcedure *proc, Array<Ast *> stmts) {
+void ir_build_stmt_list(irProcedure *proc, Slice<Ast *> stmts) {
 	// NOTE(bill): Precollect constant entities
 	for_array(i, stmts) {
 		Ast *stmt = stmts[i];
@@ -9844,13 +10114,8 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, ir
 		elem = ir_emit_load(proc, elem);
 
 		irValue *entry = ir_emit_ptr_offset(proc, elem, idx);
-		val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
-
-		irValue *key_raw = ir_emit_struct_ep(proc, entry, 0);
-		key_raw = ir_emit_struct_ep(proc, key_raw, 1);
-		irValue *key = ir_emit_conv(proc, key_raw, alloc_type_pointer(expr_type->Map.key));
-
-		idx = ir_emit_load(proc, key);
+		idx = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
+		val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 3));
 
 		break;
 	}
@@ -9995,7 +10260,7 @@ void ir_build_range_enum(irProcedure *proc, Type *enum_type, Type *val_type, irV
 	irValue *max_count = ir_const_int(enum_count);
 
 	irValue *ti          = ir_type_info(proc, t);
-	irValue *variant     = ir_emit_struct_ep(proc, ti, 3);
+	irValue *variant     = ir_emit_struct_ep(proc, ti, 4);
 	irValue *eti_ptr     = ir_emit_conv(proc, variant, t_type_info_enum_ptr);
 	irValue *values      = ir_emit_load(proc, ir_emit_struct_ep(proc, eti_ptr, 2));
 	irValue *values_data = ir_slice_elem(proc, values);
@@ -10179,7 +10444,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
 
 					String mangled_name = {};
 					{
-						gbString str = gb_string_make_length(heap_allocator(), proc->name.text, proc->name.len);
+						gbString str = gb_string_make_length(permanent_allocator(), proc->name.text, proc->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;
@@ -10902,7 +11167,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
 
 		ast_node(body, BlockStmt, ss->body);
 
-		Array<Ast *> default_stmts = {};
+		Slice<Ast *> default_stmts = {};
 		irBlock *default_fall = nullptr;
 		irBlock *default_block = nullptr;
 
@@ -11349,6 +11614,9 @@ void ir_begin_procedure_body(irProcedure *proc) {
 
 
 bool ir_remove_dead_instr(irProcedure *proc) {
+	if (proc->ignore_dead_instr) {
+		return false;
+	}
 	isize elimination_count = 0;
 retry:
 #if 1
@@ -11471,11 +11739,11 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) {
 void ir_build_proc(irValue *value, irProcedure *parent) {
 	irProcedure *proc = &value->Proc;
 
-	set_procedure_abi_types(heap_allocator(), proc->type);
+	set_procedure_abi_types(proc->type);
 
 	proc->parent = parent;
 
-	if (proc->body != nullptr) {
+	if (proc->body != nullptr && proc->body->kind != Ast_Invalid) {
 		u64 prev_state_flags = proc->module->state_flags;
 
 		if (proc->tags != 0) {
@@ -11577,6 +11845,8 @@ void ir_init_module(irModule *m, Checker *c) {
 	map_init(&m->debug_info,               heap_allocator());
 	map_init(&m->entity_names,             heap_allocator());
 	map_init(&m->anonymous_proc_lits,      heap_allocator());
+	map_init(&m->equal_procs,              heap_allocator());
+	map_init(&m->hasher_procs,             heap_allocator());
 	array_init(&m->procs,                  heap_allocator());
 	array_init(&m->procs_to_generate,      heap_allocator());
 	array_init(&m->foreign_library_paths,  heap_allocator());
@@ -11860,6 +12130,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 	// Useful types
 	Type *t_i64_slice_ptr    = alloc_type_pointer(alloc_type_slice(t_i64));
 	Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
+	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;
 
 	i32 type_info_member_types_index = 0;
 	i32 type_info_member_names_index = 0;
@@ -11879,11 +12151,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 
 		irValue *tag = nullptr;
 		irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, cast(i32)entry_index);
-		irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 3);
+		irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 4);
+
+		irValue *type_info_flags = ir_value_constant(t_type_info_flags, exact_value_i64(type_info_flags_of_type(t)));
 
 		ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 0), ir_const_int(type_size_of(t)));
 		ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 1), ir_const_int(type_align_of(t)));
-		ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), ir_typeid(proc->module, t));
+		ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), type_info_flags);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 3), ir_typeid(proc->module, t));
 
 
 		switch (t->kind) {
@@ -11897,6 +12172,21 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 
 			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name);
 			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip);
+
+			if (t->Named.type_name->pkg) {
+				irValue *name = ir_const_string(proc->module, t->Named.type_name->pkg->name);
+				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), name);
+			}
+
+			String proc_name = {};
+			if (t->Named.type_name->parent_proc_decl) {
+				DeclInfo *decl = t->Named.type_name->parent_proc_decl;
+				if (decl->entity && decl->entity->kind == Entity_Procedure) {
+					proc_name = decl->entity->token.string;
+				}
+			}
+			irValue *loc = ir_emit_source_code_location(proc, proc_name, t->Named.type_name->token.pos);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), loc);
 			break;
 		}
 
@@ -12234,8 +12524,13 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union);
 				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align);
 
+				if (is_type_comparable(t) && !is_type_simple_compare(t)) {
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 8), ir_get_equal_proc_for_type(proc->module, t));
+				}
+
+
 				if (t->Struct.soa_kind != StructSoa_None) {
-					irValue *kind = ir_emit_struct_ep(proc, tag, 8);
+					irValue *kind = ir_emit_struct_ep(proc, tag, 9);
 					Type *kind_type = type_deref(ir_type(kind));
 
 					irValue *soa_kind = ir_value_constant(kind_type, exact_value_i64(t->Struct.soa_kind));
@@ -12244,8 +12539,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 
 
 					ir_emit_store(proc, kind, soa_kind);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 9), soa_type);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_len);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_type);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 11), soa_len);
 				}
 			}
 
@@ -12308,10 +12603,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 			irValue *key              = ir_emit_struct_ep(proc, tag, 0);
 			irValue *value            = ir_emit_struct_ep(proc, tag, 1);
 			irValue *generated_struct = ir_emit_struct_ep(proc, tag, 2);
+			irValue *key_equal        = ir_emit_struct_ep(proc, tag, 3);
+			irValue *key_hasher       = ir_emit_struct_ep(proc, tag, 4);
 
 			ir_emit_store(proc, key,              ir_get_type_info_ptr(proc, t->Map.key));
 			ir_emit_store(proc, value,            ir_get_type_info_ptr(proc, t->Map.value));
 			ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, t->Map.generated_struct_type));
+			ir_emit_store(proc, key_equal,        ir_get_equal_proc_for_type(proc->module, t->Map.key));
+			ir_emit_store(proc, key_hasher,       ir_get_hasher_proc_for_type(proc->module, t->Map.key));
 			break;
 		}
 
@@ -12612,7 +12911,7 @@ void ir_gen_tree(irGen *s) {
 
 			Ast *type_expr = pl->type;
 
-			set_procedure_abi_types(heap_allocator(), e->type);
+			set_procedure_abi_types(e->type);
 			irValue *p = ir_value_procedure(m, e, e->type, type_expr, body, name);
 			p->Proc.tags = pl->tags;
 			p->Proc.inlining = pl->inlining;
@@ -12646,7 +12945,7 @@ void ir_gen_tree(irGen *s) {
 
 
 #if defined(GB_SYSTEM_WINDOWS)
-	if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) {
+	if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main && !build_context.no_entry_point) {
 		// DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32
 		String name = str_lit("DllMain");
 		Type *proc_params = alloc_type_tuple();
@@ -12717,7 +13016,7 @@ void ir_gen_tree(irGen *s) {
 		ir_emit_return(proc, v_one32);
 	}
 #endif
-	if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
+	if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) && !build_context.no_entry_point) {
 		// main :: proc(argc: i32, argv: ^^u8) -> i32
 		String name = str_lit("main");
 
@@ -12784,11 +13083,18 @@ void ir_gen_tree(irGen *s) {
 		ir_fill_slice(proc, global_args, argv, ir_emit_conv(proc, argc, t_int));
 
 		ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime));
-		{
+		Array<irValue *> empty_args = {};
+		if (build_context.command_kind == Command_test) {
+			for_array(i, m->info->testing_procedures) {
+				Entity *e = m->info->testing_procedures[i];
+				irValue **found = map_get(&proc->module->values, hash_entity(e));
+				GB_ASSERT(found != nullptr);
+				ir_emit_call(proc, *found, empty_args);
+			}
+		} else {
 			irValue **found = map_get(&proc->module->values, hash_entity(entry_point));
 			if (found != nullptr) {
-				Array<irValue *> args = {};
-				ir_emit_call(proc, *found, args);
+				ir_emit_call(proc, *found, empty_args);
 			}
 		}
 
@@ -12796,7 +13102,7 @@ void ir_gen_tree(irGen *s) {
 	}
 
 #if defined(GB_SYSTEM_WINDOWS)
-	if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt) {
+	if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt && !build_context.no_entry_point) {
 		s->print_chkstk = true;
 
 		{

+ 45 - 20
src/ir_print.cpp

@@ -76,7 +76,6 @@ void ir_write_u64(irFileBuffer *f, u64 i) {
 }
 void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) {
 	if (x.len == 2) {
-		gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
 		u64 words[2] = {};
 		BigInt y = x;
 		if (swap_endian) {
@@ -88,9 +87,8 @@ void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_en
 			y.d.words = words;
 		}
 
-		String s = big_int_to_string(a, &y, 10);
+		String s = big_int_to_string(temporary_allocator(), &y, 10);
 		ir_write_string(f, s);
-		gb_free(a, s.text);
 	} else {
 		i64 i = 0;
 		if (x.neg) {
@@ -296,7 +294,7 @@ void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) {
 
 
 void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
-	set_procedure_abi_types(heap_allocator(), t);
+	set_procedure_abi_types(t);
 
 	GB_ASSERT(is_type_proc(t));
 	t = base_type(t);
@@ -325,7 +323,7 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
 
 
 void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
-	set_procedure_abi_types(heap_allocator(), t);
+	set_procedure_abi_types(t);
 
 	i64 word_bits = 8*build_context.word_size;
 	t = base_type(t);
@@ -736,6 +734,28 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 	if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
 		i64 count  = type->Array.count;
 		Type *elem = type->Array.elem;
+
+		if (is_type_rune_array(type)) {
+			Rune rune;
+			isize offset = 0;
+			isize width = 1;
+			String s = value.value_string;
+			ir_write_byte(f, '[');
+			for (i64 i = 0; i < count && offset < s.len; i++) {
+				width = gb_utf8_decode(s.text+offset, s.len-offset, &rune);
+				if (i > 0) ir_write_str_lit(f, ", ");
+				ir_print_type(f, m, elem);
+				ir_write_byte(f, ' ');
+				ir_print_exact_value(f, m, exact_value_i64(rune), elem);
+				offset += width;
+			}
+			GB_ASSERT(offset == s.len);
+
+			ir_write_byte(f, ']');
+			return;
+		}
+
+
 		ir_write_byte(f, '[');
 
 		for (i64 i = 0; i < count; i++) {
@@ -747,7 +767,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 
 		ir_write_byte(f, ']');
 		return;
-	} else if (is_type_array(type) &&
+	}  else if (is_type_array(type) &&
 	    value.kind != ExactValue_Invalid &&
 	    value.kind != ExactValue_String &&
 	    value.kind != ExactValue_Compound) {
@@ -798,7 +818,11 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 			GB_ASSERT(is_type_array(type));
 			ir_write_str_lit(f, "c\"");
 			ir_print_escape_string(f, str, false, false);
-			ir_write_str_lit(f, "\\00\"");
+			if (type->Array.count == str.len) {
+				ir_write_str_lit(f, "\"");
+			} else {
+				ir_write_str_lit(f, "\\00\"");
+			}
 		} else if (is_type_cstring(t)) {
 			// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
 			// of the .ll file
@@ -812,7 +836,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 			ir_write_str_lit(f, ", ");
 			ir_print_type(f, m, t_i32);
 			ir_write_str_lit(f, " 0, i32 0)");
-		}else {
+		} else {
 			// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
 			// of the .ll file
 			irValue *str_array = ir_add_global_string_array(m, str);
@@ -929,9 +953,9 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 		ir_write_byte(f, ' ');
 		ir_write_byte(f, '{');
 		ir_print_type(f, m, ft); ir_write_byte(f, ' ');
-		ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
+		ir_print_exact_value(f, m, exact_value_float(value.value_complex->real), ft);
 		ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
-		ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
+		ir_print_exact_value(f, m, exact_value_float(value.value_complex->imag), ft);
 		ir_write_byte(f, '}');
 		break;
 	}
@@ -944,13 +968,13 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 		ir_write_byte(f, ' ');
 		ir_write_byte(f, '{');
 		ir_print_type(f, m, ft); ir_write_byte(f, ' ');
-		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->imag), ft);
 		ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
-		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->jmag), ft);
 		ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
-		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->kmag), ft);
 		ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
-		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->real), ft);
 		ir_write_byte(f, '}');
 		break;
 	}
@@ -1406,7 +1430,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
 		irValue *line      = value->SourceCodeLocation.line;
 		irValue *column    = value->SourceCodeLocation.column;
 		irValue *procedure = value->SourceCodeLocation.procedure;
-		u64      hash      = value->SourceCodeLocation.hash;
 
 		ir_write_byte(f, '{');
 		ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, file, t_string);
@@ -1416,8 +1439,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
 		ir_print_type(f, m, t_int);    ir_write_byte(f, ' '); ir_print_value(f, m, column, t_int);
 		ir_write_string(f, str_lit(", "));
 		ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, procedure, t_string);
-		ir_write_string(f, str_lit(", "));
-		ir_print_type(f, m, t_u64);    ir_write_byte(f, ' '); ir_write_u64(f, hash);
 		ir_write_byte(f, '}');
 		break;
 	}
@@ -1551,7 +1572,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 				break;
 
 			case BuiltinProc_cpu_relax:
-				ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+				if (build_context.metrics.arch == TargetArch_amd64) {
+					ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+				} else {
+					// ir_write_str_lit(f, "call void asm sideeffect \"yield\", \"\"()");
+				}
 				break;
 			default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
 			}
@@ -2189,7 +2214,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		irInstrCall *call = &instr->Call;
 		Type *proc_type = base_type(ir_type(call->value));
 		GB_ASSERT(is_type_proc(proc_type));
-		set_procedure_abi_types(heap_allocator(), proc_type);
+		set_procedure_abi_types(proc_type);
 
 		bool is_c_vararg = proc_type->Proc.c_vararg;
 		Type *result_type = call->type;
@@ -2396,7 +2421,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 
 
 void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
-	set_procedure_abi_types(heap_allocator(), proc->type);
+	set_procedure_abi_types(proc->type);
 
 	if (proc->body == nullptr) {
 		ir_write_str_lit(f, "declare ");

+ 959 - 0
src/llvm_abi.cpp

@@ -0,0 +1,959 @@
+enum lbArgKind {
+	lbArg_Direct,
+	lbArg_Indirect,
+	lbArg_Ignore,
+};
+
+struct lbArgType {
+	lbArgKind kind;
+	LLVMTypeRef type;
+	LLVMTypeRef cast_type;      // Optional
+	LLVMTypeRef pad_type;       // Optional
+	LLVMAttributeRef attribute; // Optional
+};
+
+lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
+	return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr};
+}
+lbArgType lb_arg_type_direct(LLVMTypeRef type) {
+	return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
+}
+
+lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
+	return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr};
+}
+
+lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
+	return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr};
+}
+
+struct lbFunctionType {
+	LLVMContextRef   ctx;
+	ProcCallingConvention calling_convention;
+	Array<lbArgType> args;
+	lbArgType        ret;
+};
+
+i64 llvm_align_formula(i64 off, i64 a) {
+	return (off + a - 1) / a * a;
+}
+
+
+bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) {
+	if (type == nullptr) {
+		return false;
+	}
+	return LLVMGetTypeKind(type) == kind;
+}
+
+LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) {
+	unsigned arg_count = cast(unsigned)ft->args.count;
+	unsigned offset = 0;
+
+	LLVMTypeRef ret = nullptr;
+	if (ft->ret.kind == lbArg_Direct) {
+		if (ft->ret.cast_type != nullptr) {
+			ret = ft->ret.cast_type;
+		} else {
+			ret = ft->ret.type;
+		}
+	} else if (ft->ret.kind == lbArg_Indirect) {
+		offset += 1;
+		ret = LLVMVoidTypeInContext(ft->ctx);
+	} else if (ft->ret.kind == lbArg_Ignore) {
+		ret = LLVMVoidTypeInContext(ft->ctx);
+	}
+	GB_ASSERT_MSG(ret != nullptr, "%d", ft->ret.kind);
+
+	unsigned maximum_arg_count = offset+arg_count;
+	LLVMTypeRef *args = gb_alloc_array(heap_allocator(), LLVMTypeRef, maximum_arg_count);
+	if (offset == 1) {
+		GB_ASSERT(ft->ret.kind == lbArg_Indirect);
+		args[0] = LLVMPointerType(ft->ret.type, 0);
+	}
+
+	unsigned arg_index = offset;
+	for (unsigned i = 0; i < arg_count; i++) {
+		lbArgType *arg = &ft->args[i];
+		if (arg->kind == lbArg_Direct) {
+			LLVMTypeRef arg_type = nullptr;
+			if (ft->args[i].cast_type != nullptr) {
+				arg_type = arg->cast_type;
+			} else {
+				arg_type = arg->type;
+			}
+			args[arg_index++] = arg_type;
+		} else if (arg->kind == lbArg_Indirect) {
+			GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
+			args[arg_index++] = LLVMPointerType(arg->type, 0);
+		} else if (arg->kind == lbArg_Ignore) {
+			// ignore
+		}
+	}
+	unsigned total_arg_count = arg_index;
+	LLVMTypeRef func_type = LLVMFunctionType(ret, args, total_arg_count, is_var_arg);
+	return LLVMPointerType(func_type, 0);
+}
+
+
+void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) {
+	if (ft == nullptr) {
+		return;
+	}
+	unsigned arg_count = cast(unsigned)ft->args.count;
+	unsigned offset = 0;
+	if (ft->ret.kind == lbArg_Indirect) {
+		offset += 1;
+	}
+
+	LLVMContextRef c = ft->ctx;
+	LLVMAttributeRef noalias_attr   = lb_create_enum_attribute(c, "noalias", true);
+	LLVMAttributeRef nonnull_attr   = lb_create_enum_attribute(c, "nonnull", true);
+	LLVMAttributeRef nocapture_attr = lb_create_enum_attribute(c, "nocapture", true);
+
+	unsigned arg_index = offset;
+	for (unsigned i = 0; i < arg_count; i++) {
+		lbArgType *arg = &ft->args[i];
+		if (arg->kind == lbArg_Ignore) {
+			continue;
+		}
+
+		if (arg->attribute) {
+			LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute);
+		}
+
+		arg_index++;
+	}
+
+	if (offset != 0 && ft->ret.kind == lbArg_Indirect && ft->ret.attribute != nullptr) {
+		LLVMAddAttributeAtIndex(fn, offset, ft->ret.attribute);
+		LLVMAddAttributeAtIndex(fn, offset, noalias_attr);
+	}
+
+	lbCallingConventionKind cc_kind = lbCallingConvention_C;
+	// TODO(bill): Clean up this logic
+	if (build_context.metrics.os != TargetOs_js)  {
+		cc_kind = lb_calling_convention_map[calling_convention];
+	}
+	LLVMSetFunctionCallConv(fn, cc_kind);
+	if (calling_convention == ProcCC_Odin) {
+		unsigned context_index = offset+arg_count;
+		LLVMAddAttributeAtIndex(fn, context_index, noalias_attr);
+		LLVMAddAttributeAtIndex(fn, context_index, nonnull_attr);
+		LLVMAddAttributeAtIndex(fn, context_index, nocapture_attr);
+	}
+
+}
+
+i64 lb_sizeof(LLVMTypeRef type);
+i64 lb_alignof(LLVMTypeRef type);
+
+i64 lb_sizeof(LLVMTypeRef type) {
+	LLVMTypeKind kind = LLVMGetTypeKind(type);
+	switch (kind) {
+	case LLVMVoidTypeKind:
+		return 0;
+	case LLVMIntegerTypeKind:
+		{
+			unsigned w = LLVMGetIntTypeWidth(type);
+			return (w + 7)/8;
+		}
+	case LLVMFloatTypeKind:
+		return 4;
+	case LLVMDoubleTypeKind:
+		return 8;
+	case LLVMPointerTypeKind:
+		return build_context.word_size;
+	case LLVMStructTypeKind:
+		{
+			unsigned field_count = LLVMCountStructElementTypes(type);
+			i64 offset = 0;
+			if (LLVMIsPackedStruct(type)) {
+				for (unsigned i = 0; i < field_count; i++) {
+					LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
+					offset += lb_sizeof(field);
+				}
+			} else {
+				for (unsigned i = 0; i < field_count; i++) {
+					LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
+					i64 align = lb_alignof(field);
+					offset = llvm_align_formula(offset, align);
+					offset += lb_sizeof(field);
+				}
+				offset = llvm_align_formula(offset, lb_alignof(type));
+			}
+			return offset;
+		}
+		break;
+	case LLVMArrayTypeKind:
+		{
+			LLVMTypeRef elem = LLVMGetElementType(type);
+			i64 elem_size = lb_sizeof(elem);
+			i64 count = LLVMGetArrayLength(type);
+			i64 size = count * elem_size;
+			return size;
+		}
+		break;
+
+	case LLVMX86_MMXTypeKind:
+		return 8;
+	case LLVMVectorTypeKind:
+		{
+			LLVMTypeRef elem = LLVMGetElementType(type);
+			i64 elem_size = lb_sizeof(elem);
+			i64 count = LLVMGetVectorSize(type);
+			i64 size = count * elem_size;
+			return gb_clamp(next_pow2(size), 1, build_context.max_align);
+		}
+
+	}
+	GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type));
+
+	return 0;
+}
+
+i64 lb_alignof(LLVMTypeRef type) {
+	LLVMTypeKind kind = LLVMGetTypeKind(type);
+	switch (kind) {
+	case LLVMVoidTypeKind:
+		return 1;
+	case LLVMIntegerTypeKind:
+		{
+			unsigned w = LLVMGetIntTypeWidth(type);
+			return gb_clamp((w + 7)/8, 1, build_context.max_align);
+		}
+	case LLVMFloatTypeKind:
+		return 4;
+	case LLVMDoubleTypeKind:
+		return 8;
+	case LLVMPointerTypeKind:
+		return build_context.word_size;
+	case LLVMStructTypeKind:
+		{
+			if (LLVMIsPackedStruct(type)) {
+				return 1;
+			} else {
+				unsigned field_count = LLVMCountStructElementTypes(type);
+				i64 max_align = 1;
+				for (unsigned i = 0; i < field_count; i++) {
+					LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
+					i64 field_align = lb_alignof(field);
+					max_align = gb_max(max_align, field_align);
+				}
+				return max_align;
+			}
+		}
+		break;
+	case LLVMArrayTypeKind:
+		return lb_alignof(LLVMGetElementType(type));
+
+	case LLVMX86_MMXTypeKind:
+		return 8;
+	case LLVMVectorTypeKind:
+		{
+			LLVMTypeRef elem = LLVMGetElementType(type);
+			i64 elem_size = lb_sizeof(elem);
+			i64 count = LLVMGetVectorSize(type);
+			i64 size = count * elem_size;
+			return gb_clamp(next_pow2(size), 1, build_context.max_align);
+		}
+
+	}
+	GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type));
+
+	// LLVMValueRef v = LLVMAlignOf(type);
+	// GB_ASSERT(LLVMIsConstant(v));
+	// return LLVMConstIntGetSExtValue(v);
+	return 1;
+}
+
+#if 0
+Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) {
+	Type **found = map_get(&m->llvm_types, hash_pointer(type));
+	if (found) {
+		return *found;
+	}
+	GB_ASSERT_MSG(level < 64, "%s %d", LLVMPrintTypeToString(type), is_return);
+
+	LLVMTypeKind kind = LLVMGetTypeKind(type);
+	switch (kind) {
+	case LLVMVoidTypeKind:
+		return nullptr;
+	case LLVMIntegerTypeKind:
+		{
+			unsigned w = LLVMGetIntTypeWidth(type);
+			if (w == 1) {
+				return t_llvm_bool;
+			}
+			unsigned bytes = (w + 7)/8;
+			switch (bytes) {
+			case 1:  return t_u8;
+			case 2:  return t_u16;
+			case 4:  return t_u32;
+			case 8:  return t_u64;
+			case 16: return t_u128;
+			}
+			GB_PANIC("Unhandled integer type");
+		}
+	case LLVMFloatTypeKind:
+		return t_f32;
+	case LLVMDoubleTypeKind:
+		return t_f64;
+	case LLVMPointerTypeKind:
+		{
+			LLVMTypeRef elem = LLVMGetElementType(type);
+			if (lb_is_type_kind(elem, LLVMFunctionTypeKind)) {
+				unsigned param_count = LLVMCountParamTypes(elem);
+				LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count);
+				defer (gb_free(heap_allocator(), params));
+				LLVMGetParamTypes(elem, params);
+
+				Type **param_types = gb_alloc_array(heap_allocator(), Type *, param_count);
+				defer (gb_free(heap_allocator(), param_types));
+
+				for (unsigned i = 0; i < param_count; i++) {
+					param_types[i] = lb_abi_to_odin_type(m, params[i], false, level+1);
+				}
+
+				LLVMTypeRef ret = LLVMGetReturnType(elem);
+				Type *ret_type = lb_abi_to_odin_type(m, ret, true, level+1);
+
+				bool is_c_vararg = !!LLVMIsFunctionVarArg(elem);
+				return alloc_type_proc_from_types(param_types, param_count, ret_type, is_c_vararg);
+			}
+			return alloc_type_pointer(lb_abi_to_odin_type(m, elem, false, level+1));
+		}
+	case LLVMFunctionTypeKind:
+		GB_PANIC("LLVMFunctionTypeKind should not be seen on its own");
+		break;
+
+	case LLVMStructTypeKind:
+		{
+			unsigned field_count = LLVMCountStructElementTypes(type);
+			Type **fields = gb_alloc_array(heap_allocator(), Type *, field_count);
+			for (unsigned i = 0; i < field_count; i++) {
+				LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(type, i);
+				if (lb_is_type_kind(field_type, LLVMPointerTypeKind) && level > 0) {
+					fields[i] = t_rawptr;
+				} else {
+					fields[i] = lb_abi_to_odin_type(m, field_type, false, level+1);
+				}
+			}
+			if (is_return) {
+				return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type), false);
+			} else {
+				return alloc_type_struct_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type));
+			}
+		}
+		break;
+	case LLVMArrayTypeKind:
+		{
+
+			i64 count = LLVMGetArrayLength(type);
+			Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1);
+			return alloc_type_array(elem, count);
+		}
+		break;
+
+	case LLVMX86_MMXTypeKind:
+		return t_vector_x86_mmx;
+	case LLVMVectorTypeKind:
+		{
+			i64 count = LLVMGetVectorSize(type);
+			Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1);
+			return alloc_type_simd_vector(count, elem);
+		}
+
+	}
+	GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type));
+
+	return 0;
+}
+#endif
+
+
+#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention)
+typedef LB_ABI_INFO(lbAbiInfoType);
+
+
+// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
+namespace lbAbi386 {
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+		ft->ctx = c;
+		ft->args = compute_arg_types(c, arg_types, arg_count);
+		ft->ret = compute_return_type(c, return_type, return_is_defined);
+		ft->calling_convention = calling_convention;
+		return ft;
+	}
+
+	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+		if (!is_return && lb_sizeof(type) > 8) {
+			return lb_arg_type_indirect(type, nullptr);
+		}
+
+		if (build_context.metrics.os == TargetOs_windows &&
+		    build_context.word_size == 8 &&
+		    lb_is_type_kind(type, LLVMIntegerTypeKind) &&
+		    type == LLVMIntTypeInContext(c, 128)) {
+		    	// NOTE(bill): Because Windows AMD64 is weird
+			LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+			return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
+		}
+
+		LLVMAttributeRef attr = nullptr;
+		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+		if (type == i1) {
+			attr = lb_create_enum_attribute(c, "zeroext", true);
+		}
+		return lb_arg_type_direct(type, nullptr, nullptr, attr);
+	}
+
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+		for (unsigned i = 0; i < arg_count; i++) {
+			LLVMTypeRef t = arg_types[i];
+			LLVMTypeKind kind = LLVMGetTypeKind(t);
+			i64 sz = lb_sizeof(t);
+			if (kind == LLVMStructTypeKind) {
+				if (sz == 0) {
+					args[i] = lb_arg_type_ignore(t);
+				} else {
+					args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true));
+				}
+			} else {
+				args[i] = non_struct(c, t, false);
+			}
+		}
+		return args;
+	}
+
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+		if (!return_is_defined) {
+			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
+			i64 sz = lb_sizeof(return_type);
+			switch (sz) {
+			case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c,  8), nullptr, nullptr);
+			case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+			}
+			return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true));
+		}
+		return non_struct(c, return_type, true);
+	}
+};
+
+namespace lbAbiAmd64Win64 {
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+
+
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+		ft->ctx = c;
+		ft->args = compute_arg_types(c, arg_types, arg_count);
+		ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+		ft->calling_convention = calling_convention;
+		return ft;
+	}
+
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+		for (unsigned i = 0; i < arg_count; i++) {
+			LLVMTypeRef t = arg_types[i];
+			LLVMTypeKind kind = LLVMGetTypeKind(t);
+			if (kind == LLVMStructTypeKind) {
+				i64 sz = lb_sizeof(t);
+				switch (sz) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					args[i] = lb_arg_type_direct(t, LLVMIntTypeInContext(c, 8*cast(unsigned)sz), nullptr, nullptr);
+					break;
+				default:
+					args[i] = lb_arg_type_indirect(t, nullptr);
+					break;
+				}
+			} else {
+				args[i] = lbAbi386::non_struct(c, t, false);
+			}
+		}
+		return args;
+	}
+};
+
+// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
+namespace lbAbiAmd64SysV {
+	enum RegClass {
+		RegClass_NoClass,
+		RegClass_Int,
+		RegClass_SSEFs,
+		RegClass_SSEFv,
+		RegClass_SSEDs,
+		RegClass_SSEDv,
+		RegClass_SSEInt8,
+		RegClass_SSEInt16,
+		RegClass_SSEInt32,
+		RegClass_SSEInt64,
+		RegClass_SSEUp,
+		RegClass_X87,
+		RegClass_X87Up,
+		RegClass_ComplexX87,
+		RegClass_Memory,
+	};
+
+	bool is_sse(RegClass reg_class) {
+		switch (reg_class) {
+		case RegClass_SSEFs:
+		case RegClass_SSEFv:
+		case RegClass_SSEDv:
+			return true;
+		}
+		return false;
+	}
+
+	void all_mem(Array<RegClass> *cs) {
+		for_array(i, *cs) {
+			(*cs)[i] = RegClass_Memory;
+		}
+	}
+
+	enum Amd64TypeAttributeKind {
+		Amd64TypeAttribute_None,
+		Amd64TypeAttribute_ByVal,
+		Amd64TypeAttribute_StructRect,
+	};
+
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+	void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
+	void fixup(LLVMTypeRef t, Array<RegClass> *cls);
+	lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind);
+	Array<RegClass> classify(LLVMTypeRef t);
+	LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
+
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+		ft->ctx = c;
+		ft->calling_convention = calling_convention;
+
+		ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+		for (unsigned i = 0; i < arg_count; i++) {
+			ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal);
+		}
+
+		if (return_is_defined) {
+			ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect);
+		} else {
+			ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		}
+
+		return ft;
+	}
+
+	bool is_mem_cls(Array<RegClass> const &cls, Amd64TypeAttributeKind attribute_kind) {
+		if (attribute_kind == Amd64TypeAttribute_ByVal) {
+			if (cls.count == 0) {
+				return false;
+			}
+			auto first = cls[0];
+			return first == RegClass_Memory || first == RegClass_X87 || first == RegClass_ComplexX87;
+		} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
+			if (cls.count == 0) {
+				return false;
+			}
+			return cls[0] == RegClass_Memory;
+		}
+		return false;
+	}
+
+	bool is_register(LLVMTypeRef type) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMIntegerTypeKind:
+		case LLVMFloatTypeKind:
+		case LLVMDoubleTypeKind:
+		case LLVMPointerTypeKind:
+			return true;
+		}
+		return false;
+	}
+
+	lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) {
+		if (is_register(type)) {
+			LLVMAttributeRef attribute = nullptr;
+			if (type == LLVMInt1TypeInContext(c)) {
+				attribute = lb_create_enum_attribute(c, "zeroext", true);
+			}
+			return lb_arg_type_direct(type, nullptr, nullptr, attribute);
+		}
+
+		auto cls = classify(type);
+		if (is_mem_cls(cls, attribute_kind)) {
+			LLVMAttributeRef attribute = nullptr;
+			if (attribute_kind == Amd64TypeAttribute_ByVal) {
+				attribute = lb_create_enum_attribute(c, "byval", true);
+			} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
+				attribute = lb_create_enum_attribute(c, "sret", true);
+			}
+			return lb_arg_type_indirect(type, attribute);
+		} else {
+			return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr);
+		}
+	}
+
+	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+		LLVMAttributeRef attr = nullptr;
+		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+		if (type == i1) {
+			attr = lb_create_enum_attribute(c, "zeroext", true);
+		}
+		return lb_arg_type_direct(type, nullptr, nullptr, attr);
+	}
+
+	Array<RegClass> classify(LLVMTypeRef t) {
+		i64 sz = lb_sizeof(t);
+		i64 words = (sz + 7)/8;
+		auto reg_classes = array_make<RegClass>(heap_allocator(), cast(isize)words);
+		if (words > 4) {
+			all_mem(&reg_classes);
+		} else {
+			classify_with(t, &reg_classes, 0, 0);
+			fixup(t, &reg_classes);
+		}
+		return reg_classes;
+	}
+
+	void unify(Array<RegClass> *cls, i64 i, RegClass newv) {
+		RegClass &oldv = (*cls)[i];
+		if (oldv == newv) {
+			return;
+		} else if (oldv == RegClass_NoClass) {
+			oldv = newv;
+		} else if (newv == RegClass_NoClass) {
+			return;
+		} else if (oldv == RegClass_Memory || newv == RegClass_Memory) {
+			return;
+		} else if (oldv == RegClass_Int || newv	== RegClass_Int) {
+			return;
+		} else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 ||
+		           newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) {
+			oldv = RegClass_Memory;
+		} else {
+			oldv = newv;
+		}
+	}
+
+	void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
+		i64 i = 0;
+		i64 e = cls->count;
+		if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) {
+			RegClass &oldv = (*cls)[i];
+			if (is_sse(oldv)) {
+				for (i++; i < e; i++) {
+					if (oldv != RegClass_SSEUp) {
+						all_mem(cls);
+						return;
+					}
+				}
+			} else {
+				all_mem(cls);
+				return;
+			}
+		} else {
+			while (i < e) {
+				RegClass &oldv = (*cls)[i];
+				if (oldv == RegClass_Memory) {
+					all_mem(cls);
+					return;
+				} else if (oldv == RegClass_X87Up) {
+					// NOTE(bill): Darwin
+					all_mem(cls);
+					return;
+				} else if (oldv == RegClass_SSEUp) {
+					oldv = RegClass_SSEDv;
+				} else if (is_sse(oldv)) {
+					i++;
+					while (i != e && oldv == RegClass_SSEUp) {
+						i++;
+					}
+				} else if (oldv == RegClass_X87) {
+					i++;
+					while (i != e && oldv == RegClass_X87Up) {
+						i++;
+					}
+				} else {
+					i++;
+				}
+			}
+		}
+	}
+
+	unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) {
+		unsigned len = 1;
+		for (isize i = offset+1; i < reg_classes.count; i++) {
+			if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) {
+				break;
+			}
+			len++;
+		}
+		return len;
+	}
+
+
+	LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
+		auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
+		for_array(i, reg_classes) {
+			RegClass reg_class = reg_classes[i];
+			switch (reg_class) {
+			case RegClass_Int:
+				array_add(&types, LLVMIntTypeInContext(c, 64));
+				break;
+			case RegClass_SSEFv:
+			case RegClass_SSEDv:
+			case RegClass_SSEInt8:
+			case RegClass_SSEInt16:
+			case RegClass_SSEInt32:
+			case RegClass_SSEInt64:
+				{
+					unsigned elems_per_word = 0;
+					LLVMTypeRef elem_type = nullptr;
+					switch (reg_class) {
+					case RegClass_SSEFv:
+						elems_per_word = 2;
+						elem_type = LLVMFloatTypeInContext(c);
+						break;
+					case RegClass_SSEDv:
+						elems_per_word = 1;
+						elem_type = LLVMDoubleTypeInContext(c);
+						break;
+					case RegClass_SSEInt8:
+						elems_per_word = 64/8;
+						elem_type = LLVMIntTypeInContext(c, 8);
+						break;
+					case RegClass_SSEInt16:
+						elems_per_word = 64/16;
+						elem_type = LLVMIntTypeInContext(c, 16);
+						break;
+					case RegClass_SSEInt32:
+						elems_per_word = 64/32;
+						elem_type = LLVMIntTypeInContext(c, 32);
+						break;
+					case RegClass_SSEInt64:
+						elems_per_word = 64/64;
+						elem_type = LLVMIntTypeInContext(c, 64);
+						break;
+					}
+
+					unsigned vec_len = llvec_len(reg_classes, i);
+					LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
+					array_add(&types, vec_type);
+					i += vec_len;
+					continue;
+				}
+				break;
+			case RegClass_SSEFs:
+				array_add(&types, LLVMFloatTypeInContext(c));
+				break;
+			case RegClass_SSEDs:
+				array_add(&types, LLVMDoubleTypeInContext(c));
+				break;
+			default:
+				GB_PANIC("Unhandled RegClass");
+			}
+		}
+
+		GB_ASSERT(types.count != 0);
+		if (types.count == 1) {
+			return types[0];
+		}
+		return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
+	}
+
+	void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
+		i64 t_align = lb_alignof(t);
+		i64 t_size  = lb_sizeof(t);
+
+		i64 misalign = off % t_align;
+		if (misalign != 0) {
+			i64 e = (off + t_size + 7) / 8;
+			for (i64 i = off / 8; i < e; i++) {
+				unify(cls, ix+i, RegClass_Memory);
+			}
+			return;
+		}
+
+		switch (LLVMGetTypeKind(t)) {
+		case LLVMIntegerTypeKind:
+		case LLVMPointerTypeKind:
+			unify(cls, ix + off/8, RegClass_Int);
+			break;
+		case LLVMFloatTypeKind:
+			unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs);
+			break;
+		case LLVMDoubleTypeKind:
+			unify(cls, ix + off/8,  RegClass_SSEDs);
+			break;
+		case LLVMStructTypeKind:
+			{
+				LLVMBool packed = LLVMIsPackedStruct(t);
+				unsigned field_count = LLVMCountStructElementTypes(t);
+
+				i64 field_off = off;
+				for (unsigned field_index = 0; field_index < field_count; field_index++) {
+					LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(t, field_index);
+					if (!packed) {
+						field_off = llvm_align_formula(field_off, lb_alignof(field_type));
+					}
+					classify_with(field_type, cls, ix, field_off);
+					field_off += lb_sizeof(field_type);
+				}
+			}
+			break;
+		case LLVMArrayTypeKind:
+			{
+				i64 len = LLVMGetArrayLength(t);
+				LLVMTypeRef elem = LLVMGetElementType(t);
+				i64 elem_sz = lb_sizeof(elem);
+				for (i64 i = 0; i < len; i++) {
+					classify_with(elem, cls, ix, off + i*elem_sz);
+				}
+			}
+			break;
+		case LLVMVectorTypeKind:
+			{
+				i64 len = LLVMGetVectorSize(t);
+				LLVMTypeRef elem = LLVMGetElementType(t);
+				i64 elem_sz = lb_sizeof(elem);
+				LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
+				RegClass reg = RegClass_NoClass;
+				switch (elem_kind) {
+				case LLVMIntegerTypeKind:
+					switch (LLVMGetIntTypeWidth(elem)) {
+					case 8:  reg = RegClass_SSEInt8;
+					case 16: reg = RegClass_SSEInt16;
+					case 32: reg = RegClass_SSEInt32;
+					case 64: reg = RegClass_SSEInt64;
+					default:
+						GB_PANIC("Unhandled integer width for vector type");
+					}
+					break;
+				case LLVMFloatTypeKind:
+					reg = RegClass_SSEFv;
+					break;
+				case LLVMDoubleTypeKind:
+					reg = RegClass_SSEDv;
+					break;
+				default:
+					GB_PANIC("Unhandled vector element type");
+				}
+
+				for (i64 i = 0; i < len; i++) {
+					unify(cls, ix + (off + i*elem_sz)/8, reg);
+					// NOTE(bill): Everything after the first one is the upper
+					// half of a register
+					reg = RegClass_SSEUp;
+				}
+			}
+			break;
+		default:
+			GB_PANIC("Unhandled type");
+			break;
+		}
+	}
+
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+		for (unsigned i = 0; i < arg_count; i++) {
+			LLVMTypeRef t = arg_types[i];
+			LLVMTypeKind kind = LLVMGetTypeKind(t);
+			if (kind == LLVMStructTypeKind) {
+				i64 sz = lb_sizeof(t);
+				if (sz == 0) {
+					args[i] = lb_arg_type_ignore(t);
+				} else {
+					args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true));
+				}
+			} else {
+				args[i] = non_struct(c, t);
+			}
+		}
+		return args;
+	}
+
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+		if (!return_is_defined) {
+			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
+			i64 sz = lb_sizeof(return_type);
+			switch (sz) {
+			case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c,  8), nullptr, nullptr);
+			case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+			}
+			return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true));
+		} else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
+			return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
+		}
+		return non_struct(c, return_type);
+	}
+};
+
+
+namespace lbAbiAarch64 {
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+		ft->ctx = c;
+		// ft->args = compute_arg_types(c, arg_types, arg_count);
+		// ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+		// ft->calling_convention = calling_convention;
+		return ft;
+	}
+}
+
+
+LB_ABI_INFO(lb_get_abi_info) {
+	switch (calling_convention) {
+	case ProcCC_None:
+	case ProcCC_PureNone:
+	case ProcCC_InlineAsm:
+		{
+			lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+			ft->ctx = c;
+			ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+			for (unsigned i = 0; i < arg_count; i++) {
+				ft->args[i] = lb_arg_type_direct(arg_types[i]);
+			}
+			if (return_is_defined) {
+				ft->ret = lb_arg_type_direct(return_type);
+			} else {
+				ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
+			}
+			ft->calling_convention = calling_convention;
+			return ft;
+		}
+	}
+
+	if (build_context.metrics.arch == TargetArch_amd64) {
+		if (build_context.metrics.os == TargetOs_windows) {
+			return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		} else {
+			return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		}
+	} else if (build_context.metrics.arch == TargetArch_386) {
+		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+	} else if (build_context.metrics.arch == TargetArch_wasm32) {
+		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+	}
+	GB_PANIC("Unsupported ABI");
+	return {};
+}

Fișier diff suprimat deoarece este prea mare
+ 473 - 166
src/llvm_backend.cpp


+ 31 - 2
src/llvm_backend.hpp

@@ -1,3 +1,5 @@
+#if defined(LLVM_BACKEND_SUPPORT)
+#if defined(GB_SYSTEM_WINDOWS)
 #include "llvm-c/Core.h"
 #include "llvm-c/ExecutionEngine.h"
 #include "llvm-c/Target.h"
@@ -12,6 +14,23 @@
 #include "llvm-c/Transforms/Scalar.h"
 #include "llvm-c/Transforms/Utils.h"
 #include "llvm-c/Transforms/Vectorize.h"
+#else
+#include <llvm-c/Core.h>
+#include <llvm-c/ExecutionEngine.h>
+#include <llvm-c/Target.h>
+#include <llvm-c/Analysis.h>
+#include <llvm-c/Object.h>
+#include <llvm-c/BitWriter.h>
+#include <llvm-c/DebugInfo.h>
+#include <llvm-c/Transforms/AggressiveInstCombine.h>
+#include <llvm-c/Transforms/InstCombine.h>
+#include <llvm-c/Transforms/IPO.h>
+#include <llvm-c/Transforms/PassManagerBuilder.h>
+#include <llvm-c/Transforms/Scalar.h>
+#include <llvm-c/Transforms/Utils.h>
+#include <llvm-c/Transforms/Vectorize.h>
+#endif
+#endif
 
 struct lbProcedure;
 
@@ -74,6 +93,8 @@ struct lbModule {
 	gbMutex mutex;
 
 	Map<LLVMTypeRef> types; // Key: Type *
+	Map<Type *> llvm_types; // Key: LLVMTypeRef
+	i32 internal_type_level;
 
 	Map<lbValue>  values;           // Key: Entity *
 	StringMap<lbValue>  members;
@@ -83,6 +104,10 @@ struct lbModule {
 	StringMap<LLVMValueRef> const_strings;
 
 	Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
+	Map<struct lbFunctionType *> function_type_map; // Key: Type *
+
+	Map<lbProcedure *> equal_procs; // Key: Type *
+	Map<lbProcedure *> hasher_procs; // Key: Type *
 
 	u32 global_array_index;
 	u32 global_generated_index;
@@ -199,6 +224,7 @@ struct lbProcedure {
 	bool         is_entry_point;
 	bool         is_startup;
 
+	lbFunctionType *abi_function_type;
 
 	LLVMValueRef    value;
 	LLVMBuilderRef  builder;
@@ -301,7 +327,7 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_ini
 
 void lb_add_foreign_library_path(lbModule *m, Entity *e);
 
-lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type=t_typeid);
+lbValue lb_typeid(lbModule *m, Type *type);
 
 lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
 lbValue lb_address_from_load(lbProcedure *p, lbValue value);
@@ -344,7 +370,7 @@ String lb_get_const_string(lbModule *m, lbValue value);
 lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
 lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
 lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type);
-lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type);
+lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type);
 void    lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node);
 
 
@@ -354,6 +380,9 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To
 
 lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
 
+lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type);
+lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type);
+lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
 
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"

Fișier diff suprimat deoarece este prea mare
+ 371 - 160
src/main.cpp


+ 184 - 100
src/parser.cpp

@@ -108,8 +108,25 @@ Token ast_token(Ast *node) {
 	return empty_token;
 }
 
+
+isize ast_node_size(AstKind kind) {
+	return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
+
+}
+// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
+Ast *alloc_ast_node(AstFile *f, AstKind kind) {
+	gbAllocator a = ast_allocator(f);
+
+	isize size = ast_node_size(kind);
+
+	Ast *node = cast(Ast *)gb_alloc(a, size);
+	node->kind = kind;
+	node->file = f;
+	return node;
+}
+
 Ast *clone_ast(Ast *node);
-Array<Ast *> clone_ast_array(Array<Ast *> array) {
+Array<Ast *> clone_ast_array(Array<Ast *> const &array) {
 	Array<Ast *> result = {};
 	if (array.count > 0) {
 		result = array_make<Ast *>(ast_allocator(nullptr), array.count);
@@ -119,13 +136,23 @@ Array<Ast *> clone_ast_array(Array<Ast *> array) {
 	}
 	return result;
 }
+Slice<Ast *> clone_ast_array(Slice<Ast *> const &array) {
+	Slice<Ast *> result = {};
+	if (array.count > 0) {
+		result = slice_clone(permanent_allocator(), array);
+		for_array(i, array) {
+			result[i] = clone_ast(array[i]);
+		}
+	}
+	return result;
+}
 
 Ast *clone_ast(Ast *node) {
 	if (node == nullptr) {
 		return nullptr;
 	}
 	Ast *n = alloc_ast_node(node->file, node->kind);
-	gb_memmove(n, node, gb_size_of(Ast));
+	gb_memmove(n, node, ast_node_size(node->kind));
 
 	switch (n->kind) {
 	default: GB_PANIC("Unhandled Ast %.*s", LIT(ast_strings[n->kind])); break;
@@ -463,23 +490,6 @@ bool ast_node_expect(Ast *node, AstKind kind) {
 	return true;
 }
 
-
-gb_global gbAtomic64 total_allocated_node_memory = {0};
-gb_global gbAtomic64 total_subtype_node_memory_test = {0};
-
-// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
-Ast *alloc_ast_node(AstFile *f, AstKind kind) {
-	gbAllocator a = ast_allocator(f);
-
-	gb_atomic64_fetch_add(&total_allocated_node_memory, cast(i64)(gb_size_of(Ast)));
-	gb_atomic64_fetch_add(&total_subtype_node_memory_test, cast(i64)(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind]));
-
-	Ast *node = gb_alloc_item(a, Ast);
-	node->kind = kind;
-	node->file = f;
-	return node;
-}
-
 Ast *ast_bad_expr(AstFile *f, Token begin, Token end) {
 	Ast *result = alloc_ast_node(f, Ast_BadExpr);
 	result->BadExpr.begin = begin;
@@ -537,10 +547,10 @@ Ast *ast_paren_expr(AstFile *f, Ast *expr, Token open, Token close) {
 	return result;
 }
 
-Ast *ast_call_expr(AstFile *f, Ast *proc, Array<Ast *> args, Token open, Token close, Token ellipsis) {
+Ast *ast_call_expr(AstFile *f, Ast *proc, Array<Ast *> const &args, Token open, Token close, Token ellipsis) {
 	Ast *result = alloc_ast_node(f, Ast_CallExpr);
 	result->CallExpr.proc     = proc;
-	result->CallExpr.args     = args;
+	result->CallExpr.args     = slice_from_array(args);
 	result->CallExpr.open     = open;
 	result->CallExpr.close    = close;
 	result->CallExpr.ellipsis = ellipsis;
@@ -624,7 +634,8 @@ Ast *ast_undef(AstFile *f, Token token) {
 Ast *ast_basic_lit(AstFile *f, Token basic_lit) {
 	Ast *result = alloc_ast_node(f, Ast_BasicLit);
 	result->BasicLit.token = basic_lit;
-	result->BasicLit.value = exact_value_from_basic_literal(basic_lit);
+	result->tav.mode = Addressing_Constant;
+	result->tav.value = exact_value_from_basic_literal(basic_lit);
 	return result;
 }
 
@@ -643,12 +654,12 @@ Ast *ast_ellipsis(AstFile *f, Token token, Ast *expr) {
 }
 
 
-Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> args) {
+Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &args) {
 	Ast *result = alloc_ast_node(f, Ast_ProcGroup);
 	result->ProcGroup.token = token;
 	result->ProcGroup.open  = open;
 	result->ProcGroup.close = close;
-	result->ProcGroup.args = args;
+	result->ProcGroup.args = slice_from_array(args);
 	return result;
 }
 
@@ -658,7 +669,7 @@ Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token,
 	result->ProcLit.body = body;
 	result->ProcLit.tags = tags;
 	result->ProcLit.where_token = where_token;
-	result->ProcLit.where_clauses = where_clauses;
+	result->ProcLit.where_clauses = slice_from_array(where_clauses);
 	return result;
 }
 
@@ -670,10 +681,10 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
 	return result;
 }
 
-Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> elems, Token open, Token close) {
+Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> const &elems, Token open, Token close) {
 	Ast *result = alloc_ast_node(f, Ast_CompoundLit);
 	result->CompoundLit.type = type;
-	result->CompoundLit.elems = elems;
+	result->CompoundLit.elems = slice_from_array(elems);
 	result->CompoundLit.open = open;
 	result->CompoundLit.close = close;
 	return result;
@@ -736,7 +747,7 @@ Ast *ast_inline_asm_expr(AstFile *f, Token token, Token open, Token close,
 	result->InlineAsmExpr.token              = token;
 	result->InlineAsmExpr.open               = open;
 	result->InlineAsmExpr.close              = close;
-	result->InlineAsmExpr.param_types        = param_types;
+	result->InlineAsmExpr.param_types        = slice_from_array(param_types);
 	result->InlineAsmExpr.return_type        = return_type;
 	result->InlineAsmExpr.asm_string         = asm_string;
 	result->InlineAsmExpr.constraints_string = constraints_string;
@@ -768,18 +779,18 @@ Ast *ast_expr_stmt(AstFile *f, Ast *expr) {
 	return result;
 }
 
-Ast *ast_assign_stmt(AstFile *f, Token op, Array<Ast *> lhs, Array<Ast *> rhs) {
+Ast *ast_assign_stmt(AstFile *f, Token op, Array<Ast *> const &lhs, Array<Ast *> const &rhs) {
 	Ast *result = alloc_ast_node(f, Ast_AssignStmt);
 	result->AssignStmt.op = op;
-	result->AssignStmt.lhs = lhs;
-	result->AssignStmt.rhs = rhs;
+	result->AssignStmt.lhs = slice_from_array(lhs);
+	result->AssignStmt.rhs = slice_from_array(rhs);
 	return result;
 }
 
 
-Ast *ast_block_stmt(AstFile *f, Array<Ast *> stmts, Token open, Token close) {
+Ast *ast_block_stmt(AstFile *f, Array<Ast *> const &stmts, Token open, Token close) {
 	Ast *result = alloc_ast_node(f, Ast_BlockStmt);
-	result->BlockStmt.stmts = stmts;
+	result->BlockStmt.stmts = slice_from_array(stmts);
 	result->BlockStmt.open = open;
 	result->BlockStmt.close = close;
 	return result;
@@ -805,10 +816,10 @@ Ast *ast_when_stmt(AstFile *f, Token token, Ast *cond, Ast *body, Ast *else_stmt
 }
 
 
-Ast *ast_return_stmt(AstFile *f, Token token, Array<Ast *> results) {
+Ast *ast_return_stmt(AstFile *f, Token token, Array<Ast *> const &results) {
 	Ast *result = alloc_ast_node(f, Ast_ReturnStmt);
 	result->ReturnStmt.token = token;
-	result->ReturnStmt.results = results;
+	result->ReturnStmt.results = slice_from_array(results);
 	return result;
 }
 
@@ -866,11 +877,11 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) {
 	return result;
 }
 
-Ast *ast_case_clause(AstFile *f, Token token, Array<Ast *> list, Array<Ast *> stmts) {
+Ast *ast_case_clause(AstFile *f, Token token, Array<Ast *> const &list, Array<Ast *> const &stmts) {
 	Ast *result = alloc_ast_node(f, Ast_CaseClause);
 	result->CaseClause.token = token;
-	result->CaseClause.list  = list;
-	result->CaseClause.stmts = stmts;
+	result->CaseClause.list  = slice_from_array(list);
+	result->CaseClause.stmts = slice_from_array(stmts);
 	return result;
 }
 
@@ -889,10 +900,10 @@ Ast *ast_branch_stmt(AstFile *f, Token token, Ast *label) {
 	return result;
 }
 
-Ast *ast_using_stmt(AstFile *f, Token token, Array<Ast *> list) {
+Ast *ast_using_stmt(AstFile *f, Token token, Array<Ast *> const &list) {
 	Ast *result = alloc_ast_node(f, Ast_UsingStmt);
 	result->UsingStmt.token = token;
-	result->UsingStmt.list  = list;
+	result->UsingStmt.list  = slice_from_array(list);
 	return result;
 }
 
@@ -905,10 +916,10 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) {
 	return result;
 }
 
-Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag,
+Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast *default_value, u32 flags, Token tag,
                CommentGroup *docs, CommentGroup *comment) {
 	Ast *result = alloc_ast_node(f, Ast_Field);
-	result->Field.names         = names;
+	result->Field.names         = slice_from_array(names);
 	result->Field.type          = type;
 	result->Field.default_value = default_value;
 	result->Field.flags         = flags;
@@ -918,10 +929,10 @@ Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u3
 	return result;
 }
 
-Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> list) {
+Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> const &list) {
 	Ast *result = alloc_ast_node(f, Ast_FieldList);
 	result->FieldList.token = token;
-	result->FieldList.list  = list;
+	result->FieldList.list  = slice_from_array(list);
 	return result;
 }
 
@@ -1002,7 +1013,7 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
 	return result;
 }
 
-Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_count,
+Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
                      Ast *polymorphic_params, bool is_packed, bool is_raw_union,
                      Ast *align,
                      Token where_token, Array<Ast *> const &where_clauses) {
@@ -1015,38 +1026,38 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
 	result->StructType.is_raw_union       = is_raw_union;
 	result->StructType.align              = align;
 	result->StructType.where_token        = where_token;
-	result->StructType.where_clauses      = where_clauses;
+	result->StructType.where_clauses      = slice_from_array(where_clauses);
 	return result;
 }
 
 
-Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
+Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
                     Token where_token, Array<Ast *> const &where_clauses) {
 	Ast *result = alloc_ast_node(f, Ast_UnionType);
 	result->UnionType.token              = token;
-	result->UnionType.variants           = variants;
+	result->UnionType.variants           = slice_from_array(variants);
 	result->UnionType.polymorphic_params = polymorphic_params;
 	result->UnionType.align              = align;
 	result->UnionType.no_nil             = no_nil;
-	result->UnionType.maybe             = maybe;
+	result->UnionType.maybe              = maybe;
 	result->UnionType.where_token        = where_token;
-	result->UnionType.where_clauses      = where_clauses;
+	result->UnionType.where_clauses      = slice_from_array(where_clauses);
 	return result;
 }
 
 
-Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array<Ast *> fields) {
+Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array<Ast *> const &fields) {
 	Ast *result = alloc_ast_node(f, Ast_EnumType);
 	result->EnumType.token = token;
 	result->EnumType.base_type = base_type;
-	result->EnumType.fields = fields;
+	result->EnumType.fields = slice_from_array(fields);
 	return result;
 }
 
-Ast *ast_bit_field_type(AstFile *f, Token token, Array<Ast *> fields, Ast *align) {
+Ast *ast_bit_field_type(AstFile *f, Token token, Array<Ast *> const &fields, Ast *align) {
 	Ast *result = alloc_ast_node(f, Ast_BitFieldType);
 	result->BitFieldType.token = token;
-	result->BitFieldType.fields = fields;
+	result->BitFieldType.fields = slice_from_array(fields);
 	result->BitFieldType.align = align;
 	return result;
 }
@@ -1069,7 +1080,7 @@ Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) {
 
 
 Ast *ast_foreign_block_decl(AstFile *f, Token token, Ast *foreign_library, Ast *body,
-                                CommentGroup *docs) {
+                            CommentGroup *docs) {
 	Ast *result = alloc_ast_node(f, Ast_ForeignBlockDecl);
 	result->ForeignBlockDecl.token           = token;
 	result->ForeignBlockDecl.foreign_library = foreign_library;
@@ -1087,12 +1098,12 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) {
 	return result;
 }
 
-Ast *ast_value_decl(AstFile *f, Array<Ast *> names, Ast *type, Array<Ast *> values, bool is_mutable,
-                        CommentGroup *docs, CommentGroup *comment) {
+Ast *ast_value_decl(AstFile *f, Array<Ast *> const &names, Ast *type, Array<Ast *> const &values, bool is_mutable,
+                    CommentGroup *docs, CommentGroup *comment) {
 	Ast *result = alloc_ast_node(f, Ast_ValueDecl);
-	result->ValueDecl.names      = names;
+	result->ValueDecl.names      = slice_from_array(names);
 	result->ValueDecl.type       = type;
-	result->ValueDecl.values     = values;
+	result->ValueDecl.values     = slice_from_array(values);
 	result->ValueDecl.is_mutable = is_mutable;
 	result->ValueDecl.docs       = docs;
 	result->ValueDecl.comment    = comment;
@@ -1111,7 +1122,7 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C
 }
 
 Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name,
-                         CommentGroup *docs, CommentGroup *comment) {
+                     CommentGroup *docs, CommentGroup *comment) {
 	Ast *result = alloc_ast_node(f, Ast_ImportDecl);
 	result->ImportDecl.token       = token;
 	result->ImportDecl.is_using    = is_using;
@@ -1123,10 +1134,10 @@ Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Toke
 }
 
 Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, Token library_name,
-                                 CommentGroup *docs, CommentGroup *comment) {
+                             CommentGroup *docs, CommentGroup *comment) {
 	Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl);
 	result->ForeignImportDecl.token        = token;
-	result->ForeignImportDecl.filepaths    = filepaths;
+	result->ForeignImportDecl.filepaths    = slice_from_array(filepaths);
 	result->ForeignImportDecl.library_name = library_name;
 	result->ForeignImportDecl.docs         = docs;
 	result->ForeignImportDecl.comment      = comment;
@@ -1136,11 +1147,11 @@ Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, To
 }
 
 
-Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array<Ast *> elems) {
+Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &elems) {
 	Ast *result = alloc_ast_node(f, Ast_Attribute);
 	result->Attribute.token = token;
 	result->Attribute.open  = open;
-	result->Attribute.elems = elems;
+	result->Attribute.elems = slice_from_array(elems);
 	result->Attribute.close = close;
 	return result;
 }
@@ -1182,6 +1193,12 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
 	Array<Token> list = {};
 	list.allocator = heap_allocator();
 	isize end_line = f->curr_token.pos.line;
+	if (f->curr_token_index == 1 &&
+	    f->prev_token.kind == Token_Comment &&
+	    f->prev_token.pos.line+1 == f->curr_token.pos.line) {
+		// NOTE(bill): Special logic for the first comment in the file
+		array_add(&list, f->prev_token);
+	}
 	while (f->curr_token.kind == Token_Comment &&
 	       f->curr_token.pos.line <= end_line+n) {
 		array_add(&list, consume_comment(f, &end_line));
@@ -1192,7 +1209,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
 	CommentGroup *comments = nullptr;
 	if (list.count > 0) {
 		comments = gb_alloc_item(heap_allocator(), CommentGroup);
-		comments->list = list;
+		comments->list = slice_from_array(list);
 		array_add(&f->comments, comments);
 	}
 	return comments;
@@ -1894,12 +1911,18 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 
 	case Token_opaque: {
 		Token token = expect_token(f, Token_opaque);
+		warning(token, "opaque is deprecated, please use #opaque");
 		Ast *type = parse_type(f);
 		return ast_opaque_type(f, token, type);
 	}
 
 	case Token_Hash: {
 		Token token = expect_token(f, Token_Hash);
+		if (allow_token(f, Token_opaque)) {
+			Ast *type = parse_type(f);
+			return ast_opaque_type(f, token, type);
+		}
+
 		Token name = expect_token(f, Token_Ident);
 		if (name.string == "type") {
 			return ast_helper_type(f, token, parse_type(f));
@@ -2201,7 +2224,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 
 		if (allow_token(f, Token_OpenParen)) {
 			isize param_count = 0;
-			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, true, true);
 			if (param_count == 0) {
 				syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter");
 				polymorphic_params = nullptr;
@@ -2262,7 +2285,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		Ast *fields = parse_struct_field_list(f, &name_count);
 		Token    close  = expect_token(f, Token_CloseBrace);
 
-		Array<Ast *> decls = {};
+		Slice<Ast *> decls = {};
 		if (fields != nullptr) {
 			GB_ASSERT(fields->kind == Ast_FieldList);
 			decls = fields->FieldList.list;
@@ -2284,7 +2307,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 
 		if (allow_token(f, Token_OpenParen)) {
 			isize param_count = 0;
-			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, true, true);
 			if (param_count == 0) {
 				syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric");
 				polymorphic_params = nullptr;
@@ -2586,7 +2609,15 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) {
 	f->expr_level--;
 	close_paren = expect_closing(f, Token_CloseParen, str_lit("argument list"));
 
-	return ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
+
+	Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
+
+	Ast *o = unparen_expr(operand);
+	if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
+		return ast_selector_call_expr(f, o->SelectorExpr.token, o, call);
+	}
+
+	return call;
 }
 
 Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
@@ -2638,11 +2669,10 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
 
 		case Token_ArrowRight: {
 			Token token = advance_token(f);
-			// syntax_error(token, "Selector expressions use '.' rather than '->'");
 
-			Ast *sel = ast_selector_expr(f, token, operand, parse_ident(f));
-			Ast *call = parse_call_expr(f, sel);
-			operand = ast_selector_call_expr(f, token, sel, call);
+			operand = ast_selector_expr(f, token, operand, parse_ident(f));
+			// Ast *call = parse_call_expr(f, sel);
+			// operand = ast_selector_call_expr(f, token, sel, call);
 			break;
 		}
 
@@ -3317,11 +3347,10 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
 				return FieldPrefix_no_alias;
 			} else if (f->curr_token.string == "c_vararg") {
 				return FieldPrefix_c_var_arg;
+			} else if (f->curr_token.string == "const") {
+				return FieldPrefix_const;
 			}
 			break;
-
-		case Token_const:
-			return FieldPrefix_const;
 		}
 		return FieldPrefix_Unknown;
 	}
@@ -4723,18 +4752,14 @@ void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFi
 
 
 // NOTE(bill): Returns true if it's added
-bool try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) {
-	if (build_context.generate_docs) {
-		return false;
-	}
-
+AstPackage *try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) {
 	String const FILE_EXT = str_lit(".odin");
 
 	gb_mutex_lock(&p->file_add_mutex);
 	defer (gb_mutex_unlock(&p->file_add_mutex));
 
 	if (string_set_exists(&p->imported_files, path)) {
-		return false;
+		return nullptr;
 	}
 	string_set_add(&p->imported_files, path);
 
@@ -4757,7 +4782,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
 		pkg->is_single_file = true;
 		parser_add_file_to_process(p, pkg, fi, pos);
 		parser_add_package(p, pkg);
-		return true;
+		return pkg;
 	}
 
 
@@ -4773,22 +4798,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
 	switch (rd_err) {
 	case ReadDirectory_InvalidPath:
 		syntax_error(pos, "Invalid path: %.*s", LIT(rel_path));
-		return false;
+		return nullptr;
 	case ReadDirectory_NotExists:
 		syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path));
-		return false;
+		return nullptr;
 	case ReadDirectory_Permission:
 		syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
-		return false;
+		return nullptr;
 	case ReadDirectory_NotDir:
 		syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path));
-		return false;
+		return nullptr;
 	case ReadDirectory_Empty:
 		syntax_error(pos, "Empty directory: %.*s", LIT(rel_path));
-		return false;
+		return nullptr;
 	case ReadDirectory_Unknown:
 		syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
-		return false;
+		return nullptr;
 	}
 
 	for_array(list_index, list) {
@@ -4810,7 +4835,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
 
 	parser_add_package(p, pkg);
 
-	return true;
+	return pkg;
 }
 
 gb_global Rune illegal_import_runes[] = {
@@ -4829,7 +4854,7 @@ bool is_import_path_valid(String path) {
 		u8 *curr = start;
 		while (curr < end) {
 			isize width = 1;
-			Rune r = curr[0];
+			Rune r = *curr;
 			if (r >= 0x80) {
 				width = gb_utf8_decode(curr, end-curr, &r);
 				if (r == GB_RUNE_INVALID && width == 1) {
@@ -4854,6 +4879,45 @@ bool is_import_path_valid(String path) {
 	return false;
 }
 
+bool is_build_flag_path_valid(String path) {
+	if (path.len > 0) {
+		u8 *start = path.text;
+		u8 *end = path.text + path.len;
+		u8 *curr = start;
+		isize index = 0;
+		while (curr < end) {
+			isize width = 1;
+			Rune r = *curr;
+			if (r >= 0x80) {
+				width = gb_utf8_decode(curr, end-curr, &r);
+				if (r == GB_RUNE_INVALID && width == 1) {
+					return false;
+				}
+				else if (r == GB_RUNE_BOM && curr-start > 0) {
+					return false;
+				}
+			}
+
+			for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) {
+#if defined(GB_SYSTEM_WINDOWS)
+				if (r == '\\') {
+					break;
+				}
+#endif
+				if (r == illegal_import_runes[i]) {
+					return false;
+				}
+			}
+
+			curr += width;
+			index += 1;
+		}
+
+		return true;
+	}
+	return false;
+}
+
 
 bool is_package_name_reserved(String const &name) {
 	if (name == "builtin") {
@@ -4974,7 +5038,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir,
 
 
 
-void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> &decls);
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice<Ast *> &decls);
 
 void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) {
 	if (ws->body != nullptr) {
@@ -4995,7 +5059,7 @@ void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenS
 	}
 }
 
-void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> &decls) {
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice<Ast *> &decls) {
 	for_array(i, decls) {
 		Ast *node = decls[i];
 		if (!is_ast_decl(node) &&
@@ -5034,8 +5098,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *>
 		} else if (node->kind == Ast_ForeignImportDecl) {
 			ast_node(fl, ForeignImportDecl, node);
 
-			fl->fullpaths.allocator = heap_allocator();
-			array_reserve(&fl->fullpaths, fl->filepaths.count);
+			auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count);
 
 			for_array(fp_idx, fl->filepaths) {
 				String file_str = fl->filepaths[fp_idx].string;
@@ -5049,14 +5112,17 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *>
 					}
 					fullpath = foreign_path;
 				}
-				array_add(&fl->fullpaths, fullpath);
+				array_add(&fullpaths, fullpath);
 			}
-			if (fl->fullpaths.count == 0) {
+			if (fullpaths.count == 0) {
 				syntax_error(decls[i], "No foreign paths found");
 				decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]);
 				goto end;
 			}
 
+			fl->fullpaths = slice_from_array(fullpaths);
+
+
 		} else if (node->kind == Ast_WhenStmt) {
 			ast_node(ws, WhenStmt, node);
 			parse_setup_file_when_stmt(p, f, base_dir, ws);
@@ -5218,12 +5284,12 @@ bool parse_file(Parser *p, AstFile *f) {
 	f->pkg_decl = pd;
 
 	if (f->error_count == 0) {
-		f->decls = array_make<Ast *>(heap_allocator());
+		auto decls = array_make<Ast *>(heap_allocator());
 
 		while (f->curr_token.kind != Token_EOF) {
 			Ast *stmt = parse_stmt(f);
 			if (stmt && stmt->kind != Ast_EmptyStmt) {
-				array_add(&f->decls, stmt);
+				array_add(&decls, stmt);
 				if (stmt->kind == Ast_ExprStmt &&
 				    stmt->ExprStmt.expr != nullptr &&
 				    stmt->ExprStmt.expr->kind == Ast_ProcLit) {
@@ -5232,6 +5298,8 @@ bool parse_file(Parser *p, AstFile *f) {
 			}
 		}
 
+		f->decls = slice_from_array(decls);
+
 		parse_setup_file_decls(p, f, base_dir, f->decls);
 	}
 
@@ -5325,7 +5393,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
 	}
 
 	TokenPos init_pos = {};
-	if (!build_context.generate_docs) {
+	{
 		String s = get_fullpath_core(heap_allocator(), str_lit("runtime"));
 		try_add_import_path(p, s, s, init_pos, Package_Runtime);
 	}
@@ -5333,6 +5401,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
 	try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init);
 	p->init_fullpath = init_fullpath;
 
+	for_array(i, build_context.extra_packages) {
+		String path = build_context.extra_packages[i];
+		String fullpath = path_to_full_path(heap_allocator(), path); // LEAK?
+		if (!path_is_directory(fullpath)) {
+			String const ext = str_lit(".odin");
+			if (!string_ends_with(fullpath, ext)) {
+				error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
+				return ParseFile_WrongExtension;
+			}
+		}
+		AstPackage *pkg = try_add_import_path(p, fullpath, fullpath, init_pos, Package_Normal);
+		if (pkg) {
+			pkg->is_extra = true;
+		}
+	}
+
 	thread_pool_start(&parser_thread_pool);
 	thread_pool_wait_to_process(&parser_thread_pool);
 

+ 42 - 39
src/parser.hpp

@@ -46,7 +46,7 @@ enum ParseFileError {
 };
 
 struct CommentGroup {
-	Array<Token> list; // Token_Comment
+	Slice<Token> list; // Token_Comment
 };
 
 
@@ -98,8 +98,8 @@ struct AstFile {
 	bool         in_foreign_block;
 	bool         allow_type;
 
-	Array<Ast *> decls;
-	Array<Ast *> imports; // 'import' 'using import'
+	Slice<Ast *> decls;
+	Array<Ast *> imports; // 'import'
 	isize        directive_count;
 
 	Ast *        curr_proc;
@@ -107,6 +107,8 @@ struct AstFile {
 	f64          time_to_tokenize; // seconds
 	f64          time_to_parse;    // seconds
 
+	bool is_test;
+
 	CommentGroup *lead_comment;     // Comment (block) before the decl
 	CommentGroup *line_comment;     // Comment after the semicolon
 	CommentGroup *docs;             // current docs
@@ -148,6 +150,7 @@ struct AstPackage {
 	Scope *   scope;
 	DeclInfo *decl_info;
 	bool      used;
+	bool      is_extra;
 };
 
 
@@ -217,14 +220,16 @@ enum ProcCallingConvention {
 	ProcCC_ForeignBlockDefault = -1,
 };
 
-enum StateFlag {
+enum StateFlag : u16 {
 	StateFlag_bounds_check    = 1<<0,
 	StateFlag_no_bounds_check = 1<<1,
 
 	StateFlag_no_deferred = 1<<5,
+
+	StateFlag_BeenHandled = 1<<15,
 };
 
-enum ViralStateFlag {
+enum ViralStateFlag : u16 {
 	ViralStateFlag_ContainsDeferredProcedure = 1<<0,
 };
 
@@ -275,7 +280,6 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
 	AST_KIND(Undef,          "undef",           Token) \
 	AST_KIND(BasicLit,       "basic literal",   struct { \
 		Token token; \
-		ExactValue value; \
 	}) \
 	AST_KIND(BasicDirective, "basic directive", struct { \
 		Token  token; \
@@ -289,7 +293,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
 		Token        token; \
 		Token        open;  \
 		Token        close; \
-		Array<Ast *> args;  \
+		Slice<Ast *> args;  \
 	}) \
 	AST_KIND(ProcLit, "procedure literal", struct { \
 		Ast *type; \
@@ -297,12 +301,12 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
 		u64  tags; \
 		ProcInlining inlining; \
 		Token where_token; \
-		Array<Ast *> where_clauses; \
+		Slice<Ast *> where_clauses; \
 		DeclInfo *decl; \
 	}) \
 	AST_KIND(CompoundLit, "compound literal", struct { \
 		Ast *type; \
-		Array<Ast *> elems; \
+		Slice<Ast *> elems; \
 		Token open, close; \
 		i64 max_count; \
 	}) \
@@ -325,7 +329,7 @@ AST_KIND(_ExprBegin,  "",  bool) \
 	}) \
 	AST_KIND(CallExpr,     "call expression", struct { \
 		Ast *        proc; \
-		Array<Ast *> args; \
+		Slice<Ast *> args; \
 		Token        open; \
 		Token        close; \
 		Token        ellipsis; \
@@ -342,7 +346,7 @@ AST_KIND(_ExprBegin,  "",  bool) \
 	AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
 		Token token; \
 		Token open, close; \
-		Array<Ast *> param_types; \
+		Slice<Ast *> param_types; \
 		Ast *return_type; \
 		Ast *asm_string; \
 		Ast *constraints_string; \
@@ -362,11 +366,11 @@ AST_KIND(_StmtBegin,     "", bool) \
 	}) \
 	AST_KIND(AssignStmt, "assign statement", struct { \
 		Token op; \
-		Array<Ast *> lhs, rhs; \
+		Slice<Ast *> lhs, rhs; \
 	}) \
 AST_KIND(_ComplexStmtBegin, "", bool) \
 	AST_KIND(BlockStmt, "block statement", struct { \
-		Array<Ast *> stmts; \
+		Slice<Ast *> stmts; \
 		Ast *label;         \
 		Token open, close; \
 	}) \
@@ -388,7 +392,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
 	}) \
 	AST_KIND(ReturnStmt, "return statement", struct { \
 		Token token; \
-		Array<Ast *> results; \
+		Slice<Ast *> results; \
 	}) \
 	AST_KIND(ForStmt, "for statement", struct { \
 		Token token; \
@@ -418,8 +422,8 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
 	}) \
 	AST_KIND(CaseClause, "case clause", struct { \
 		Token token;             \
-		Array<Ast *> list;   \
-		Array<Ast *> stmts;  \
+		Slice<Ast *> list;   \
+		Slice<Ast *> stmts;  \
 		Entity *implicit_entity; \
 	}) \
 	AST_KIND(SwitchStmt, "switch statement", struct { \
@@ -436,12 +440,12 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
 		Ast *tag;    \
 		Ast *body;   \
 		bool partial; \
-}) \
+	}) \
 	AST_KIND(DeferStmt,  "defer statement",  struct { Token token; Ast *stmt; }) \
 	AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
 	AST_KIND(UsingStmt,  "using statement",  struct { \
 		Token token; \
-		Array<Ast *> list; \
+		Slice<Ast *> list; \
 	}) \
 AST_KIND(_ComplexStmtEnd, "", bool) \
 AST_KIND(_StmtEnd,        "", bool) \
@@ -459,9 +463,9 @@ AST_KIND(_DeclBegin,      "", bool) \
 		Ast *name; \
 	}) \
 	AST_KIND(ValueDecl, "value declaration", struct { \
-		Array<Ast *> names;       \
+		Slice<Ast *> names;       \
 		Ast *        type;        \
-		Array<Ast *> values;      \
+		Slice<Ast *> values;      \
 		Array<Ast *> attributes;  \
 		CommentGroup *docs;       \
 		CommentGroup *comment;    \
@@ -486,10 +490,10 @@ AST_KIND(_DeclBegin,      "", bool) \
 	}) \
 	AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
 		Token    token;           \
-		Array<Token> filepaths;   \
+		Slice<Token> filepaths;   \
 		Token    library_name;    \
 		String   collection_name; \
-		Array<String> fullpaths;  \
+		Slice<String> fullpaths;  \
 		Array<Ast *> attributes;  \
 		CommentGroup *docs;       \
 		CommentGroup *comment;    \
@@ -497,11 +501,11 @@ AST_KIND(_DeclBegin,      "", bool) \
 AST_KIND(_DeclEnd,   "", bool) \
 	AST_KIND(Attribute, "attribute", struct { \
 		Token token;        \
-		Array<Ast *> elems; \
+		Slice<Ast *> elems; \
 		Token open, close;  \
 	}) \
 	AST_KIND(Field, "field", struct { \
-		Array<Ast *> names;         \
+		Slice<Ast *> names;         \
 		Ast *        type;          \
 		Ast *        default_value; \
 		Token        tag;           \
@@ -511,7 +515,7 @@ AST_KIND(_DeclEnd,   "", bool) \
 	}) \
 	AST_KIND(FieldList, "field list", struct { \
 		Token token;       \
-		Array<Ast *> list; \
+		Slice<Ast *> list; \
 	}) \
 AST_KIND(_TypeBegin, "", bool) \
 	AST_KIND(TypeidType, "typeid", struct { \
@@ -565,34 +569,34 @@ AST_KIND(_TypeBegin, "", bool) \
 	}) \
 	AST_KIND(StructType, "struct type", struct { \
 		Token token;                \
-		Array<Ast *> fields;        \
+		Slice<Ast *> fields;        \
 		isize field_count;          \
 		Ast *polymorphic_params;    \
 		Ast *align;                 \
 		Token where_token;          \
-		Array<Ast *> where_clauses; \
+		Slice<Ast *> where_clauses; \
 		bool is_packed;             \
 		bool is_raw_union;          \
 	}) \
 	AST_KIND(UnionType, "union type", struct { \
 		Token        token;         \
-		Array<Ast *> variants;      \
+		Slice<Ast *> variants;      \
 		Ast *polymorphic_params;    \
 		Ast *        align;         \
 		bool         maybe;         \
 		bool         no_nil;        \
 		Token where_token;          \
-		Array<Ast *> where_clauses; \
+		Slice<Ast *> where_clauses; \
 	}) \
 	AST_KIND(EnumType, "enum type", struct { \
 		Token        token; \
 		Ast *        base_type; \
-		Array<Ast *> fields; /* FieldValue */ \
+		Slice<Ast *> fields; /* FieldValue */ \
 		bool         is_using; \
 	}) \
 	AST_KIND(BitFieldType, "bit field type", struct { \
 		Token        token; \
-		Array<Ast *> fields; /* FieldValue with : */ \
+		Slice<Ast *> fields; /* FieldValue with : */ \
 		Ast *        align; \
 	}) \
 	AST_KIND(BitSetType, "bit set type", struct { \
@@ -638,23 +642,22 @@ isize const ast_variant_sizes[] = {
 
 struct AstCommonStuff {
 	AstKind      kind;
-	u32          state_flags;
-	u32          viral_state_flags;
-	bool         been_handled;
+	u16          state_flags;
+	u16          viral_state_flags;
 	AstFile *    file;
 	Scope *      scope;
-	TypeAndValue tav;
+	TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
 };
 
 struct Ast {
 	AstKind      kind;
-	u32          state_flags;
-	u32          viral_state_flags;
-	bool         been_handled;
+	u16          state_flags;
+	u16          viral_state_flags;
 	AstFile *    file;
 	Scope *      scope;
-	TypeAndValue tav;
+	TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
 
+	// IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant
 	union {
 #define AST_KIND(_kind_name_, name, ...) GB_JOIN2(Ast, _kind_name_) _kind_name_;
 	AST_KINDS

+ 91 - 46
src/ptr_set.cpp

@@ -1,25 +1,30 @@
+typedef u32 PtrSetIndex;
+
 struct PtrSetFindResult {
-	isize hash_index;
-	isize entry_prev;
-	isize entry_index;
+	PtrSetIndex hash_index;
+	PtrSetIndex entry_prev;
+	PtrSetIndex entry_index;
 };
 
+enum : PtrSetIndex { PTR_SET_SENTINEL = ~(PtrSetIndex)0 };
+
 
 template <typename T>
 struct PtrSetEntry {
-	T     ptr;
-	isize next;
+	T           ptr;
+	PtrSetIndex next;
 };
 
 template <typename T>
 struct PtrSet {
-	Array<isize>          hashes;
+	Array<PtrSetIndex>    hashes;
 	Array<PtrSetEntry<T>> entries;
 };
 
 template <typename T> void ptr_set_init   (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
 template <typename T> void ptr_set_destroy(PtrSet<T> *s);
 template <typename T> T    ptr_set_add    (PtrSet<T> *s, T ptr);
+template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existsed
 template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
 template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
 template <typename T> void ptr_set_clear  (PtrSet<T> *s);
@@ -27,12 +32,31 @@ template <typename T> void ptr_set_grow   (PtrSet<T> *s);
 template <typename T> void ptr_set_rehash (PtrSet<T> *s, isize new_count);
 
 
+isize next_pow2_isize(isize n) {
+	if (n <= 0) {
+		return 0;
+	}
+	n--;
+	n |= n >> 1;
+	n |= n >> 2;
+	n |= n >> 4;
+	n |= n >> 8;
+	n |= n >> 16;
+	if (gb_size_of(isize) == 8) {
+		n |= n >> 32;
+	}
+	n++;
+	return n;
+}
+
 template <typename T>
 void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
+	capacity = next_pow2_isize(gb_max(16, capacity));
+
 	array_init(&s->hashes,  a, capacity);
 	array_init(&s->entries, a, 0, capacity);
 	for (isize i = 0; i < capacity; i++) {
-		s->hashes.data[i] = -1;
+		s->hashes.data[i] = PTR_SET_SENTINEL;
 	}
 }
 
@@ -43,72 +67,69 @@ void ptr_set_destroy(PtrSet<T> *s) {
 }
 
 template <typename T>
-gb_internal isize ptr_set__add_entry(PtrSet<T> *s, T ptr) {
+gb_internal PtrSetIndex ptr_set__add_entry(PtrSet<T> *s, T ptr) {
 	PtrSetEntry<T> e = {};
 	e.ptr = ptr;
-	e.next = -1;
+	e.next = PTR_SET_SENTINEL;
 	array_add(&s->entries, e);
-	return s->entries.count-1;
+	return cast(PtrSetIndex)(s->entries.count-1);
 }
 
 
 template <typename T>
 gb_internal PtrSetFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
-	PtrSetFindResult fr = {-1, -1, -1};
-	if (s->hashes.count > 0) {
+	PtrSetFindResult fr = {PTR_SET_SENTINEL, PTR_SET_SENTINEL, PTR_SET_SENTINEL};
+	if (s->hashes.count != 0) {
 		u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr;
 		u64 n = cast(u64)s->hashes.count;
-		fr.hash_index = cast(isize)(hash % n);
-		fr.entry_index = s->hashes[fr.hash_index];
-		while (fr.entry_index >= 0) {
-			if (s->entries[fr.entry_index].ptr == ptr) {
+		fr.hash_index = cast(PtrSetIndex)(hash & (n-1));
+		fr.entry_index = s->hashes.data[fr.hash_index];
+		while (fr.entry_index != PTR_SET_SENTINEL) {
+			if (s->entries.data[fr.entry_index].ptr == ptr) {
 				return fr;
 			}
 			fr.entry_prev = fr.entry_index;
-			fr.entry_index = s->entries[fr.entry_index].next;
+			fr.entry_index = s->entries.data[fr.entry_index].next;
 		}
 	}
 	return fr;
 }
 
 template <typename T>
-gb_internal b32 ptr_set__full(PtrSet<T> *s) {
+gb_internal bool ptr_set__full(PtrSet<T> *s) {
 	return 0.75f * s->hashes.count <= s->entries.count;
 }
 
-#define PTR_ARRAY_GROW_FORMULA(x) (4*(x) + 7)
-GB_STATIC_ASSERT(PTR_ARRAY_GROW_FORMULA(0) > 0);
-
 template <typename T>
 gb_inline void ptr_set_grow(PtrSet<T> *s) {
-	isize new_count = PTR_ARRAY_GROW_FORMULA(s->entries.count);
+	isize new_count = s->hashes.count*2;
 	ptr_set_rehash(s, new_count);
 }
 
 template <typename T>
 void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
-	isize i, j;
+	PtrSetIndex i, j;
 	PtrSet<T> ns = {};
 	ptr_set_init(&ns, s->hashes.allocator);
 	array_resize(&ns.hashes, new_count);
 	array_reserve(&ns.entries, s->entries.count);
 	for (i = 0; i < new_count; i++) {
-		ns.hashes[i] = -1;
+		ns.hashes.data[i] = PTR_SET_SENTINEL;
 	}
 	for (i = 0; i < s->entries.count; i++) {
-		PtrSetEntry<T> *e = &s->entries[i];
+		PtrSetEntry<T> *e = &s->entries.data[i];
 		PtrSetFindResult fr;
 		if (ns.hashes.count == 0) {
 			ptr_set_grow(&ns);
 		}
 		fr = ptr_set__find(&ns, e->ptr);
 		j = ptr_set__add_entry(&ns, e->ptr);
-		if (fr.entry_prev < 0) {
-			ns.hashes[fr.hash_index] = j;
+		if (fr.entry_prev == PTR_SET_SENTINEL) {
+			ns.hashes.data[fr.hash_index] = j;
 		} else {
-			ns.entries[fr.entry_prev].next = j;
+			ns.entries.data[fr.entry_prev].next = j;
 		}
-		ns.entries[j].next = fr.entry_index;
+		ns.entries.data[j].next = fr.entry_index;
 		if (ptr_set__full(&ns)) {
 			ptr_set_grow(&ns);
 		}
@@ -120,26 +141,24 @@ void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
 template <typename T>
 gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
 	isize index = ptr_set__find(s, ptr).entry_index;
-	return index >= 0;
+	return index != PTR_SET_SENTINEL;
 }
 
 // Returns true if it already exists
 template <typename T>
 T ptr_set_add(PtrSet<T> *s, T ptr) {
-	isize index;
+	PtrSetIndex index;
 	PtrSetFindResult fr;
 	if (s->hashes.count == 0) {
 		ptr_set_grow(s);
 	}
 	fr = ptr_set__find(s, ptr);
-	if (fr.entry_index >= 0) {
-		index = fr.entry_index;
-	} else {
+	if (fr.entry_index == PTR_SET_SENTINEL) {
 		index = ptr_set__add_entry(s, ptr);
-		if (fr.entry_prev >= 0) {
-			s->entries[fr.entry_prev].next = index;
+		if (fr.entry_prev != PTR_SET_SENTINEL) {
+			s->entries.data[fr.entry_prev].next = index;
 		} else {
-			s->hashes[fr.hash_index] = index;
+			s->hashes.data[fr.hash_index] = index;
 		}
 	}
 	if (ptr_set__full(s)) {
@@ -148,32 +167,58 @@ T ptr_set_add(PtrSet<T> *s, T ptr) {
 	return ptr;
 }
 
+template <typename T>
+bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously existsed
+	bool exists = false;
+	PtrSetIndex index;
+	PtrSetFindResult fr;
+	if (s->hashes.count == 0) {
+		ptr_set_grow(s);
+	}
+	fr = ptr_set__find(s, ptr);
+	if (fr.entry_index != PTR_SET_SENTINEL) {
+		exists = true;
+	} else {
+		index = ptr_set__add_entry(s, ptr);
+		if (fr.entry_prev != PTR_SET_SENTINEL) {
+			s->entries.data[fr.entry_prev].next = index;
+		} else {
+			s->hashes.data[fr.hash_index] = index;
+		}
+	}
+	if (ptr_set__full(s)) {
+		ptr_set_grow(s);
+	}
+	return exists;
+}
+
+
 
 template <typename T>
 void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
 	PtrSetFindResult last;
-	if (fr.entry_prev < 0) {
-		s->hashes[fr.hash_index] = s->entries[fr.entry_index].next;
+	if (fr.entry_prev == PTR_SET_SENTINEL) {
+		s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next;
 	} else {
-		s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next;
+		s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next;
 	}
 	if (fr.entry_index == s->entries.count-1) {
 		array_pop(&s->entries);
 		return;
 	}
-	s->entries[fr.entry_index] = s->entries[s->entries.count-1];
-	last = ptr_set__find(s, s->entries[fr.entry_index].ptr);
-	if (last.entry_prev >= 0) {
-		s->entries[last.entry_prev].next = fr.entry_index;
+	s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1];
+	last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr);
+	if (last.entry_prev != PTR_SET_SENTINEL) {
+		s->entries.data[last.entry_prev].next = fr.entry_index;
 	} else {
-		s->hashes[last.hash_index] = fr.entry_index;
+		s->hashes.data[last.hash_index] = fr.entry_index;
 	}
 }
 
 template <typename T>
 void ptr_set_remove(PtrSet<T> *s, T ptr) {
 	PtrSetFindResult fr = ptr_set__find(s, ptr);
-	if (fr.entry_index >= 0) {
+	if (fr.entry_index != PTR_SET_SENTINEL) {
 		ptr_set__erase(s, fr);
 	}
 }

+ 1 - 0
src/string.cpp

@@ -164,6 +164,7 @@ int string_compare(String const &x, String const &y) {
 				return cast(int)x[offset] - cast(int)y[offset];
 			}
 		}
+		return cast(int)(x.len - y.len);
 	}
 	return 0;
 }

+ 0 - 2
src/tokenizer.cpp

@@ -118,8 +118,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
 	TOKEN_KIND(Token_no_inline,   "no_inline"),   \
 	TOKEN_KIND(Token_context,     "context"),     \
 	TOKEN_KIND(Token_asm,         "asm"),         \
-	TOKEN_KIND(Token_macro,       "macro"),       \
-	TOKEN_KIND(Token_const,       "const"),       \
 TOKEN_KIND(Token__KeywordEnd, ""), \
 	TOKEN_KIND(Token_Count, "")
 

+ 224 - 68
src/types.cpp

@@ -323,6 +323,8 @@ String const type_strings[] = {
 enum TypeFlag : u32 {
 	TypeFlag_Polymorphic     = 1<<1,
 	TypeFlag_PolySpecialized = 1<<2,
+	TypeFlag_InProcessOfCheckingPolymorphic = 1<<3,
+	TypeFlag_InProcessOfCheckingABI = 1<<4,
 };
 
 struct Type {
@@ -371,7 +373,28 @@ enum Typeid_Kind : u8 {
 	Typeid_Relative_Slice,
 };
 
+// IMPORTANT NOTE(bill): This must match the same as the in core.odin
+enum TypeInfoFlag : u32 {
+	TypeInfoFlag_Comparable     = 1<<0,
+	TypeInfoFlag_Simple_Compare = 1<<1,
+};
 
+bool is_type_comparable(Type *t);
+bool is_type_simple_compare(Type *t);
+
+u32 type_info_flags_of_type(Type *type) {
+	if (type == nullptr) {
+		return 0;
+	}
+	u32 flags = 0;
+	if (is_type_comparable(type)) {
+		flags |= TypeInfoFlag_Comparable;
+	}
+	if (is_type_simple_compare(type)) {
+		flags |= TypeInfoFlag_Comparable;
+	}
+	return flags;
+}
 
 
 // TODO(bill): Should I add extra information here specifying the kind of selection?
@@ -661,12 +684,15 @@ gb_global Type *t_context_ptr                    = nullptr;
 gb_global Type *t_source_code_location           = nullptr;
 gb_global Type *t_source_code_location_ptr       = nullptr;
 
-gb_global Type *t_map_key                        = nullptr;
+gb_global Type *t_map_hash                       = nullptr;
 gb_global Type *t_map_header                     = nullptr;
 
 gb_global Type *t_vector_x86_mmx                 = nullptr;
 
 
+gb_global Type *t_equal_proc  = nullptr;
+gb_global Type *t_hasher_proc = nullptr;
+
 
 i64      type_size_of               (Type *t);
 i64      type_align_of              (Type *t);
@@ -769,7 +795,8 @@ void set_base_type(Type *t, Type *base) {
 
 
 Type *alloc_type(TypeKind kind) {
-	gbAllocator a = heap_allocator();
+	// gbAllocator a = heap_allocator();
+	gbAllocator a = permanent_allocator();
 	Type *t = gb_alloc_item(a, Type);
 	zero_item(t);
 	t->kind = kind;
@@ -884,6 +911,25 @@ Type *alloc_type_named(String name, Type *base, Entity *type_name) {
 	return t;
 }
 
+bool is_calling_convention_none(ProcCallingConvention calling_convention) {
+	switch (calling_convention) {
+	case ProcCC_None:
+	case ProcCC_PureNone:
+	case ProcCC_InlineAsm:
+		return true;
+	}
+	return false;
+}
+
+bool is_calling_convention_odin(ProcCallingConvention calling_convention) {
+	switch (calling_convention) {
+	case ProcCC_Odin:
+	case ProcCC_Contextless:
+		return true;
+	}
+	return false;
+}
+
 Type *alloc_type_tuple() {
 	Type *t = alloc_type(Type_Tuple);
 	return t;
@@ -918,7 +964,6 @@ bool is_type_valid_for_keys(Type *t);
 
 Type *alloc_type_map(i64 count, Type *key, Type *value) {
 	if (key != nullptr) {
-		GB_ASSERT(is_type_valid_for_keys(key));
 		GB_ASSERT(value != nullptr);
 	}
 	Type *t = alloc_type(Type_Map);
@@ -1192,20 +1237,6 @@ bool is_type_slice(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_Slice;
 }
-bool is_type_u8_slice(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Slice) {
-		return is_type_u8(t->Slice.elem);
-	}
-	return false;
-}
-bool is_type_u8_ptr(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Pointer) {
-		return is_type_u8(t->Slice.elem);
-	}
-	return false;
-}
 bool is_type_proc(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_Proc;
@@ -1249,6 +1280,37 @@ bool is_type_relative_slice(Type *t) {
 	return t->kind == Type_RelativeSlice;
 }
 
+bool is_type_u8_slice(Type *t) {
+	t = base_type(t);
+	if (t->kind == Type_Slice) {
+		return is_type_u8(t->Slice.elem);
+	}
+	return false;
+}
+bool is_type_u8_array(Type *t) {
+	t = base_type(t);
+	if (t->kind == Type_Array) {
+		return is_type_u8(t->Array.elem);
+	}
+	return false;
+}
+bool is_type_u8_ptr(Type *t) {
+	t = base_type(t);
+	if (t->kind == Type_Pointer) {
+		return is_type_u8(t->Slice.elem);
+	}
+	return false;
+}
+bool is_type_rune_array(Type *t) {
+	t = base_type(t);
+	if (t->kind == Type_Array) {
+		return is_type_rune(t->Array.elem);
+	}
+	return false;
+}
+
+
+
 
 Type *core_array_type(Type *t) {
 	for (;;) {
@@ -1261,53 +1323,7 @@ Type *core_array_type(Type *t) {
 	return t;
 }
 
-// NOTE(bill): type can be easily compared using memcmp
-bool is_type_simple_compare(Type *t) {
-	t = core_type(t);
-	switch (t->kind) {
-	case Type_Array:
-		return is_type_simple_compare(t->Array.elem);
-
-	case Type_EnumeratedArray:
-		return is_type_simple_compare(t->EnumeratedArray.elem);
-
-	case Type_Basic:
-		if (t->Basic.flags & BasicFlag_SimpleCompare) {
-			return true;
-		}
-		return false;
-
-	case Type_Pointer:
-	case Type_Proc:
-	case Type_BitSet:
-	case Type_BitField:
-		return true;
-
-	case Type_Struct:
-		for_array(i, t->Struct.fields) {
-			Entity *f = t->Struct.fields[i];
-			if (!is_type_simple_compare(f->type)) {
-				return false;
-			}
-		}
-		return true;
-
-	case Type_Union:
-		for_array(i, t->Union.variants) {
-			Type *v = t->Union.variants[i];
-			if (!is_type_simple_compare(v)) {
-				return false;
-			}
-		}
-		return true;
-
-	case Type_SimdVector:
-		return is_type_simple_compare(t->SimdVector.elem);
-
-	}
 
-	return false;
-}
 
 Type *base_complex_elem_type(Type *t) {
 	t = core_type(t);
@@ -1526,6 +1542,8 @@ bool is_type_valid_for_keys(Type *t) {
 	if (is_type_untyped(t)) {
 		return false;
 	}
+	return is_type_comparable(t);
+#if 0
 	if (is_type_integer(t)) {
 		return true;
 	}
@@ -1541,8 +1559,15 @@ bool is_type_valid_for_keys(Type *t) {
 	if (is_type_typeid(t)) {
 		return true;
 	}
+	if (is_type_simple_compare(t)) {
+		return true;
+	}
+	if (is_type_comparable(t)) {
+		return true;
+	}
 
 	return false;
+#endif
 }
 
 bool is_type_valid_bit_set_elem(Type *t) {
@@ -1695,12 +1720,23 @@ TypeTuple *get_record_polymorphic_params(Type *t) {
 
 
 bool is_type_polymorphic(Type *t, bool or_specialized=false) {
+	if (t->flags & TypeFlag_InProcessOfCheckingPolymorphic) {
+		return false;
+	}
+
 	switch (t->kind) {
 	case Type_Generic:
 		return true;
 
 	case Type_Named:
-		return is_type_polymorphic(t->Named.base, or_specialized);
+		{
+			u32 flags = t->flags;
+			t->flags |= TypeFlag_InProcessOfCheckingPolymorphic;
+			bool ok = is_type_polymorphic(t->Named.base, or_specialized);
+			t->flags = flags;
+			return ok;
+		}
+
 	case Type_Opaque:
 		return is_type_polymorphic(t->Opaque.elem, or_specialized);
 	case Type_Pointer:
@@ -1892,10 +1928,77 @@ bool is_type_comparable(Type *t) {
 
 	case Type_Opaque:
 		return is_type_comparable(t->Opaque.elem);
+
+	case Type_Struct:
+		if (type_size_of(t) == 0) {
+			return false;
+		}
+		if (t->Struct.is_raw_union) {
+			return is_type_simple_compare(t);
+		}
+		for_array(i, t->Struct.fields) {
+			Entity *f = t->Struct.fields[i];
+			if (!is_type_comparable(f->type)) {
+				return false;
+			}
+		}
+		return true;
+	}
+	return false;
+}
+
+// NOTE(bill): type can be easily compared using memcmp
+bool is_type_simple_compare(Type *t) {
+	t = core_type(t);
+	switch (t->kind) {
+	case Type_Array:
+		return is_type_simple_compare(t->Array.elem);
+
+	case Type_EnumeratedArray:
+		return is_type_simple_compare(t->EnumeratedArray.elem);
+
+	case Type_Basic:
+		if (t->Basic.flags & BasicFlag_SimpleCompare) {
+			return true;
+		}
+		if (t->Basic.kind == Basic_typeid) {
+			return true;
+		}
+		return false;
+
+	case Type_Pointer:
+	case Type_Proc:
+	case Type_BitSet:
+	case Type_BitField:
+		return true;
+
+	case Type_Struct:
+		for_array(i, t->Struct.fields) {
+			Entity *f = t->Struct.fields[i];
+			if (!is_type_simple_compare(f->type)) {
+				return false;
+			}
+		}
+		return true;
+
+	case Type_Union:
+		for_array(i, t->Union.variants) {
+			Type *v = t->Union.variants[i];
+			if (!is_type_simple_compare(v)) {
+				return false;
+			}
+		}
+		return true;
+
+	case Type_SimdVector:
+		return is_type_simple_compare(t->SimdVector.elem);
+
 	}
+
 	return false;
 }
 
+
 Type *strip_type_aliasing(Type *x) {
 	if (x == nullptr) {
 		return x;
@@ -2317,7 +2420,7 @@ Selection lookup_field_from_index(Type *type, i64 index) {
 	GB_ASSERT(is_type_struct(type) || is_type_union(type) || is_type_tuple(type));
 	type = base_type(type);
 
-	gbAllocator a = heap_allocator();
+	gbAllocator a = permanent_allocator();
 	isize max_count = 0;
 	switch (type->kind) {
 	case Type_Struct:   max_count = type->Struct.fields.count;   break;
@@ -2365,7 +2468,6 @@ Selection lookup_field_from_index(Type *type, i64 index) {
 	return empty_selection;
 }
 
-
 Entity *scope_lookup_current(Scope *s, String const &name);
 
 Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
@@ -2375,7 +2477,6 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
 		return empty_selection;
 	}
 
-	gbAllocator a = heap_allocator();
 	Type *type = type_deref(type_);
 	bool is_ptr = type != type_;
 	sel.indirect = sel.indirect || is_ptr;
@@ -2964,7 +3065,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
 }
 
 Array<i64> type_set_offsets_of(Array<Entity *> const &fields, bool is_packed, bool is_raw_union) {
-	gbAllocator a = heap_allocator();
+	gbAllocator a = permanent_allocator();
 	auto offsets = array_make<i64>(a, fields.count);
 	i64 curr_offset = 0;
 	if (is_raw_union) {
@@ -3353,6 +3454,58 @@ Type *reduce_tuple_to_single_type(Type *original_type) {
 }
 
 
+Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) {
+	Type *t = alloc_type_struct();
+	t->Struct.fields = array_make<Entity *>(heap_allocator(), field_count);
+
+	Scope *scope = nullptr;
+	for_array(i, t->Struct.fields) {
+		t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved);
+	}
+	t->Struct.is_packed = is_packed;
+
+	return t;
+}
+
+Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) {
+	if (field_count == 0) {
+		return nullptr;
+	}
+	if (!must_be_tuple && field_count == 1) {
+		return field_types[0];
+	}
+
+	Type *t = alloc_type_tuple();
+	t->Tuple.variables = array_make<Entity *>(heap_allocator(), field_count);
+
+	Scope *scope = nullptr;
+	for_array(i, t->Tuple.variables) {
+		t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false);
+	}
+	t->Tuple.is_packed = is_packed;
+
+	return t;
+}
+
+Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg, ProcCallingConvention calling_convention) {
+
+	Type *params  = alloc_type_tuple_from_field_types(param_types, param_count, false, true);
+	isize results_count = 0;
+	if (results != nullptr) {
+		if (results->kind != Type_Tuple) {
+			results = alloc_type_tuple_from_field_types(&results, 1, false, true);
+		}
+		results_count = results->Tuple.variables.count;
+	}
+
+	Scope *scope = nullptr;
+	Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, calling_convention);
+	t->Proc.c_vararg = is_c_vararg;
+	return t;
+}
+
+
+
 gbString write_type_to_string(gbString str, Type *type) {
 	if (type == nullptr) {
 		return gb_string_appendc(str, "<no type>");
@@ -3671,3 +3824,6 @@ gbString type_to_string(Type *type) {
 	return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
 }
 
+
+
+

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff