Browse Source

Merge https://github.com/gingerBill/Odin
"Fixed" a proc overload bug. Still needs a *real* fix.

Zac Pierson 8 years ago
parent
commit
c7bb861d3c
47 changed files with 4541 additions and 2043 deletions
  1. 3 4
      .gitignore
  2. 2 2
      README.md
  3. 0 200
      bin/CODE_OWNERS.TXT
  4. 0 467
      bin/CREDITS.TXT
  5. 0 70
      bin/LICENSE.TXT
  6. BIN
      bin/llc.exe
  7. BIN
      bin/lli.exe
  8. BIN
      bin/opt.exe
  9. 6 10
      build.bat
  10. 221 81
      code/demo.odin
  11. 34 3
      core/_preload.odin
  12. 66 35
      core/fmt.odin
  13. 36 31
      core/math.odin
  14. 10 31
      core/mem.odin
  15. 44 46
      core/os_windows.odin
  16. 40 10
      core/strconv.odin
  17. 1 1
      core/sync.odin
  18. 25 25
      core/sys/wgl.odin
  19. 135 133
      core/sys/windows.odin
  20. 0 0
      misc/compile_time_execution_problems.md
  21. 11 0
      misc/lib_maker_clang.bat
  22. 0 0
      misc/libs.txt
  23. 0 0
      misc/logo-slim.png
  24. 0 0
      misc/roadmap.md
  25. 0 0
      misc/todo.md
  26. 0 4
      run.bat
  27. 2 8
      src/array.c
  28. 1 1
      src/build_settings.c
  29. 54 6
      src/check_decl.c
  30. 117 188
      src/check_expr.c
  31. 144 85
      src/check_stmt.c
  32. 57 31
      src/checker.c
  33. 26 1
      src/entity.c
  34. 52 46
      src/exact_value.c
  35. 292 112
      src/gb/gb.h
  36. 273 184
      src/ir.c
  37. 8 12
      src/ir_print.c
  38. 5 4
      src/main.c
  39. 124 98
      src/parser.c
  40. 2269 0
      src/ssa.c
  41. 277 0
      src/ssa_op.c
  42. 18 18
      src/tokenizer.c
  43. 31 13
      src/types.c
  44. 1 0
      src/unicode.c
  45. 66 44
      src/utf8proc/utf8proc.c
  46. 80 24
      src/utf8proc/utf8proc.h
  47. 10 15
      src/utf8proc/utf8proc_data.c

+ 3 - 4
.gitignore

@@ -254,10 +254,9 @@ paket-files/
 
 # - Windows
 *.sln
-!misc/llvm-bim/lli.exe
-!misc/llvm-bim/opt.exe
-builds
+builds/
+bin/
 
 # - Linux/MacOS
 odin
-odin.dSYM
+odin.dSYM

+ 2 - 2
README.md

@@ -1,4 +1,4 @@
-<img src="logo-slim.png" alt="Odin logo" height="74">
+<img src="misc/logo-slim.png" alt="Odin logo" height="74">
 
 # The Odin Programming Language
 
@@ -25,7 +25,7 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
 - Windows
 	* x86-64
 	* MSVC 2015 installed (C99 support)
-	* LLVM installed
+	* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
 	* Requires MSVC's link.exe as the linker
 		* run `vcvarsall.bat` to setup the path
 

+ 0 - 200
bin/CODE_OWNERS.TXT

@@ -1,200 +0,0 @@
-This file is a list of the people responsible for ensuring that patches for a
-particular part of LLVM are reviewed, either by themself or by someone else.
-They are also the gatekeepers for their part of LLVM, with the final word on
-what goes in or not.
-
-The list is sorted by surname and formatted to allow easy grepping and
-beautification by scripts.  The fields are: name (N), email (E), web-address
-(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
-(S). Each entry should contain at least the (N), (E) and (D) fields.
-
-N: Joe Abbey
-E: [email protected]
-D: LLVM Bitcode (lib/Bitcode/* include/llvm/Bitcode/*)
-
-N: Owen Anderson
-E: [email protected]
-D: SelectionDAG (lib/CodeGen/SelectionDAG/*)
-
-N: Rafael Avila de Espindola
-E: [email protected]
-D: Gold plugin (tools/gold/*)
-
-N: Justin Bogner
-E: [email protected]
-D: InstrProfiling and related parts of ProfileData
-
-N: Chandler Carruth
-E: [email protected]
-E: [email protected]
-D: Config, ADT, Support, inlining & related passes, SROA/mem2reg & related passes, CMake, library layering
-
-N: Evan Cheng
-E: [email protected]
-D: parts of code generator not covered by someone else
-
-N: Eric Christopher
-E: [email protected]
-D: Debug Information, autotools/configure/make build, inline assembly
-
-N: Greg Clayton
-E: [email protected]
-D: LLDB
-
-N: Marshall Clow
-E: [email protected]
-D: libc++
-
-N: Peter Collingbourne
-E: [email protected]
-D: llgo
-
-N: Quentin Colombet
-E: [email protected]
-D: Register allocators
-
-N: Duncan P. N. Exon Smith
-E: [email protected]
-D: Branch weights and BlockFrequencyInfo
-
-N: Hal Finkel
-E: [email protected]
-D: BBVectorize, the loop reroller, alias analysis and the PowerPC target
-
-N: Dan Gohman
-E: [email protected]
-D: WebAssembly Backend (lib/Target/WebAssembly/*)
-
-N: Renato Golin
-E: [email protected]
-D: ARM Linux support
-
-N: Venkatraman Govindaraju
-E: [email protected]
-D: Sparc Backend (lib/Target/Sparc/*)
-
-N: Tobias Grosser
-E: [email protected]
-D: Polly
-
-N: James Grosbach
-E: [email protected]
-D: MC layer
-
-N: Justin Holewinski
-E: [email protected]
-D: NVPTX Target (lib/Target/NVPTX/*)
-
-N: Lang Hames
-E: [email protected]
-D: MCJIT, RuntimeDyld and JIT event listeners
-
-N: Galina Kistanova
-E: [email protected]
-D: LLVM Buildbot
-
-N: Anton Korobeynikov
-E: [email protected]
-D: Exception handling, Windows codegen, ARM EABI
-
-N: Benjamin Kramer
-E: [email protected]
-D: DWARF Parser
-
-N: Sergei Larin
-E: [email protected]
-D: VLIW Instruction Scheduling, Packetization
-
-N: Chris Lattner
-E: [email protected]
-W: http://nondot.org/~sabre/
-D: Everything not covered by someone else
-
-N: David Majnemer
-E: [email protected]
-D: IR Constant Folder, InstCombine
-
-N: Dylan McKay
-E: [email protected]
-D: AVR Backend
-
-N: Tim Northover
-E: [email protected]
-D: AArch64 backend, misc ARM backend
-
-N: Diego Novillo
-E: [email protected]
-D: SampleProfile and related parts of ProfileData
-
-N: Jakob Olesen
-E: [email protected]
-D: TableGen
-
-N: Richard Osborne
-E: [email protected]
-D: XCore Backend
-
-N: Krzysztof Parzyszek
-E: [email protected]
-D: Hexagon Backend
-
-N: Paul Robinson
-E: [email protected]
-D: Sony PlayStation®4 support
-
-N: Chad Rosier
-E: [email protected]
-D: Fast-Isel
-
-N: Nadav Rotem
-E: [email protected]
-D: X86 Backend, Loop Vectorizer
-
-N: Daniel Sanders
-E: [email protected]
-D: MIPS Backend (lib/Target/Mips/*)
-
-N: Duncan Sands
-E: [email protected]
-D: DragonEgg
-
-N: Kostya Serebryany
-E: [email protected]
-D: AddressSanitizer, ThreadSanitizer (LLVM parts)
-
-N: Michael Spencer
-E: [email protected]
-D: Windows parts of Support, Object, ar, nm, objdump, ranlib, size
-
-N: Alexei Starovoitov
-E: [email protected]
-D: BPF backend
-
-N: Tom Stellard
-E: [email protected]
-E: [email protected]
-D: Release manager for the 3.5 and 3.6 branches, R600 Backend, libclc
-
-N: Evgeniy Stepanov
-E: [email protected]
-D: MemorySanitizer (LLVM part)
-
-N: Andrew Trick
-E: [email protected]
-D: IndVar Simplify, Loop Strength Reduction, Instruction Scheduling
-
-N: Ulrich Weigand
-E: [email protected]
-D: SystemZ Backend
-
-N: Bill Wendling
-E: [email protected]
-D: libLTO, IR Linker
-
-N: Peter Zotov
-E: [email protected]
-D: OCaml bindings
-
-N: Andrey Churbanov
-E: [email protected]
-D: OpenMP runtime library

+ 0 - 467
bin/CREDITS.TXT

@@ -1,467 +0,0 @@
-This file is a partial list of people who have contributed to the LLVM
-project.  If you have contributed a patch or made some other contribution to
-LLVM, please submit a patch to this file to add yourself, and it will be
-done!
-
-The list is sorted by surname and formatted to allow easy grepping and
-beautification by scripts.  The fields are: name (N), email (E), web-address
-(W), PGP key ID and fingerprint (P), description (D), snail-mail address
-(S), and (I) IRC handle.
-
-
-N: Vikram Adve
-E: [email protected]
-W: http://www.cs.uiuc.edu/~vadve/
-D: The Sparc64 backend, provider of much wisdom, and motivator for LLVM
-
-N: Owen Anderson
-E: [email protected]
-D: LCSSA pass and related LoopUnswitch work
-D: GVNPRE pass, DataLayout refactoring, random improvements
-
-N: Henrik Bach
-D: MingW Win32 API portability layer
-
-N: Aaron Ballman
-E: [email protected]
-D: __declspec attributes, Windows support, general bug fixing
-
-N: Nate Begeman
-E: [email protected]
-D: PowerPC backend developer
-D: Target-independent code generator and analysis improvements
-
-N: Daniel Berlin
-E: [email protected]
-D: ET-Forest implementation.
-D: Sparse bitmap
-
-N: David Blaikie
-E: [email protected]
-D: General bug fixing/fit & finish, mostly in Clang
-
-N: Neil Booth
-E: [email protected]
-D: APFloat implementation.
-
-N: Misha Brukman
-E: [email protected]
-W: http://misha.brukman.net
-D: Portions of X86 and Sparc JIT compilers, PowerPC backend
-D: Incremental bitcode loader
-
-N: Cameron Buschardt
-E: [email protected]
-D: The `mem2reg' pass - promotes values stored in memory to registers
-
-N: Brendon Cahoon
-E: [email protected]
-D: Loop unrolling with run-time trip counts.
-
-N: Chandler Carruth
-E: [email protected]
-E: [email protected]
-D: Hashing algorithms and interfaces
-D: Inline cost analysis
-D: Machine block placement pass
-D: SROA
-
-N: Casey Carter
-E: [email protected]
-D: Fixes to the Reassociation pass, various improvement patches
-
-N: Evan Cheng
-E: [email protected]
-D: ARM and X86 backends
-D: Instruction scheduler improvements
-D: Register allocator improvements
-D: Loop optimizer improvements
-D: Target-independent code generator improvements
-
-N: Dan Villiom Podlaski Christiansen
-E: [email protected]
-E: [email protected]
-W: http://villiom.dk
-D: LLVM Makefile improvements
-D: Clang diagnostic & driver tweaks
-S: Aarhus, Denmark
-
-N: Jeff Cohen
-E: [email protected]
-W: http://jolt-lang.org
-D: Native Win32 API portability layer
-
-N: John T. Criswell
-E: [email protected]
-D: Original Autoconf support, documentation improvements, bug fixes
-
-N: Anshuman Dasgupta
-E: [email protected]
-D: Deterministic finite automaton based infrastructure for VLIW packetization
-
-N: Stefanus Du Toit
-E: [email protected]
-D: Bug fixes and minor improvements
-
-N: Rafael Avila de Espindola
-E: [email protected]
-D: The ARM backend
-
-N: Dave Estes
-E: [email protected]
-D: AArch64 machine description for Cortex-A53
-
-N: Alkis Evlogimenos
-E: [email protected]
-D: Linear scan register allocator, many codegen improvements, Java frontend
-
-N: Hal Finkel
-E: [email protected]
-D: Basic-block autovectorization, PowerPC backend improvements
-
-N: Eric Fiselier
-E: [email protected]
-D: LIT patches and documentation.
-
-N: Ryan Flynn
-E: [email protected]
-D: Miscellaneous bug fixes
-
-N: Brian Gaeke
-E: [email protected]
-W: http://www.students.uiuc.edu/~gaeke/
-D: Portions of X86 static and JIT compilers; initial SparcV8 backend
-D: Dynamic trace optimizer
-D: FreeBSD/X86 compatibility fixes, the llvm-nm tool
-
-N: Nicolas Geoffray
-E: [email protected]
-W: http://www-src.lip6.fr/homepages/Nicolas.Geoffray/
-D: PPC backend fixes for Linux
-
-N: Louis Gerbarg
-E: [email protected]
-D: Portions of the PowerPC backend
-
-N: Saem Ghani
-E: [email protected]
-D: Callgraph class cleanups
-
-N: Mikhail Glushenkov
-E: [email protected]
-D: Author of llvmc2
-
-N: Dan Gohman
-E: [email protected]
-D: Miscellaneous bug fixes
-D: WebAssembly Backend
-
-N: David Goodwin
-E: [email protected]
-D: Thumb-2 code generator
-
-N: David Greene
-E: [email protected]
-D: Miscellaneous bug fixes
-D: Register allocation refactoring
-
-N: Gabor Greif
-E: [email protected]
-D: Improvements for space efficiency
-
-N: James Grosbach
-E: [email protected]
-I: grosbach
-D: SjLj exception handling support
-D: General fixes and improvements for the ARM back-end
-D: MCJIT
-D: ARM integrated assembler and assembly parser
-D: Led effort for the backend formerly known as ARM64
-
-N: Lang Hames
-E: [email protected]
-D: PBQP-based register allocator
-
-N: Gordon Henriksen
-E: [email protected]
-D: Pluggable GC support
-D: C interface
-D: Ocaml bindings
-
-N: Raul Fernandes Herbster
-E: [email protected]
-D: JIT support for ARM
-
-N: Paolo Invernizzi
-E: [email protected]
-D: Visual C++ compatibility fixes
-
-N: Patrick Jenkins
-E: [email protected]
-D: Nightly Tester
-
-N: Dale Johannesen
-E: [email protected]
-D: ARM constant islands improvements
-D: Tail merging improvements
-D: Rewrite X87 back end
-D: Use APFloat for floating point constants widely throughout compiler
-D: Implement X87 long double
-
-N: Brad Jones
-E: [email protected]
-D: Support for packed types
-
-N: Rod Kay
-E: [email protected]
-D: Author of LLVM Ada bindings
-
-N: Eric Kidd
-W: http://randomhacks.net/
-D: llvm-config script
-
-N: Anton Korobeynikov
-E: [email protected]
-D: Mingw32 fixes, cross-compiling support, stdcall/fastcall calling conv.
-D: x86/linux PIC codegen, aliases, regparm/visibility attributes
-D: Switch lowering refactoring
-
-N: Sumant Kowshik
-E: [email protected]
-D: Author of the original C backend
-
-N: Benjamin Kramer
-E: [email protected]
-D: Miscellaneous bug fixes
-
-N: Sundeep Kushwaha
-E: [email protected]
-D: Implemented DFA-based target independent VLIW packetizer
-
-N: Christopher Lamb
-E: [email protected]
-D: aligned load/store support, parts of noalias and restrict support
-D: vreg subreg infrastructure, X86 codegen improvements based on subregs
-D: address spaces
-
-N: Jim Laskey
-E: [email protected]
-D: Improvements to the PPC backend, instruction scheduling
-D: Debug and Dwarf implementation
-D: Auto upgrade mangler
-D: llvm-gcc4 svn wrangler
-
-N: Chris Lattner
-E: [email protected]
-W: http://nondot.org/~sabre/
-D: Primary architect of LLVM
-
-N: Tanya Lattner (Tanya Brethour)
-E: [email protected]
-W: http://nondot.org/~tonic/
-D: The initial llvm-ar tool, converted regression testsuite to dejagnu
-D: Modulo scheduling in the SparcV9 backend
-D: Release manager (1.7+)
-
-N: Sylvestre Ledru
-E: [email protected]
-W: http://sylvestre.ledru.info/
-W: http://llvm.org/apt/
-D: Debian and Ubuntu packaging
-D: Continuous integration with jenkins
-
-N: Andrew Lenharth
-E: [email protected]
-W: http://www.lenharth.org/~andrewl/
-D: Alpha backend
-D: Sampling based profiling
-
-N: Nick Lewycky
-E: [email protected]
-D: PredicateSimplifier pass
-
-N: Tony Linthicum, et. al.
-E: [email protected]
-D: Backend for Qualcomm's Hexagon VLIW processor.
-
-N: Bruno Cardoso Lopes
-E: [email protected]
-I: bruno
-W: http://brunocardoso.cc
-D: Mips backend
-D: Random ARM integrated assembler and assembly parser improvements
-D: General X86 AVX1 support
-
-N: Duraid Madina
-E: [email protected]
-W: http://kinoko.c.u-tokyo.ac.jp/~duraid/
-D: IA64 backend, BigBlock register allocator
-
-N: John McCall
-E: [email protected]
-D: Clang semantic analysis and IR generation
-
-N: Michael McCracken
-E: [email protected]
-D: Line number support for llvmgcc
-
-N: Vladimir Merzliakov
-E: [email protected]
-D: Test suite fixes for FreeBSD
-
-N: Scott Michel
-E: [email protected]
-D: Added STI Cell SPU backend.
-
-N: Kai Nacke
-E: [email protected]
-D: Support for implicit TLS model used with MS VC runtime
-D: Dumping of Win64 EH structures
-
-N: Takumi Nakamura
-E: [email protected]
-E: [email protected]
-D: Cygwin and MinGW support.
-D: Win32 tweaks.
-S: Yokohama, Japan
-
-N: Edward O'Callaghan
-E: [email protected]
-W: http://www.auroraux.org
-D: Add Clang support with various other improvements to utils/NewNightlyTest.pl
-D: Fix and maintain Solaris & AuroraUX support for llvm, various build warnings
-D: and error clean ups.
-
-N: Morten Ofstad
-E: [email protected]
-D: Visual C++ compatibility fixes
-
-N: Jakob Stoklund Olesen
-E: [email protected]
-D: Machine code verifier
-D: Blackfin backend
-D: Fast register allocator
-D: Greedy register allocator
-
-N: Richard Osborne
-E: [email protected]
-D: XCore backend
-
-N: Devang Patel
-E: [email protected]
-D: LTO tool, PassManager rewrite, Loop Pass Manager, Loop Rotate
-D: GCC PCH Integration (llvm-gcc), llvm-gcc improvements
-D: Optimizer improvements, Loop Index Split
-
-N: Ana Pazos
-E: [email protected]
-D: Fixes and improvements to the AArch64 backend
-
-N: Wesley Peck
-E: [email protected]
-W: http://wesleypeck.com/
-D: MicroBlaze backend
-
-N: Francois Pichet
-E: [email protected]
-D: MSVC support
-
-N: Vladimir Prus
-W: http://vladimir_prus.blogspot.com
-E: [email protected]
-D: Made inst_iterator behave like a proper iterator, LowerConstantExprs pass
-
-N: Kalle Raiskila
-E: [email protected]
-D: Some bugfixes to CellSPU
-
-N: Xerxes Ranby
-E: [email protected]
-D: Cmake dependency chain and various bug fixes
-
-N: Alex Rosenberg
-E: [email protected]
-I: arosenberg
-D: ARM calling conventions rewrite, hard float support
-
-N: Chad Rosier
-E: [email protected]
-I: mcrosier
-D: AArch64 fast instruction selection pass
-D: Fixes and improvements to the ARM fast-isel pass
-D: Fixes and improvements to the AArch64 backend
-
-N: Nadav Rotem
-E: [email protected]
-D: X86 code generation improvements, Loop Vectorizer.
-
-N: Roman Samoilov
-E: [email protected]
-D: MSIL backend
-
-N: Duncan Sands
-E: [email protected]
-I: baldrick
-D: Ada support in llvm-gcc
-D: Dragonegg plugin
-D: Exception handling improvements
-D: Type legalizer rewrite
-
-N: Ruchira Sasanka
-E: [email protected]
-D: Graph coloring register allocator for the Sparc64 backend
-
-N: Arnold Schwaighofer
-E: [email protected]
-D: Tail call optimization for the x86 backend
-
-N: Shantonu Sen
-E: [email protected]
-D: Miscellaneous bug fixes
-
-N: Anand Shukla
-E: [email protected]
-D: The `paths' pass
-
-N: Michael J. Spencer
-E: [email protected]
-D: Shepherding Windows COFF support into MC.
-D: Lots of Windows stuff.
-
-N: Reid Spencer
-E: [email protected]
-W: http://reidspencer.com/
-D: Lots of stuff, see: http://wiki.llvm.org/index.php/User:Reid
-
-N: Alp Toker
-E: [email protected]
-W: http://atoker.com/
-D: C++ frontend next generation standards implementation
-
-N: Craig Topper
-E: [email protected]
-D: X86 codegen and disassembler improvements. AVX2 support.
-
-N: Edwin Torok
-E: [email protected]
-D: Miscellaneous bug fixes
-
-N: Adam Treat
-E: [email protected]
-D: C++ bugs filed, and C++ front-end bug fixes.
-
-N: Lauro Ramos Venancio
-E: [email protected]
-D: ARM backend improvements
-D: Thread Local Storage implementation
-
-N: Bill Wendling
-I: wendling
-E: [email protected]
-D: Release manager, IR Linker, LTO
-D: Bunches of stuff
-
-N: Bob Wilson
-E: [email protected]
-D: Advanced SIMD (NEON) support in the ARM backend.
-

+ 0 - 70
bin/LICENSE.TXT

@@ -1,70 +0,0 @@
-==============================================================================
-LLVM Release License
-==============================================================================
-University of Illinois/NCSA
-Open Source License
-
-Copyright (c) 2003-2015 University of Illinois at Urbana-Champaign.
-All rights reserved.
-
-Developed by:
-
-    LLVM Team
-
-    University of Illinois at Urbana-Champaign
-
-    http://llvm.org
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal with
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-    * Redistributions of source code must retain the above copyright notice,
-      this list of conditions and the following disclaimers.
-
-    * Redistributions in binary form must reproduce the above copyright notice,
-      this list of conditions and the following disclaimers in the
-      documentation and/or other materials provided with the distribution.
-
-    * Neither the names of the LLVM Team, University of Illinois at
-      Urbana-Champaign, nor the names of its contributors may be used to
-      endorse or promote products derived from this Software without specific
-      prior written permission.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
-SOFTWARE.
-
-==============================================================================
-Copyrights and Licenses for Third Party Software Distributed with LLVM:
-==============================================================================
-The LLVM software contains code written by third parties.  Such software will
-have its own individual LICENSE.TXT file in the directory in which it appears.
-This file will describe the copyrights, license, and restrictions which apply
-to that code.
-
-The disclaimer of warranty in the University of Illinois Open Source License
-applies to all code in the LLVM Distribution, and nothing in any of the
-other licenses gives permission to use the names of the LLVM Team or the
-University of Illinois to endorse or promote products derived from this
-Software.
-
-The following pieces of software have additional or alternate copyrights,
-licenses, and/or restrictions:
-
-Program             Directory
--------             ---------
-Autoconf            llvm/autoconf
-                    llvm/projects/ModuleMaker/autoconf
-Google Test         llvm/utils/unittest/googletest
-OpenBSD regex       llvm/lib/Support/{reg*, COPYRIGHT.regex}
-pyyaml tests        llvm/test/YAMLParser/{*.data, LICENSE.TXT}
-ARM contributions   llvm/lib/Target/ARM/LICENSE.TXT
-md5 contributions   llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h

BIN
bin/llc.exe


BIN
bin/lli.exe


BIN
bin/opt.exe


+ 6 - 10
build.bat

@@ -44,16 +44,12 @@ del *.ilk > NUL 2> NUL
 
 cl %compiler_settings% "src\main.c" ^
 	/link %linker_settings% -OUT:%exe_name% ^
-	&& odin build code/Jaze/src/main.odin
-	rem && odin run code/demo.odin
-	rem && odin build_dll code/example.odin ^
-	rem odin run code/demo.odin
-
-rem pushd src\asm
-rem 	nasm hellope.asm -fwin64 -o hellope.obj ^
-rem 	&& cl /nologo hellope.obj /link kernel32.lib /entry:main  ^
-rem 	&& hellope.exe
-rem popd
+	&& odin run code/demo.odin
+	rem && odin build code/metagen.odin ^
+	rem && call "code\metagen.exe" "src\ast_nodes.metagen"
+	rem && odin run code/Jaze/src/main.odin
+
+del *.obj > NUL 2> NUL
 
 :end_of_build
 

+ 221 - 81
code/demo.odin

@@ -7,53 +7,21 @@
 #import "os.odin";
 #import "strconv.odin";
 #import "sync.odin";
+#import win32 "sys/windows.odin";
 
 main :: proc() {
-	// buf: [64]byte;
-	// // len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64);
-	// x := 624.123;
-	// s := strconv.format_float(buf[..], x, 'f', 6, 64);
-	// fmt.println(s);
-	// fmt.printf("%3d\n", 102);
-
-	s := new_slice(int, 0, 10);
-	append(s, 1, 2, 6, 3, 6, 5, 5, 5, 5, 1, 2);
-	fmt.println(s);
-
-
-when false {
 /*
-	Version 0.1.1
-
 	Added:
-	 * Dynamic Arrays `[dynamic]Type`
-	 * Dynamic Maps   `map[Key]Value`
-	 * Dynamic array and map literals
-	 * Custom struct alignemnt `struct #align 8 { bar: i8 }`
-	 * Allow `_` in numbers
-	 * Variadic `append`
-	 * fmt.sprint*
-	 * Entities prefixes with an underscore do not get exported on imports
-	 * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
-	 * enum types have an implict `names` field, a []string of all the names in that enum
-	 * immutable variables are "completely immutable" - rules need a full explanation
-	 * `slice_to_bytes` - convert any slice to a slice of bytes
-	 * `union_cast` allows for optional ok check
-	 * Record type field `names` (struct/raw_union/enum)
-	 * ?: ternary operator
-	 * Unions with variants and common fields
-	 * New built-in procedures
-	     - `delete` to delete map entries `delete(m, key)`
-	     - `clear` to clear dynamic maps and arrays `clear(map_or_array)`
-	     - `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
-	 * Unexported entities and fields using an underscore prefix
+		* Unexported entities and fields using an underscore prefix
+			- See `sync.odin` and explain
 
 	Removed:
 	 * Maybe/option types
 	 * Remove `type` keyword and other "reserved" keywords
-	 * `compile_assert` and `assert`return the value of the condition for semantic reasons
+	 * ..< and ... removed and replace with .. (half-closed range)
 
 	Changed:
+	 * `compile_assert` and `assert`return the value of the condition for semantic reasons
 	 * thread_local -> #thread_local
 	 * #include -> #load
 	 * Files only get checked if they are actually used
@@ -61,20 +29,7 @@ when false {
 	 * Version numbering now starts from 0.1.0 and uses the convention:
 	 	- major.minor.patch
 	 * Core library additions to Windows specific stuff
-
-	Fixes:
-	 * Many fmt.* fixes
-	 * Overloading bug due to comparison of named types
-	 * Overloading bug due to `#import .` collision
-	 * disallow a `cast` from pointers of unions
-	 * Minor bugs in generated IR code for slices
-
-	To come very Soon™:
-	 * Linux and OS X builds (unofficial ones do exist already)
-*/
-	{
-
-	}
+ */
 
 	{
 		Fruit :: enum {
@@ -85,6 +40,161 @@ when false {
 		fmt.println(Fruit.names);
 	}
 
+	{
+		A :: struct           {x, y: f32};
+		B :: struct #align 16 {x, y: f32};
+		fmt.println("align_of(A) =", align_of(A));
+		fmt.println("align_of(B) =", align_of(B));
+	}
+
+	{
+		// Removal of ..< and ...
+		for i in 0..16 {
+		}
+		// Is similar to
+		for _i := 0; _i < 16; _i++ { immutable i := _i;
+		}
+	}
+
+	{
+	#label thing
+		for i in 0..10 {
+			for j := i+1; j < 10; j++ {
+				if j == 2 {
+					fmt.println(i, j);
+					break thing;
+				}
+			}
+		}
+	}
+
+	{
+		t := type_info(int);
+		using Type_Info;
+		match i in t {
+		case Integer, Float:
+			fmt.println("It's a number");
+		}
+
+		x: any = 123;
+		match i in x {
+		case int, f32:
+			fmt.println("It's an int or f32");
+		}
+	}
+
+	{
+		cond := true;
+		x: int;
+		if cond {
+			x = 3;
+		} else {
+			x = 4;
+		}
+
+
+		// Ternary operator
+		y := cond ? 3 : 4;
+
+		FOO :: true ? 123 : 432; // Constant ternary expression
+		fmt.println("Ternary values:", y, FOO);
+	}
+
+	{
+		// Slices now store a capacity
+		buf: [256]byte;
+		s: []byte;
+		s = buf[..0]; // == buf[0..0];
+		fmt.println("count =", s.count);
+		fmt.println("capacity =", s.capacity);
+		append(s, 1, 2, 3);
+		fmt.println(s);
+
+		s = buf[1..2..3];
+		fmt.println("count =", s.count);
+		fmt.println("capacity =", s.capacity);
+		fmt.println(s);
+
+		clear(s); // Sets count to zero
+		s.count = 0; // Equivalent
+	}
+
+	{
+		Foo :: struct {
+			x, y, z: f32,
+			ok:      bool,
+			flags:   u32,
+		}
+		foo_array: [256]Foo;
+		foo_as_bytes: []byte = slice_to_bytes(foo_array[..]);
+		// Useful for things like
+		// os.write(handle, foo_as_bytes);
+
+		foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo));
+		// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
+		// And if so what would the syntax be?
+		// slice_transmute([]Foo, foo_as_bytes);
+	}
+
+	{
+		Vec3 :: [vector 3]f32;
+
+		x := Vec3{1, 2, 3};
+		y := Vec3{4, 5, 6};
+		fmt.println(x < y);
+		fmt.println(x + y);
+		fmt.println(x - y);
+		fmt.println(x * y);
+		fmt.println(x / y);
+
+		for i in x {
+			fmt.println(i);
+		}
+
+		compile_assert(size_of([vector 7]bool) == size_of([7]bool));
+		compile_assert(size_of([vector 7]i32) == size_of([7]i32));
+		// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
+	}
+
+	{
+		// fmt.* changes
+		// bprint* returns `int` (bytes written)
+		// sprint* returns `string` (bytes written as a string)
+
+		data: [256]byte;
+		str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!');
+		fmt.println(str);
+
+		buf := data[..0];
+		count := fmt.bprintf(^buf, "Hellope %d %s %c", 123, "others", '!');
+		fmt.println(cast(string)buf[..count]);
+
+		// NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet
+	}
+
+	{
+		x: [dynamic]f64;
+		reserve(x, 16);
+		defer free(x); // `free` is overloaded for numerous types
+		// Number literals can have underscores in them for readability
+		append(x, 2_000_000.500_000, 3, 5, 7); // variadic append
+
+		for p, i in x {
+			if i > 0 { fmt.print(", "); }
+			fmt.print(p);
+		}
+		fmt.println();
+	}
+
+	{
+		// Dynamic array "literals"
+		x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
+		defer free(x);
+		fmt.println(x); // fmt.print* supports printing of dynamic types
+		clear(x);
+		fmt.println(x);
+	}
+
 	{
 		m: map[f32]int;
 		reserve(m, 16);
@@ -119,49 +229,79 @@ when false {
 		c := m["c"];
 		_, ok := m["c"];
 		assert(ok && c == 7654);
+		fmt.println(m);
 
+		delete(m, "c"); // deletes entry with key "c"
+		_, found := m["c"];
+		assert(!found);
+
+		fmt.println(m);
+		clear(m);
 		fmt.println(m);
+
+		// NOTE: Fixed size maps are planned but we have not yet implemented
+		// them as we have had no need for them as of yet
 	}
 
 	{
-		x: [dynamic]f64;
-		reserve(x, 16);
-		defer free(x);
-		append(x, 2_000_000.500_000, 3, 5, 7);
+		Vector3 :: struct{x, y, z: f32};
+		Quaternion :: struct{x, y, z, w: f32};
 
-		for p, i in x {
-			if i > 0 { fmt.print(", "); }
-			fmt.print(p);
+		Entity :: union {
+			// Common Fields
+			id:             u64,
+			name:           string,
+			using position: Vector3,
+			orientation:    Quaternion,
+			flags:          u32,
+
+			// Variants
+			Frog{
+				ribbit_volume: f32,
+				jump_height:   f32,
+			},
+			Door{
+				openness: f32,
+			},
+			Map{
+				width, height:   f32,
+				place_positions: []Vector3,
+				place_names:     []string,
+			},
 		}
-		fmt.println();
-	}
 
-	{
-		x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
-		defer free(x);
-		fmt.println(x);
-	}
+		entity: Entity;
+		// implicit conversion from variant to base type
+		entity = Entity.Frog{
+			id = 1337,
+			ribbit_volume = 0.5,
+			jump_height = 2.1,
+			/*other data */
+		};
 
+		entity.name = "Frank";
+		entity.position = Vector3{1, 4, 9};
 
-	{
-		Vec3 :: [vector 3]f32;
-
-		x := Vec3{1, 2, 3};
-		y := Vec3{4, 5, 6};
-		fmt.println(x < y);
-		fmt.println(x + y);
-		fmt.println(x - y);
-		fmt.println(x * y);
-		fmt.println(x / y);
+		using Entity;
+		match e in entity {
+		case Frog:
+			fmt.println("Ribbit");
+		case Door:
+			fmt.println("Creak");
+		case Map:
+			fmt.println("Rustle");
+		default:
+			fmt.println("Just a normal entity");
+		}
 
-		for i in x {
-			fmt.println(i);
+		if frog, ok := union_cast(Frog)entity; ok {
+			fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
 		}
 
-		compile_assert(size_of([vector 7]bool) == size_of([7]bool));
-		compile_assert(size_of([vector 7]i32) == size_of([7]i32));
-		// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
+		// Panics if not the correct type
+		frog: Frog;
+		frog = union_cast(Frog)entity;
+		frog, _ = union_cast(Frog)entity; // ignore error and force cast
 	}
 }
-}
 

+ 34 - 3
core/_preload.odin

@@ -128,9 +128,6 @@ __debug_trap       :: proc()        #foreign __llvm_core "llvm.debugtrap";
 __trap             :: proc()        #foreign __llvm_core "llvm.trap";
 read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
 
-__cpuid :: proc(level: u32, sig: ^u32) -> i32 #foreign __llvm_core "__get_cpuid";
-
-
 
 
 
@@ -350,6 +347,40 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline {
 }
 
 
+__mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
+	llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
+	llvm_memset_64bit(data, cast(byte)value, len, 1, false);
+	return data;
+}
+__mem_zero :: proc(data: rawptr, len: int) -> rawptr {
+	return __mem_set(data, 0, len);
+}
+__mem_copy :: proc(dst, src: rawptr, len: int) -> rawptr {
+	// NOTE(bill): This _must_ be implemented like C's memmove
+	llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
+	llvm_memmove_64bit(dst, src, len, 1, false);
+	return dst;
+}
+__mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
+	// NOTE(bill): This _must_ be implemented like C's memcpy
+	llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
+	llvm_memcpy_64bit(dst, src, len, 1, false);
+	return dst;
+}
+
+__mem_compare :: proc(a, b: ^byte, n: int) -> int {
+	for i in 0..n {
+		match {
+		case (a+i)^ < (b+i)^:
+			return -1;
+		case (a+i)^ > (b+i)^:
+			return +1;
+		}
+	}
+	return 0;
+}
+
+
 Raw_Any :: struct #ordered {
 	type_info: ^Type_Info,
 	data:      rawptr,

+ 66 - 35
core/fmt.odin

@@ -102,7 +102,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
 		default:
 			write_string(buf, info.signed ? "i" : "u");
 			fi := Fmt_Info{buf = buf};
-			fmt_int(^fi, cast(u64)(8*info.size), false, 'd');
+			fmt_int(^fi, cast(u64)(8*info.size), false, 64, 'd');
 		}
 
 	case Float:
@@ -155,7 +155,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
 	case Array:
 		write_string(buf, "[");
 		fi := Fmt_Info{buf = buf};
-		fmt_int(^fi, cast(u64)info.count, false, 'd');
+		fmt_int(^fi, cast(u64)info.count, false, 64, 'd');
 		write_string(buf, "]");
 		write_type(buf, info.elem);
 	case Dynamic_Array:
@@ -168,7 +168,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
 	case Vector:
 		write_string(buf, "[vector ");
 		fi := Fmt_Info{buf = buf};
-		fmt_int(^fi, cast(u64)info.count, false, 'd');
+		fmt_int(^fi, cast(u64)info.count, false, 64, 'd');
 		write_string(buf, "]");
 		write_type(buf, info.elem);
 
@@ -185,7 +185,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
 		if info.custom_align {
 			write_string(buf, "#align ");
 			fi := Fmt_Info{buf = buf};
-			fmt_int(^fi, cast(u64)info.align, false, 'd');
+			fmt_int(^fi, cast(u64)info.align, false, 64, 'd');
 			write_byte(buf, ' ');
 		}
 		write_byte(buf, '{');
@@ -295,15 +295,15 @@ bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
 
 sprint :: proc(buf: []byte, args: ..any) -> string {
 	count := bprint(^buf, ..args);
-	return cast(string)buf;
+	return cast(string)buf[..count];
 }
 sprintln :: proc(buf: []byte, args: ..any) -> string {
 	count := bprintln(^buf, ..args);
-	return cast(string)buf;
+	return cast(string)buf[..count];
 }
 sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
 	count := bprintf(^buf, fmt, ..args);
-	return cast(string)buf;
+	return cast(string)buf[..count];
 }
 
 
@@ -435,16 +435,47 @@ fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) {
 	}
 }
 
-_write_int :: proc(fi: ^Fmt_Info, u: u64, base: int, neg: bool, digits: string) {
-	if neg {
-		u = -u;
+
+is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
+	neg := false;
+	if is_signed {
+		match bit_size {
+		case 8:
+			i := cast(i8)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		case 16:
+			i := cast(i16)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		case 32:
+			i := cast(i32)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		case 64:
+			i := cast(i64)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		default:
+			panic("is_integer_negative: Unknown integer size");
+		}
 	}
+	return u, neg;
+}
+
+_write_int :: proc(fi: ^Fmt_Info, u: u64, base: int, is_signed: bool, bit_size: int, digits: string) {
+	_, neg := is_integer_negative(u, is_signed, bit_size);
+
 	BUF_SIZE :: 256;
 	if fi.width_set || fi.prec_set {
 		width := fi.width + fi.prec + 3; // 3 extra bytes for sign and prefix
 		if width > BUF_SIZE {
 			// TODO(bill):????
-			panic("_write_int buffer overrun. Width and precision too big");
+			panic("_write_int: buffer overrun. Width and precision too big");
 		}
 	}
 
@@ -467,7 +498,7 @@ _write_int :: proc(fi: ^Fmt_Info, u: u64, base: int, neg: bool, digits: string)
 	}
 
 	match base {
-	case 2, 8, 10, 16:
+	case 2, 8, 10, 12, 16:
 		break;
 	default:
 		panic("_write_int: unknown base, whoops");
@@ -475,10 +506,10 @@ _write_int :: proc(fi: ^Fmt_Info, u: u64, base: int, neg: bool, digits: string)
 
 	buf: [256]byte;
 	flags: strconv.Int_Flag;
-	if fi.hash  { flags |= strconv.Int_Flag.PREFIX; }
-	if fi.plus  { flags |= strconv.Int_Flag.PLUS; }
-	if fi.space { flags |= strconv.Int_Flag.SPACE; }
-	s := strconv.append_bits(buf[..0], u, base, neg, digits, flags);
+	if fi.hash   { flags |= strconv.Int_Flag.PREFIX; }
+	if fi.plus   { flags |= strconv.Int_Flag.PLUS; }
+	if fi.space  { flags |= strconv.Int_Flag.SPACE; }
+	s := strconv.append_bits(buf[..0], u, base, is_signed, bit_size, digits, flags);
 
 	prev_zero := fi.zero;
 	defer fi.zero = prev_zero;
@@ -493,14 +524,14 @@ fmt_rune :: proc(fi: ^Fmt_Info, r: rune) {
 	write_rune(fi.buf, r);
 }
 
-fmt_int :: proc(fi: ^Fmt_Info, u: u64, neg: bool, verb: rune) {
+fmt_int :: proc(fi: ^Fmt_Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
 	match verb {
-	case 'v': _write_int(fi, u, 10, neg, __DIGITS_LOWER);
-	case 'b': _write_int(fi, u,  2, neg, __DIGITS_LOWER);
-	case 'o': _write_int(fi, u,  8, neg, __DIGITS_LOWER);
-	case 'd': _write_int(fi, u, 10, neg, __DIGITS_LOWER);
-	case 'x': _write_int(fi, u, 16, neg, __DIGITS_LOWER);
-	case 'X': _write_int(fi, u, 16, neg, __DIGITS_UPPER);
+	case 'v': _write_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER);
+	case 'b': _write_int(fi, u,  2, is_signed, bit_size, __DIGITS_LOWER);
+	case 'o': _write_int(fi, u,  8, is_signed, bit_size, __DIGITS_LOWER);
+	case 'd': _write_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER);
+	case 'x': _write_int(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER);
+	case 'X': _write_int(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER);
 	case 'c', 'r':
 		fmt_rune(fi, cast(rune)u);
 	case 'U':
@@ -509,7 +540,7 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, neg: bool, verb: rune) {
 			fmt_bad_verb(fi, verb);
 		} else {
 			write_string(fi.buf, "U+");
-			_write_int(fi, u, 16, false, __DIGITS_UPPER);
+			_write_int(fi, u, 16, false, bit_size, __DIGITS_UPPER);
 		}
 
 	default:
@@ -598,7 +629,7 @@ fmt_pointer :: proc(fi: ^Fmt_Info, p: rawptr, verb: rune) {
 	if !fi.hash || verb == 'v' {
 		write_string(fi.buf, "0x");
 	}
-	_write_int(fi, u, 16, false, __DIGITS_UPPER);
+	_write_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
 }
 
 fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
@@ -876,16 +907,16 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 	case f32:     fmt_float(fi, cast(f64)a, 32, verb);
 	case f64:     fmt_float(fi, a, 64, verb);
 
-	case int:     fmt_int(fi, cast(u64)a, a < 0, verb);
-	case i8:      fmt_int(fi, cast(u64)a, a < 0, verb);
-	case i16:     fmt_int(fi, cast(u64)a, a < 0, verb);
-	case i32:     fmt_int(fi, cast(u64)a, a < 0, verb);
-	case i64:     fmt_int(fi, cast(u64)a, a < 0, verb);
-	case uint:    fmt_int(fi, cast(u64)a, false, verb);
-	case u8:      fmt_int(fi, cast(u64)a, false, verb);
-	case u16:     fmt_int(fi, cast(u64)a, false, verb);
-	case u32:     fmt_int(fi, cast(u64)a, false, verb);
-	case u64:     fmt_int(fi, cast(u64)a, false, verb);
+	case int:     fmt_int(fi, cast(u64)a, true,  8*size_of(int), verb);
+	case i8:      fmt_int(fi, cast(u64)a, true,  8, verb);
+	case i16:     fmt_int(fi, cast(u64)a, true,  16, verb);
+	case i32:     fmt_int(fi, cast(u64)a, true,  32, verb);
+	case i64:     fmt_int(fi, cast(u64)a, true,  64, verb);
+	case uint:    fmt_int(fi, cast(u64)a, false, 8*size_of(uint), verb);
+	case u8:      fmt_int(fi, cast(u64)a, false, 8, verb);
+	case u16:     fmt_int(fi, cast(u64)a, false, 16, verb);
+	case u32:     fmt_int(fi, cast(u64)a, false, 32, verb);
+	case u64:     fmt_int(fi, cast(u64)a, false, 64, verb);
 	case string:  fmt_string(fi, a, verb);
 	default:      fmt_value(fi, arg, verb);
 	}

+ 36 - 31
core/math.odin

@@ -27,14 +27,14 @@ Mat4 :: [4]Vec4;
 sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
 sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
 
-sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
-sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
+sin  :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
+sin  :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
 
-cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
-cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
+cos  :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
+cos  :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
 
-tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
-tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
+tan  :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
+tan  :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
 
 lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
 lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
@@ -53,36 +53,42 @@ fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
 copy_sign :: proc(x, y: f32) -> f32 {
 	ix := transmute(u32)x;
 	iy := transmute(u32)y;
-	ix &= 0x7fffffff;
-	ix |= iy & 0x80000000;
+	ix &= 0x7fff_ffff;
+	ix |= iy & 0x8000_0000;
 	return transmute(f32)ix;
 }
-round :: proc(x: f32) -> f32 {
-	if x >= 0 {
-		return floor(x + 0.5);
-	}
-	return ceil(x - 0.5);
-}
-floor :: proc(x: f32) -> f32 {
-	if x >= 0 {
-		return cast(f32)cast(int)x;
-	}
-	return cast(f32)cast(int)(x-0.5);
-}
-ceil :: proc(x: f32) -> f32 {
-	if x < 0 {
-		return cast(f32)cast(int)x;
-	}
-	return cast(f32)cast(int)(x+1);
-}
 
-remainder32 :: proc(x, y: f32) -> f32 {
-	return x - round(x/y) * y;
+copy_sign :: proc(x, y: f64) -> f64 {
+	ix := transmute(u64)x;
+	iy := transmute(u64)y;
+	ix &= 0x7fff_ffff_ffff_ff;
+	ix |= iy & 0x8000_0000_0000_0000;
+	return transmute(f64)ix;
 }
 
-fmod32 :: proc(x, y: f32) -> f32 {
+round     :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
+round     :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
+
+floor     :: proc(x: f32) -> f32 { return x >= 0 ? cast(f32)cast(i64)x : cast(f32)cast(i64)(x-0.5); } // TODO: Get accurate versions
+floor     :: proc(x: f64) -> f64 { return x >= 0 ? cast(f64)cast(i64)x : cast(f64)cast(i64)(x-0.5); } // TODO: Get accurate versions
+
+ceil      :: proc(x: f32) -> f32 { return x <  0 ? cast(f32)cast(i64)x : cast(f32)cast(i64)(x+1); } // TODO: Get accurate versions
+ceil      :: proc(x: f64) -> f64 { return x <  0 ? cast(f64)cast(i64)x : cast(f64)cast(i64)(x+1); } // TODO: Get accurate versions
+
+remainder :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
+remainder :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
+
+mod :: proc(x, y: f32) -> f32 {
+	y = abs(y);
+	result := remainder(abs(x), y);
+	if sign(result) < 0 {
+		result += y;
+	}
+	return copy_sign(result, x);
+}
+mod :: proc(x, y: f64) -> f64 {
 	y = abs(y);
-	result := remainder32(abs(x), y);
+	result := remainder(abs(x), y);
 	if sign(result) < 0 {
 		result += y;
 	}
@@ -95,7 +101,6 @@ to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
 
 
 
-
 dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
 dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
 dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }

+ 10 - 31
core/mem.odin

@@ -6,41 +6,20 @@ swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
 swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
 
 
-set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
-	llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
-	llvm_memset_64bit(data, cast(byte)value, len, 1, false);
-	return data;
+set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
+	return __mem_set(data, value, len);
 }
-
-zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" {
-	return set(data, 0, len);
+zero :: proc(data: rawptr, len: int) -> rawptr {
+	return __mem_zero(data, len);
 }
-
-copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
-	// NOTE(bill): This _must_ be implemented like C's memmove
-	llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
-	llvm_memmove_64bit(dst, src, len, 1, false);
-	return dst;
+copy :: proc(dst, src: rawptr, len: int) -> rawptr {
+	return __mem_copy(dst, src, len);
 }
-
-copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
-	// NOTE(bill): This _must_ be implemented like C's memcpy
-	llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
-	llvm_memcpy_64bit(dst, src, len, 1, false);
-	return dst;
+copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
+	return __mem_copy_non_overlapping(dst, src, len);
 }
-
-compare :: proc(a, b: []byte) -> int #link_name "__mem_compare" {
-	n := min(a.count, b.count);
-	for i in 0..n {
-		match {
-		case a[i] < b[i]:
-			return -1;
-		case a[i] > b[i]:
-			return +1;
-		}
-	}
-	return 0;
+compare :: proc(a, b: []byte) -> int {
+	return __mem_compare(a.data, b.data, min(a.count, b.count));
 }
 
 

+ 44 - 46
core/os_windows.odin

@@ -1,4 +1,4 @@
-#import win32 "sys/windows.odin";
+#import w "sys/windows.odin";
 #import "fmt.odin";
 
 
@@ -53,29 +53,28 @@ ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
 
 
 open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
-	using win32;
 	if path.count == 0 {
 		return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
 	}
 
 	access: u32;
 	match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
-	case O_RDONLY: access = FILE_GENERIC_READ;
-	case O_WRONLY: access = FILE_GENERIC_WRITE;
-	case O_RDWR:   access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+	case O_RDONLY: access = w.FILE_GENERIC_READ;
+	case O_WRONLY: access = w.FILE_GENERIC_WRITE;
+	case O_RDWR:   access = w.FILE_GENERIC_READ | w.FILE_GENERIC_WRITE;
 	}
 
 	if mode&O_CREAT != 0 {
-		access |= FILE_GENERIC_WRITE;
+		access |= w.FILE_GENERIC_WRITE;
 	}
 	if mode&O_APPEND != 0 {
-		access &~= FILE_GENERIC_WRITE;
-		access |= FILE_APPEND_DATA;
+		access &~= w.FILE_GENERIC_WRITE;
+		access |=  w.FILE_APPEND_DATA;
 	}
 
-	share_mode := cast(u32)(FILE_SHARE_READ|FILE_SHARE_WRITE);
-	sa: ^SECURITY_ATTRIBUTES = nil;
-	sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
+	share_mode := cast(u32)(w.FILE_SHARE_READ|w.FILE_SHARE_WRITE);
+	sa: ^w.Security_Attributes = nil;
+	sa_inherit := w.Security_Attributes{length = size_of(w.Security_Attributes), inherit_handle = 1};
 	if mode&O_CLOEXEC == 0 {
 		sa = ^sa_inherit;
 	}
@@ -83,37 +82,37 @@ open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
 	create_mode: u32;
 	match {
 	case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
-		create_mode = CREATE_NEW;
+		create_mode = w.CREATE_NEW;
 	case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
-		create_mode = CREATE_ALWAYS;
+		create_mode = w.CREATE_ALWAYS;
 	case mode&O_CREAT == O_CREAT:
-		create_mode = OPEN_ALWAYS;
+		create_mode = w.OPEN_ALWAYS;
 	case mode&O_TRUNC == O_TRUNC:
-		create_mode = TRUNCATE_EXISTING;
+		create_mode = w.TRUNCATE_EXISTING;
 	default:
-		create_mode = OPEN_EXISTING;
+		create_mode = w.OPEN_EXISTING;
 	}
 
 	buf: [300]byte;
 	copy(buf[..], cast([]byte)path);
 
-	handle := cast(Handle)CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil);
+	handle := cast(Handle)w.CreateFileA(^buf[0], access, share_mode, sa, create_mode, w.FILE_ATTRIBUTE_NORMAL, nil);
 	if handle != INVALID_HANDLE {
 		return handle, ERROR_NONE;
 	}
-	err := GetLastError();
+	err := w.GetLastError();
 	return INVALID_HANDLE, cast(Errno)err;
 }
 
 close :: proc(fd: Handle) {
-	win32.CloseHandle(cast(win32.HANDLE)fd);
+	w.CloseHandle(cast(w.Handle)fd);
 }
 
 write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	bytes_written: i32;
-	e := win32.WriteFile(cast(win32.HANDLE)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
-	if e == win32.FALSE {
-		err := win32.GetLastError();
+	e := w.WriteFile(cast(w.Handle)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
+	if e == w.FALSE {
+		err := w.GetLastError();
 		return 0, cast(Errno)err;
 	}
 	return cast(int)bytes_written, ERROR_NONE;
@@ -121,16 +120,16 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 
 read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	bytes_read: i32;
-	e := win32.ReadFile(cast(win32.HANDLE)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
-	if e == win32.FALSE {
-		err := win32.GetLastError();
+	e := w.ReadFile(cast(w.Handle)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
+	if e == w.FALSE {
+		err := w.GetLastError();
 		return 0, cast(Errno)err;
 	}
 	return cast(int)bytes_read, ERROR_NONE;
 }
 
 seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
-	using win32;
+	using w;
 	w: u32;
 	match whence {
 	case 0: w = FILE_BEGIN;
@@ -139,11 +138,11 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
 	}
 	hi := cast(i32)(offset>>32);
 	lo := cast(i32)(offset);
-	ft := GetFileType(cast(HANDLE)fd);
+	ft := GetFileType(cast(Handle)fd);
 	if ft == FILE_TYPE_PIPE {
 		return 0, ERROR_FILE_IS_PIPE;
 	}
-	dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
+	dw_ptr := SetFilePointer(cast(Handle)fd, lo, ^hi, w);
 	if dw_ptr == INVALID_SET_FILE_POINTER {
 		err := GetLastError();
 		return 0, cast(Errno)err;
@@ -153,14 +152,14 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
 
 
 // NOTE(bill): Uses startup to initialize it
-stdin  := get_std_handle(win32.STD_INPUT_HANDLE);
-stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
-stderr := get_std_handle(win32.STD_ERROR_HANDLE);
+stdin  := get_std_handle(w.STD_INPUT_HANDLE);
+stdout := get_std_handle(w.STD_OUTPUT_HANDLE);
+stderr := get_std_handle(w.STD_ERROR_HANDLE);
 
 
 get_std_handle :: proc(h: int) -> Handle {
-	fd := win32.GetStdHandle(cast(i32)h);
-	win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
+	fd := w.GetStdHandle(cast(i32)h);
+	w.SetHandleInformation(fd, w.HANDLE_FLAG_INHERIT, 0);
 	return cast(Handle)fd;
 }
 
@@ -170,23 +169,23 @@ get_std_handle :: proc(h: int) -> Handle {
 
 
 last_write_time :: proc(fd: Handle) -> File_Time {
-	file_info: win32.BY_HANDLE_FILE_INFORMATION;
-	win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
+	file_info: w.By_Handle_File_Information;
+	w.GetFileInformationByHandle(cast(w.Handle)fd, ^file_info);
 	lo := cast(File_Time)file_info.last_write_time.lo;
 	hi := cast(File_Time)file_info.last_write_time.hi;
 	return lo | hi << 32;
 }
 
 last_write_time_by_name :: proc(name: string) -> File_Time {
-	last_write_time: win32.FILETIME;
-	data: win32.FILE_ATTRIBUTE_DATA;
+	last_write_time: w.Filetime;
+	data: w.File_Attribute_Data;
 	buf: [1024]byte;
 
 	assert(buf.count > name.count);
 
 	copy(buf[..], cast([]byte)name);
 
-	if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
+	if w.GetFileAttributesExA(^buf[0], w.GetFileExInfoStandard, ^data) != 0 {
 		last_write_time = data.last_write_time;
 	}
 
@@ -210,7 +209,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 	defer close(fd);
 
 	length: i64;
-	file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
+	file_size_ok := w.GetFileSizeEx(cast(w.Handle)fd, ^length) != 0;
 	if !file_size_ok {
 		return nil, false;
 	}
@@ -233,7 +232,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 			to_read = MAX;
 		}
 
-		win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
+		w.ReadFile(cast(w.Handle)fd, ^data[total_read], to_read, ^single_read_length, nil);
 		if single_read_length <= 0 {
 			free(data);
 			return nil, false;
@@ -248,8 +247,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 
 
 heap_alloc :: proc(size: int) -> rawptr {
-	assert(size > 0);
-	return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
+	return w.HeapAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, size);
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	if new_size == 0 {
@@ -259,24 +257,24 @@ heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	if ptr == nil {
 		return heap_alloc(new_size);
 	}
-	return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
+	return w.HeapReAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, ptr, new_size);
 }
 heap_free :: proc(ptr: rawptr) {
 	if ptr == nil {
 		return;
 	}
-	win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
+	w.HeapFree(w.GetProcessHeap(), 0, ptr);
 }
 
 
 exit :: proc(code: int) {
-	win32.ExitProcess(cast(u32)code);
+	w.ExitProcess(cast(u32)code);
 }
 
 
 
 current_thread_id :: proc() -> int {
-	return cast(int)win32.GetCurrentThreadId();
+	return cast(int)w.GetCurrentThreadId();
 }
 
 

+ 40 - 10
core/strconv.odin

@@ -25,15 +25,12 @@ append_bool :: proc(buf: []byte, b: bool) -> string {
 }
 
 append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
-	using Int_Flag;
-	return append_bits(buf, u, base, false, digits, 0);
+	return append_bits(buf, u, base, false, 8*size_of(uint), digits, 0);
 }
 append_int :: proc(buf: []byte, i: i64, base: int) -> string {
-	return append_bits(buf, cast(u64)i, base, i < 0, digits, 0);
-}
-itoa :: proc(buf: []byte, i: int) -> string {
-	return append_int(buf, cast(i64)i, 10);
+	return append_bits(buf, cast(u64)i, base, true, 8*size_of(int), digits, 0);
 }
+itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, cast(i64)i, 10); }
 
 append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
 	return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size);
@@ -266,7 +263,39 @@ MAX_BASE :: 32;
 immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz";
 
 
-append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, flags: Int_Flag) -> string {
+is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
+	neg := false;
+	if is_signed {
+		match bit_size {
+		case 8:
+			i := cast(i8)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		case 16:
+			i := cast(i16)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		case 32:
+			i := cast(i32)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		case 64:
+			i := cast(i64)u;
+			neg = i < 0;
+			if neg { i = -i; }
+			u = cast(u64)i;
+		default:
+			panic("is_integer_negative: Unknown integer size");
+		}
+	}
+	return u, neg;
+}
+
+
+append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flag) -> string {
 	is_pow2 :: proc(x: i64) -> bool {
 		if (x <= 0) {
 			return false;
@@ -280,9 +309,9 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
 
 	a: [65]byte;
 	i := a.count;
-	if neg {
-		u = -u;
-	}
+
+	neg: bool;
+	u, neg = is_integer_negative(u, is_signed, bit_size);
 
 	if is_pow2(cast(i64)base) {
 		b := cast(u64)base;
@@ -313,6 +342,7 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
 		case 2:  i--; a[i] = 'b';
 		case 8:  i--; a[i] = 'o';
 		case 10: i--; a[i] = 'd';
+		case 12: i--; a[i] = 'z';
 		case 16: i--; a[i] = 'x';
 		default: ok = false;
 		}

+ 1 - 1
core/sync.odin

@@ -2,7 +2,7 @@
 #import "atomic.odin";
 
 Semaphore :: struct {
-	_handle: win32.HANDLE,
+	_handle: win32.Handle,
 }
 
 Mutex :: struct {

+ 25 - 25
core/sys/wgl.odin

@@ -8,10 +8,10 @@ CONTEXT_PROFILE_MASK_ARB           :: 0x9126;
 CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002;
 CONTEXT_CORE_PROFILE_BIT_ARB       :: 0x00000001;
 
-HGLRC :: HANDLE;
-COLORREF :: u32;
+Hglrc :: Handle;
+Color_Ref :: u32;
 
-LAYERPLANEDESCRIPTOR :: struct #ordered {
+Layer_Plane_Descriptor :: struct #ordered {
 	size:             u16,
 	version:          u16,
 	flags:            u32,
@@ -35,38 +35,38 @@ LAYERPLANEDESCRIPTOR :: struct #ordered {
 	aux_buffers:      byte,
 	layer_type:       byte,
 	reserved:         byte,
-	transparent:      COLORREF,
+	transparent:      Color_Ref,
 }
 
-POINTFLOAT :: struct #ordered {
+Point_Float :: struct #ordered {
 	x, y: f32,
 }
 
-GLYPHMETRICSFLOAT :: struct #ordered {
+Glyph_Metrics_Float :: struct #ordered {
 	black_box_x:  f32,
 	black_box_y:  f32,
-	glyph_origin: POINTFLOAT,
+	glyph_origin: Point_Float,
 	cell_inc_x:   f32,
 	cell_inc_y:   f32,
 }
 
-CreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC;
-ChoosePixelFormatARBType    :: #type proc(hdc: HDC, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> BOOL #cc_c;
+Create_Context_Attribs_ARB_Type :: #type proc(hdc: Hdc, hshareContext: rawptr, attribList: ^i32) -> Hglrc;
+Choose_Pixel_Format_ARB_Type    :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
 
 
-CreateContext           :: proc(hdc: HDC) -> HGLRC                                                                                               #foreign opengl32 "wglCreateContext";
-MakeCurrent             :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL                                                                                  #foreign opengl32 "wglMakeCurrent";
-GetProcAddress          :: proc(c_str: ^u8) -> PROC                                                                                              #foreign opengl32 "wglGetProcAddress";
-DeleteContext           :: proc(hglrc: HGLRC) -> BOOL                                                                                            #foreign opengl32 "wglDeleteContext";
-CopyContext             :: proc(src, dst: HGLRC, mask: u32) -> BOOL                                                                              #foreign opengl32 "wglCopyContext";
-CreateLayerContext      :: proc(hdc: HDC, layer_plane: i32) -> HGLRC                                                                             #foreign opengl32 "wglCreateLayerContext";
-DescribeLayerPlane      :: proc(hdc: HDC, pixel_format, layer_plane: i32, bytes: u32, pd: ^LAYERPLANEDESCRIPTOR) -> BOOL                         #foreign opengl32 "wglDescribeLayerPlane";
-GetCurrentContext       :: proc() -> HGLRC                                                                                                       #foreign opengl32 "wglGetCurrentContext";
-GetCurrentDC            :: proc() -> HDC                                                                                                         #foreign opengl32 "wglGetCurrentDC";
-GetLayerPaletteEntries  :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32                                                #foreign opengl32 "wglGetLayerPaletteEntries";
-RealizeLayerPalette     :: proc(hdc: HDC, layer_plane: i32, realize: BOOL) -> BOOL                                                               #foreign opengl32 "wglRealizeLayerPalette";
-SetLayerPaletteEntries  :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32                                                #foreign opengl32 "wglSetLayerPaletteEntries";
-ShareLists              :: proc(hglrc1, hglrc2: HGLRC) -> BOOL                                                                                   #foreign opengl32 "wglShareLists";
-SwapLayerBuffers        :: proc(hdc: HDC, planes: u32) -> BOOL                                                                                   #foreign opengl32 "wglSwapLayerBuffers";
-UseFontBitmaps          :: proc(hdc: HDC, first, count, list_base: u32) -> BOOL                                                                  #foreign opengl32 "wglUseFontBitmaps";
-UseFontOutlines         :: proc(hdc: HDC, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^GLYPHMETRICSFLOAT) -> BOOL #foreign opengl32 "wglUseFontOutlines";
+CreateContext           :: proc(hdc: Hdc) -> Hglrc                                                                                                 #foreign opengl32 "wglCreateContext";
+MakeCurrent             :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool                                                                                    #foreign opengl32 "wglMakeCurrent";
+GetProcAddress          :: proc(c_str: ^u8) -> Proc                                                                                                #foreign opengl32 "wglGetProcAddress";
+DeleteContext           :: proc(hglrc: Hglrc) -> Bool                                                                                              #foreign opengl32 "wglDeleteContext";
+CopyContext             :: proc(src, dst: Hglrc, mask: u32) -> Bool                                                                                #foreign opengl32 "wglCopyContext";
+CreateLayerContext      :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc                                                                               #foreign opengl32 "wglCreateLayerContext";
+DescribeLayerPlane      :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^Layer_Plane_Descriptor) -> Bool                         #foreign opengl32 "wglDescribeLayerPlane";
+GetCurrentContext       :: proc() -> Hglrc                                                                                                         #foreign opengl32 "wglGetCurrentContext";
+GetCurrentDC            :: proc() -> Hdc                                                                                                           #foreign opengl32 "wglGetCurrentDC";
+GetLayerPaletteEntries  :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32                                                 #foreign opengl32 "wglGetLayerPaletteEntries";
+RealizeLayerPalette     :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool                                                                 #foreign opengl32 "wglRealizeLayerPalette";
+SetLayerPaletteEntries  :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32                                                 #foreign opengl32 "wglSetLayerPaletteEntries";
+ShareLists              :: proc(hglrc1, hglrc2: Hglrc) -> Bool                                                                                     #foreign opengl32 "wglShareLists";
+SwapLayerBuffers        :: proc(hdc: Hdc, planes: u32) -> Bool                                                                                     #foreign opengl32 "wglSwapLayerBuffers";
+UseFontBitmaps          :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool                                                                    #foreign opengl32 "wglUseFontBitmaps";
+UseFontOutlines         :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_Metrics_Float) -> Bool #foreign opengl32 "wglUseFontOutlines";

+ 135 - 133
core/sys/windows.odin

@@ -2,29 +2,29 @@
 #foreign_system_library "user32.lib"   when ODIN_OS == "windows";
 #foreign_system_library "gdi32.lib"    when ODIN_OS == "windows";
 #foreign_system_library "winmm.lib"    when ODIN_OS == "windows";
-
-HANDLE    :: rawptr;
-HWND      :: HANDLE;
-HDC       :: HANDLE;
-HINSTANCE :: HANDLE;
-HICON     :: HANDLE;
-HCURSOR   :: HANDLE;
-HMENU     :: HANDLE;
-HBRUSH    :: HANDLE;
-HGDIOBJ   :: HANDLE;
-HMODULE   :: HANDLE;
-WPARAM    :: uint;
-LPARAM    :: int;
-LRESULT   :: int;
-ATOM      :: i16;
-BOOL      :: i32;
-WNDPROC   :: #type proc(HWND, u32, WPARAM, LPARAM) -> LRESULT #cc_c;
-
-
-INVALID_HANDLE_VALUE :: cast(HANDLE)~cast(int)0;
-
-FALSE: BOOL : 0;
-TRUE:  BOOL : 1;
+#foreign_system_library "shell32.lib"  when ODIN_OS == "windows";
+
+Handle    :: rawptr;
+Hwnd      :: Handle;
+Hdc       :: Handle;
+Hinstance :: Handle;
+Hicon     :: Handle;
+Hcursor   :: Handle;
+Hmenu     :: Handle;
+Hbrush    :: Handle;
+Hgdiobj   :: Handle;
+Hmodule   :: Handle;
+Wparam    :: uint;
+Lparam    :: int;
+Lresult   :: int;
+Bool      :: i32;
+Wnd_Proc  :: #type proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
+
+
+INVALID_HANDLE :: cast(Handle)~cast(int)0;
+
+FALSE: Bool : 0;
+TRUE:  Bool : 1;
 
 CS_VREDRAW    :: 0x0001;
 CS_HREDRAW    :: 0x0002;
@@ -56,7 +56,7 @@ WM_CHAR              :: 0x0102;
 
 PM_REMOVE :: 1;
 
-COLOR_BACKGROUND :: cast(HBRUSH)(cast(int)1);
+COLOR_BACKGROUND :: cast(Hbrush)(cast(int)1);
 BLACK_BRUSH :: 4;
 
 SM_CXSCREEN :: 0;
@@ -65,53 +65,53 @@ SM_CYSCREEN :: 1;
 SW_SHOW :: 5;
 
 
-POINT :: struct #ordered {
+Point :: struct #ordered {
 	x, y: i32,
 }
 
-WNDCLASSEXA :: struct #ordered {
+WndClassExA :: struct #ordered {
 	size, style:           u32,
-	wnd_proc:              WNDPROC,
+	wnd_proc:              Wnd_Proc,
 	cls_extra, wnd_extra:  i32,
-	instance:              HINSTANCE,
-	icon:                  HICON,
-	cursor:                HCURSOR,
-	background:            HBRUSH,
+	instance:              Hinstance,
+	icon:                  Hicon,
+	cursor:                Hcursor,
+	background:            Hbrush,
 	menu_name, class_name: ^u8,
-	sm:                    HICON,
+	sm:                    Hicon,
 }
 
-MSG :: struct #ordered {
-	hwnd:    HWND,
+Msg :: struct #ordered {
+	hwnd:    Hwnd,
 	message: u32,
-	wparam:  WPARAM,
-	lparam:  LPARAM,
+	wparam:  Wparam,
+	lparam:  Lparam,
 	time:    u32,
-	pt:      POINT,
+	pt:      Point,
 }
 
-RECT :: struct #ordered {
+Rect :: struct #ordered {
 	left:   i32,
 	top:    i32,
 	right:  i32,
 	bottom: i32,
 }
 
-FILETIME :: struct #ordered {
+Filetime :: struct #ordered {
 	lo, hi: u32,
 }
 
-SYSTEMTIME :: struct #ordered {
+Systemtime :: struct #ordered {
 	year, month: u16,
 	day_of_week, day: u16,
 	hour, minute, second, millisecond: u16,
 }
 
-BY_HANDLE_FILE_INFORMATION :: struct #ordered {
+By_Handle_File_Information :: struct #ordered {
 	file_attributes:      u32,
 	creation_time,
 	last_access_time,
-	last_write_time:      FILETIME,
+	last_write_time:      Filetime,
 	volume_serial_number,
 	file_size_high,
 	file_size_low,
@@ -120,11 +120,11 @@ BY_HANDLE_FILE_INFORMATION :: struct #ordered {
 	file_index_low:       u32,
 }
 
-FILE_ATTRIBUTE_DATA :: struct #ordered {
+File_Attribute_Data :: struct #ordered {
 	file_attributes:  u32,
 	creation_time,
 	last_access_time,
-	last_write_time:  FILETIME,
+	last_write_time:  Filetime,
 	file_size_high,
 	file_size_low:    u32,
 }
@@ -136,13 +136,13 @@ GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
 
 GetLastError     :: proc() -> i32                            #foreign kernel32;
 ExitProcess      :: proc(exit_code: u32)                     #foreign kernel32;
-GetDesktopWindow :: proc() -> HWND                           #foreign user32;
-GetCursorPos     :: proc(p: ^POINT) -> i32                   #foreign user32;
-ScreenToClient   :: proc(h: HWND, p: ^POINT) -> i32          #foreign user32;
-GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE      #foreign kernel32;
-GetStockObject   :: proc(fn_object: i32) -> HGDIOBJ          #foreign gdi32;
+GetDesktopWindow :: proc() -> Hwnd                           #foreign user32;
+GetCursorPos     :: proc(p: ^Point) -> i32                   #foreign user32;
+ScreenToClient   :: proc(h: Hwnd, p: ^Point) -> i32          #foreign user32;
+GetModuleHandleA :: proc(module_name: ^u8) -> Hinstance      #foreign kernel32;
+GetStockObject   :: proc(fn_object: i32) -> Hgdiobj          #foreign gdi32;
 PostQuitMessage  :: proc(exit_code: i32)                     #foreign user32;
-SetWindowTextA   :: proc(hwnd: HWND, c_string: ^u8) -> BOOL  #foreign user32;
+SetWindowTextA   :: proc(hwnd: Hwnd, c_string: ^u8) -> Bool  #foreign user32;
 
 QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign kernel32;
 QueryPerformanceCounter   :: proc(result: ^i64) -> i32 #foreign kernel32;
@@ -152,28 +152,28 @@ Sleep :: proc(ms: i32) -> i32 #foreign kernel32;
 OutputDebugStringA :: proc(c_str: ^u8) #foreign kernel32;
 
 
-RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign user32;
+RegisterClassExA :: proc(wc: ^WndClassExA) -> i16 #foreign user32;
 CreateWindowExA  :: proc(ex_style: u32,
                          class_name, title: ^u8,
                          style: u32,
                          x, y, w, h: i32,
-                         parent: HWND, menu: HMENU, instance: HINSTANCE,
-                         param: rawptr) -> HWND #foreign user32;
+                         parent: Hwnd, menu: Hmenu, instance: Hinstance,
+                         param: rawptr) -> Hwnd #foreign user32;
 
-ShowWindow       :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign user32;
-TranslateMessage :: proc(msg: ^MSG) -> BOOL                 #foreign user32;
-DispatchMessageA :: proc(msg: ^MSG) -> LRESULT              #foreign user32;
-UpdateWindow     :: proc(hwnd: HWND) -> BOOL                #foreign user32;
-PeekMessageA     :: proc(msg: ^MSG, hwnd: HWND,
-                         msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign user32;
+ShowWindow       :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool #foreign user32;
+TranslateMessage :: proc(msg: ^Msg) -> Bool                 #foreign user32;
+DispatchMessageA :: proc(msg: ^Msg) -> Lresult              #foreign user32;
+UpdateWindow     :: proc(hwnd: Hwnd) -> Bool                #foreign user32;
+PeekMessageA     :: proc(msg: ^Msg, hwnd: Hwnd,
+                         msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool #foreign user32;
 
-DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign user32;
+DefWindowProcA :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult #foreign user32;
 
-AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign user32;
-GetActiveWindow  :: proc() -> HWND #foreign user32;
+AdjustWindowRect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool #foreign user32;
+GetActiveWindow  :: proc() -> Hwnd #foreign user32;
 
-DestroyWindow       :: proc(wnd: HWND) -> BOOL #foreign user32;
-DescribePixelFormat :: proc(dc: HDC, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32;
+DestroyWindow       :: proc(wnd: Hwnd) -> Bool #foreign user32;
+DescribePixelFormat :: proc(dc: Hdc, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32;
 
 
 GetQueryPerformanceFrequency :: proc() -> i64 {
@@ -182,34 +182,36 @@ GetQueryPerformanceFrequency :: proc() -> i64 {
 	return r;
 }
 
-GetCommandLineA    :: proc() -> ^u8 #foreign kernel32;
-GetSystemMetrics   :: proc(index: i32) -> i32 #foreign kernel32;
-GetCurrentThreadId :: proc() -> u32 #foreign kernel32;
+GetCommandLineA    :: proc() -> ^u8                                 #foreign kernel32;
+GetCommandLineW    :: proc() -> ^u16                                #foreign kernel32;
+GetSystemMetrics   :: proc(index: i32) -> i32                       #foreign kernel32;
+GetCurrentThreadId :: proc() -> u32                                 #foreign kernel32;
+CommandLineToArgvW :: proc(cmd_list: ^u16, num_args: ^i32) -> ^^u16 #foreign shell32;
 
-timeGetTime             :: proc() -> u32 #foreign winmm;
-GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^FILETIME) #foreign kernel32;
-FileTimeToLocalFileTime :: proc(file_time: ^FILETIME, local_file_time: ^FILETIME) -> BOOL #foreign kernel32;
-FileTimeToSystemTime    :: proc(file_time: ^FILETIME, system_time: ^SYSTEMTIME) -> BOOL #foreign kernel32;
-SystemTimeToFileTime    :: proc(system_time: ^SYSTEMTIME, file_time: ^FILETIME) -> BOOL #foreign kernel32;
+timeGetTime             :: proc() -> u32                                                  #foreign winmm;
+GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^Filetime)                      #foreign kernel32;
+FileTimeToLocalFileTime :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool #foreign kernel32;
+FileTimeToSystemTime    :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool   #foreign kernel32;
+SystemTimeToFileTime    :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool   #foreign kernel32;
 
 // File Stuff
 
-CloseHandle  :: proc(h: HANDLE) -> i32 #foreign kernel32;
-GetStdHandle :: proc(h: i32) -> HANDLE #foreign kernel32;
+CloseHandle  :: proc(h: Handle) -> i32 #foreign kernel32;
+GetStdHandle :: proc(h: i32) -> Handle #foreign kernel32;
 CreateFileA  :: proc(filename: ^u8, desired_access, share_mode: u32,
                      security: rawptr,
-                     creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign kernel32;
-ReadFile  :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32;
-WriteFile :: proc(h: HANDLE, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32;
+                     creation, flags_and_attribs: u32, template_file: Handle) -> Handle #foreign kernel32;
+ReadFile  :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool #foreign kernel32;
+WriteFile :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool #foreign kernel32;
 
-GetFileSizeEx              :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign kernel32;
-GetFileAttributesExA       :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> BOOL #foreign kernel32;
-GetFileInformationByHandle :: proc(file_handle: HANDLE, file_info: ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign kernel32;
+GetFileSizeEx              :: proc(file_handle: Handle, file_size: ^i64) -> Bool #foreign kernel32;
+GetFileAttributesExA       :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> Bool #foreign kernel32;
+GetFileInformationByHandle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool #foreign kernel32;
 
-GetFileType    :: proc(file_handle: HANDLE) -> u32 #foreign kernel32;
-SetFilePointer :: proc(file_handle: HANDLE, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32;
+GetFileType    :: proc(file_handle: Handle) -> u32 #foreign kernel32;
+SetFilePointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32;
 
-SetHandleInformation :: proc(obj: HANDLE, mask, flags: u32) -> BOOL #foreign kernel32;
+SetHandleInformation :: proc(obj: Handle, mask, flags: u32) -> Bool #foreign kernel32;
 
 HANDLE_FLAG_INHERIT :: 1;
 HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2;
@@ -242,13 +244,13 @@ TRUNCATE_EXISTING :: 5;
 FILE_ATTRIBUTE_READONLY             :: 0x00000001;
 FILE_ATTRIBUTE_HIDDEN               :: 0x00000002;
 FILE_ATTRIBUTE_SYSTEM               :: 0x00000004;
-FILE_ATTRIBUTE_DIRECTORY            :: 0x00000010;
+FILE_ATTRIBUTE_DIRectORY            :: 0x00000010;
 FILE_ATTRIBUTE_ARCHIVE              :: 0x00000020;
 FILE_ATTRIBUTE_DEVICE               :: 0x00000040;
 FILE_ATTRIBUTE_NORMAL               :: 0x00000080;
 FILE_ATTRIBUTE_TEMPORARY            :: 0x00000100;
 FILE_ATTRIBUTE_SPARSE_FILE          :: 0x00000200;
-FILE_ATTRIBUTE_REPARSE_POINT        :: 0x00000400;
+FILE_ATTRIBUTE_REPARSE_Point        :: 0x00000400;
 FILE_ATTRIBUTE_COMPRESSED           :: 0x00000800;
 FILE_ATTRIBUTE_OFFLINE              :: 0x00001000;
 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  :: 0x00002000;
@@ -263,27 +265,27 @@ INVALID_SET_FILE_POINTER :: ~cast(u32)0;
 
 
 
-HeapAlloc      :: proc (h: HANDLE, flags: u32, bytes: int) -> rawptr                 #foreign kernel32;
-HeapReAlloc    :: proc (h: HANDLE, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign kernel32;
-HeapFree       :: proc (h: HANDLE, flags: u32, memory: rawptr) -> BOOL               #foreign kernel32;
-GetProcessHeap :: proc () -> HANDLE #foreign kernel32;
+HeapAlloc      :: proc (h: Handle, flags: u32, bytes: int) -> rawptr                 #foreign kernel32;
+HeapReAlloc    :: proc (h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign kernel32;
+HeapFree       :: proc (h: Handle, flags: u32, memory: rawptr) -> Bool               #foreign kernel32;
+GetProcessHeap :: proc () -> Handle #foreign kernel32;
 
 
 HEAP_ZERO_MEMORY :: 0x00000008;
 
 // Synchronization
 
-SECURITY_ATTRIBUTES :: struct #ordered {
+Security_Attributes :: struct #ordered {
 	length:              u32,
 	security_descriptor: rawptr,
-	inherit_handle:      BOOL,
+	inherit_handle:      Bool,
 }
 
 INFINITE :: 0xffffffff;
 
-CreateSemaphoreA    :: proc(attributes: ^SECURITY_ATTRIBUTES, initial_count, maximum_count: i32, name: ^byte) -> HANDLE #foreign kernel32;
-ReleaseSemaphore    :: proc(semaphore: HANDLE, release_count: i32, previous_count: ^i32) -> BOOL #foreign kernel32;
-WaitForSingleObject :: proc(handle: HANDLE, milliseconds: u32) -> u32 #foreign kernel32;
+CreateSemaphoreA    :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: ^byte) -> Handle #foreign kernel32;
+ReleaseSemaphore    :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool #foreign kernel32;
+WaitForSingleObject :: proc(handle: Handle, milliseconds: u32) -> u32 #foreign kernel32;
 
 
 InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign kernel32;
@@ -307,11 +309,11 @@ ReadBarrier      :: proc() #foreign kernel32;
 
 
 
-HMONITOR :: HANDLE;
+Hmonitor :: Handle;
 
 GWL_STYLE     :: -16;
 
-HWND_TOP :: cast(HWND)cast(uint)0;
+Hwnd_TOP :: cast(Hwnd)cast(uint)0;
 
 MONITOR_DEFAULTTONULL    :: 0x00000000;
 MONITOR_DEFAULTTOPRIMARY :: 0x00000001;
@@ -324,39 +326,39 @@ SWP_NOSIZE        :: 0x0001;
 SWP_NOMOVE        :: 0x0002;
 
 
-MONITORINFO :: struct #ordered {
+Monitor_Info :: struct #ordered {
 	size:      u32,
-	monitor:   RECT,
-	work:      RECT,
+	monitor:   Rect,
+	work:      Rect,
 	flags:     u32,
 }
 
-WINDOWPLACEMENT :: struct #ordered {
+Window_Placement :: struct #ordered {
 	length:     u32,
 	flags:      u32,
 	show_cmd:   u32,
-	min_pos:    POINT,
-	max_pos:    POINT,
-	normal_pos: RECT,
+	min_pos:    Point,
+	max_pos:    Point,
+	normal_pos: Rect,
 }
 
-GetMonitorInfoA    :: proc(monitor: HMONITOR, mi: ^MONITORINFO) -> BOOL #foreign user32;
-MonitorFromWindow  :: proc(wnd: HWND, flags : u32) -> HMONITOR #foreign user32;
+GetMonitorInfoA    :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool #foreign user32;
+MonitorFromWindow  :: proc(wnd: Hwnd, flags : u32) -> Hmonitor #foreign user32;
 
-SetWindowPos       :: proc(wnd: HWND, wndInsertAfter: HWND, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos";
+SetWindowPos       :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos";
 
-GetWindowPlacement :: proc(wnd: HWND, wndpl: ^WINDOWPLACEMENT) -> BOOL #foreign user32;
-SetWindowPlacement :: proc(wnd: HWND, wndpl: ^WINDOWPLACEMENT) -> BOOL #foreign user32;
+GetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
+SetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
 
-GetWindowLongPtrA :: proc(wnd: HWND, index: i32) -> i64 #foreign user32;
-SetWindowLongPtrA :: proc(wnd: HWND, index: i32, new: i64) -> i64 #foreign user32;
+GetWindowLongPtrA :: proc(wnd: Hwnd, index: i32) -> i64 #foreign user32;
+SetWindowLongPtrA :: proc(wnd: Hwnd, index: i32, new: i64) -> i64 #foreign user32;
 
-GetWindowText :: proc(wnd: HWND, str: ^byte, maxCount: i32) -> i32 #foreign user32;
+GetWindowText :: proc(wnd: Hwnd, str: ^byte, maxCount: i32) -> i32 #foreign user32;
 
-HIWORD :: proc(wParam: WPARAM) -> u16 { return cast(u16)((cast(u32)wParam >> 16) & 0xffff); }
-HIWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)((cast(u32)lParam >> 16) & 0xffff); }
-LOWORD :: proc(wParam: WPARAM) -> u16 { return cast(u16)wParam; }
-LOWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)lParam; }
+HIWORD :: proc(wParam: Wparam) -> u16 { return cast(u16)((cast(u32)wParam >> 16) & 0xffff); }
+HIWORD :: proc(lParam: Lparam) -> u16 { return cast(u16)((cast(u32)lParam >> 16) & 0xffff); }
+LOWORD :: proc(wParam: Wparam) -> u16 { return cast(u16)wParam; }
+LOWORD :: proc(lParam: Lparam) -> u16 { return cast(u16)lParam; }
 
 
 
@@ -367,7 +369,7 @@ LOWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)lParam; }
 
 
 
-BITMAPINFOHEADER :: struct #ordered {
+Bitmap_Info_Header :: struct #ordered {
 	size:              u32,
 	width, height:     i32,
 	planes, bit_count: i16,
@@ -378,33 +380,33 @@ BITMAPINFOHEADER :: struct #ordered {
 	clr_used:          u32,
 	clr_important:     u32,
 }
-BITMAPINFO :: struct #ordered {
-	using header: BITMAPINFOHEADER,
-	colors:       [1]RGBQUAD,
+Bitmap_Info :: struct #ordered {
+	using header: Bitmap_Info_Header,
+	colors:       [1]Rgb_Quad,
 }
 
 
-RGBQUAD :: struct #ordered { blue, green, red, reserved: byte }
+Rgb_Quad :: struct #ordered { blue, green, red, reserved: byte }
 
 BI_RGB         :: 0;
 DIB_RGB_COLORS :: 0x00;
 SRCCOPY: u32    : 0x00cc0020;
 
 
-StretchDIBits :: proc (hdc: HDC,
+StretchDIBits :: proc (hdc: Hdc,
                        x_dst, y_dst, width_dst, height_dst: i32,
                        x_src, y_src, width_src, header_src: i32,
-                       bits: rawptr, bits_info: ^BITMAPINFO,
+                       bits: rawptr, bits_info: ^Bitmap_Info,
                        usage: u32,
                        rop: u32) -> i32 #foreign gdi32;
 
 
 
-LoadLibraryA   :: proc (c_str: ^u8) -> HMODULE #foreign kernel32;
-FreeLibrary    :: proc (h: HMODULE) #foreign kernel32;
-GetProcAddress :: proc (h: HMODULE, c_str: ^u8) -> PROC #foreign kernel32;
+LoadLibraryA   :: proc (c_str: ^u8) -> Hmodule #foreign kernel32;
+FreeLibrary    :: proc (h: Hmodule) #foreign kernel32;
+GetProcAddress :: proc (h: Hmodule, c_str: ^u8) -> Proc #foreign kernel32;
 
-GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign user32;
+GetClientRect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool #foreign user32;
 
 // Windows OpenGL
 PFD_TYPE_RGBA             :: 0;
@@ -461,14 +463,14 @@ PIXELFORMATDESCRIPTOR :: struct #ordered {
 	damage_mask: u32,
 }
 
-GetDC             :: proc(h: HWND) -> HDC #foreign user32;
-SetPixelFormat    :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> BOOL #foreign gdi32;
-ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32;
-SwapBuffers       :: proc(hdc: HDC) -> BOOL #foreign gdi32;
-ReleaseDC         :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign user32;
+GetDC             :: proc(h: Hwnd) -> Hdc #foreign user32;
+SetPixelFormat    :: proc(hdc: Hdc, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> Bool #foreign gdi32;
+ChoosePixelFormat :: proc(hdc: Hdc, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32;
+SwapBuffers       :: proc(hdc: Hdc) -> Bool #foreign gdi32;
+ReleaseDC         :: proc(wnd: Hwnd, hdc: Hdc) -> i32 #foreign user32;
 
 
-PROC  :: #type proc() #cc_c;
+Proc  :: #type proc() #cc_c;
 
 
 GetKeyState      :: proc(v_key: i32) -> i16 #foreign user32;
@@ -611,7 +613,7 @@ Key_Code :: enum i32 {
 	RCONTROL   = 0xA3,
 	LMENU      = 0xA4,
 	RMENU      = 0xA5,
-	PROCESSKEY = 0xE5,
+	ProcESSKEY = 0xE5,
 	ATTN       = 0xF6,
 	CRSEL      = 0xF7,
 	EXSEL      = 0xF8,

+ 0 - 0
compile_time_execution_problems.md → misc/compile_time_execution_problems.md


+ 11 - 0
misc/lib_maker_clang.bat

@@ -0,0 +1,11 @@
+@echo off
+setlocal EnableDelayedExpansion
+
+set file_input=%1
+set name=%1
+FOR %%f IN (name) do (
+   FOR %%g in (!%%f!) do set "%%f=%%~ng"
+)
+
+call clang -O2 -c %file_input% -o %name%.o ^
+	&& call ar %name%.o -rcs %name%.lib

+ 0 - 0
libs.txt → misc/libs.txt


+ 0 - 0
logo-slim.png → misc/logo-slim.png


+ 0 - 0
roadmap.md → misc/roadmap.md


+ 0 - 0
todo.md → misc/todo.md


+ 0 - 4
run.bat

@@ -1,4 +0,0 @@
-@echo off
-
-
-rem call clang -c -emit-llvm -DGB_IMPLEMENTATION -DGB_DEF=GB_DLL_EXPORT ..\src\gb\gb.h

+ 2 - 8
src/array.c

@@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) {
 		x->count = capacity;
 	}
 
-	{
-		// TODO(bill): Resize rather than copy and delete
-		void *new_data = gb_alloc(x->allocator, element_size*capacity);
-		gb_memmove(new_data, x->e, element_size*x->count);
-		gb_free(x->allocator, x->e);
-		x->capacity = capacity;
-		x->e = new_data;
-	}
+	x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
+	x->capacity = capacity;
 }
 
 

+ 1 - 1
src/build_settings.c

@@ -264,7 +264,7 @@ String get_fullpath_core(gbAllocator a, String path) {
 void init_build_context(void) {
 	BuildContext *bc = &build_context;
 	bc->ODIN_VENDOR  = str_lit("odin");
-	bc->ODIN_VERSION = str_lit("0.1.1");
+	bc->ODIN_VERSION = str_lit("0.1.3");
 	bc->ODIN_ROOT    = odin_root_dir();
 
 #if defined(GB_SYSTEM_WINDOWS)

+ 54 - 6
src/check_decl.c

@@ -12,6 +12,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 			gbString expr_str = expr_to_string(operand->expr);
 
 			// TODO(bill): is this a good enough error message?
+			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error_node(operand->expr,
 			      "Cannot assign builtin procedure `%s` in %.*s",
 			      expr_str,
@@ -276,12 +277,6 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
 			error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
 		}
 
-		// TODO(bill): Is this the best option? What about passing to external shit?!
-		// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
-		// 	error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
-		// 	proc_type->Proc.calling_convention = ProcCC_Odin;
-		// }
-
 		d->scope = c->context.scope;
 
 		GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
@@ -408,6 +403,56 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 }
 
 
+void check_alias_decl(Checker *c, Entity *e, AstNode *expr) {
+	GB_ASSERT(e->type == NULL);
+	GB_ASSERT(e->kind == Entity_Alias);
+
+	if (e->flags & EntityFlag_Visited) {
+		e->type = t_invalid;
+		return;
+	}
+	e->flags |= EntityFlag_Visited;
+	e->type = t_invalid;
+
+	expr = unparen_expr(expr);
+
+	if (expr->kind == AstNode_Alias) {
+		error_node(expr, "#alias of an #alias is not allowed");
+		return;
+	}
+
+	if (expr->kind == AstNode_Ident) {
+		Operand o = {0};
+		Entity *f = check_ident(c, &o, expr, NULL, NULL, true);
+		if (f != NULL) {
+			e->Alias.original = f;
+			e->type = f->type;
+		}
+		return;
+	} else if (expr->kind == AstNode_SelectorExpr) {
+		Operand o = {0};
+		Entity *f = check_selector(c, &o, expr, NULL);
+		if (f != NULL) {
+			e->Alias.original = f;
+			e->type = f->type;
+		}
+		return;
+	}
+
+	Operand o = {0};
+	check_expr_or_type(c, &o, expr);
+	if (o.mode == Addressing_Invalid) {
+		return;
+	}
+	switch (o.mode) {
+	case Addressing_Type:
+		e->type = o.type;
+		break;
+	default:
+		error_node(expr, "#alias declarations only allow types");
+	}
+}
+
 void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
 	if (e->type != NULL) {
 		return;
@@ -443,6 +488,9 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
 	case Entity_Procedure:
 		check_proc_lit(c, e, d);
 		break;
+	case Entity_Alias:
+		check_alias_decl(c, e, d->init_expr);
+		break;
 	}
 
 	c->context = prev;

+ 117 - 188
src/check_expr.c

@@ -272,6 +272,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 
 		if (operand->mode == Addressing_Builtin) {
 			// TODO(bill): is this a good enough error message?
+			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error_node(operand->expr,
 			           "Cannot assign builtin procedure `%s` in %.*s",
 			           expr_str,
@@ -379,7 +380,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 				Entity **found = map_entity_get(&entity_map, key);
 				if (found != NULL) {
 					Entity *e = *found;
-					// TODO(bill): Scope checking already checks the declaration
+					// NOTE(bill): Scope checking already checks the declaration but in many cases, this can happen so why not?
+					// This may be a little janky but it's not really that much of a problem
 					error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
 					error(e->token,   "\tpreviously declared");
 				} else {
@@ -604,16 +606,11 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
 		{
 			ast_node(fl, FieldList, f->list);
 
-			// TODO(bill): Just do a gb_memcopy here
 			// NOTE(bill): Copy the contents for the common fields for now
 			AstNodeArray list = {0};
 			array_init_count(&list, c->allocator, ut->fields.count+fl->list.count);
-			for (isize j = 0; j < ut->fields.count; j++) {
-				list.e[j] = ut->fields.e[j];
-			}
-			for (isize j = 0; j < fl->list.count; j++) {
-				list.e[j+ut->fields.count] = fl->list.e[j];
-			}
+			gb_memmove_array(list.e, ut->fields.e, ut->fields.count);
+			gb_memmove_array(list.e+ut->fields.count, fl->list.e, fl->list.count);
 
 			isize list_count = 0;
 			for_array(j, list) {
@@ -654,7 +651,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
 
 		HashKey key = hash_string(name_token.string);
 		if (map_entity_get(&entity_map, key) != NULL) {
-			// TODO(bill): Scope checking already checks the declaration
+			// NOTE(bill): Scope checking already checks the declaration
 			error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
 		} else {
 			map_entity_set(&entity_map, key, e);
@@ -746,9 +743,9 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 		constant_type = named_type;
 	}
 
-	ExactValue iota = make_exact_value_integer(-1);
-	ExactValue min_value = make_exact_value_integer(0);
-	ExactValue max_value = make_exact_value_integer(0);
+	ExactValue iota = exact_value_integer(-1);
+	ExactValue min_value = exact_value_integer(0);
+	ExactValue max_value = exact_value_integer(0);
 
 	for_array(i, et->fields) {
 		AstNode *field = et->fields.e[i];
@@ -783,10 +780,10 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 			if (o.mode != Addressing_Invalid) {
 				iota = o.value;
 			} else {
-				iota = exact_binary_operator_value(Token_Add, iota, make_exact_value_integer(1));
+				iota = exact_binary_operator_value(Token_Add, iota, exact_value_integer(1));
 			}
 		} else {
-			iota = exact_binary_operator_value(Token_Add, iota, make_exact_value_integer(1));
+			iota = exact_binary_operator_value(Token_Add, iota, exact_value_integer(1));
 		}
 
 
@@ -839,7 +836,7 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 	enum_type->Record.field_count = field_count;
 
 	enum_type->Record.enum_count = make_entity_constant(c->allocator, c->context.scope,
-		make_token_ident(str_lit("count")), t_int, make_exact_value_integer(field_count));
+		make_token_ident(str_lit("count")), t_int, exact_value_integer(field_count));
 	enum_type->Record.enum_min_value = make_entity_constant(c->allocator, c->context.scope,
 		make_token_ident(str_lit("min_value")), constant_type, min_value);
 	enum_type->Record.enum_max_value = make_entity_constant(c->allocator, c->context.scope,
@@ -1028,7 +1025,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 }
 
 
-void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint) {
+Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint, bool allow_import_name) {
 	GB_ASSERT(n->kind == AstNode_Ident);
 	o->mode = Addressing_Invalid;
 	o->expr = n;
@@ -1046,7 +1043,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ
 		if (named_type != NULL) {
 			set_base_type(named_type, t_invalid);
 		}
-		return;
+		return NULL;
 	}
 
 	bool is_overloaded = false;
@@ -1095,7 +1092,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ
 			o->type              = t_invalid;
 			o->overload_count    = overload_count;
 			o->overload_entities = procs;
-			return;
+			return NULL;
 		}
 		gb_free(heap_allocator(), procs);
 	}
@@ -1106,20 +1103,26 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ
 
 	if (e->type == NULL) {
 		compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(name));
-		return;
+		return NULL;
 	}
 
-	Type *type = e->type;
+	e->flags |= EntityFlag_Used;
 
+	Entity *original_e = e;
+	while (e != NULL && e->kind == Entity_Alias && e->Alias.original != NULL) {
+		e = e->Alias.original;
+	}
+
+	Type *type = e->type;
 	switch (e->kind) {
 	case Entity_Constant:
 		if (type == t_invalid) {
 			o->type = t_invalid;
-			return;
+			return e;
 		}
 		o->value = e->Constant.value;
 		if (o->value.kind == ExactValue_Invalid) {
-			return;
+			return e;
 		}
 		o->mode = Addressing_Constant;
 		break;
@@ -1128,7 +1131,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ
 		e->flags |= EntityFlag_Used;
 		if (type == t_invalid) {
 			o->type = t_invalid;
-			return;
+			return e;
 		}
 		o->mode = Addressing_Variable;
 		if (e->Variable.is_immutable) {
@@ -1136,10 +1139,10 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ
 		}
 		break;
 
-	case Entity_TypeName: {
+	case Entity_TypeName:
+		// NOTE(bill): Cyclical dependency checking is handled in the "type system" not here
 		o->mode = Addressing_Type;
-		// TODO(bill): Fix cyclical dependancy checker
-	} break;
+		break;
 
 	case Entity_Procedure:
 		o->mode = Addressing_Value;
@@ -1151,22 +1154,29 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ
 		break;
 
 	case Entity_ImportName:
-		error_node(n, "Use of import `%.*s` not in selector", LIT(e->ImportName.name));
-		return;
+		if (!allow_import_name) {
+			error_node(n, "Use of import `%.*s` not in selector", LIT(name));
+		}
+		return e;
 	case Entity_LibraryName:
-		error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(e->LibraryName.name));
-		return;
+		error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name));
+		return e;
+
+	case Entity_Label:
+		o->mode = Addressing_NoValue;
+		break;
 
 	case Entity_Nil:
 		o->mode = Addressing_Value;
 		break;
 
 	default:
-		compiler_error("Compiler error: Unknown EntityKind");
+		compiler_error("Unknown EntityKind");
 		break;
 	}
 
 	o->type = type;
+	return e;
 }
 
 i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) {
@@ -1342,7 +1352,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
 	switch (e->kind) {
 	case_ast_node(i, Ident, e);
 		Operand o = {0};
-		check_ident(c, &o, e, named_type, NULL);
+		check_ident(c, &o, e, named_type, NULL, false);
 
 		switch (o.mode) {
 		case Addressing_Invalid:
@@ -1663,7 +1673,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
 		return true;
 	}
 
-	type = base_type(base_enum_type(type));
+	type = core_type(type);
 
 	if (is_type_boolean(type)) {
 		return in_value.kind == ExactValue_Bool;
@@ -1682,7 +1692,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
 		if (s < 64) {
 			umax = (1ull << s) - 1ull;
 		} else {
-			// TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
+			// IMPORTANT TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
 			s = 64;
 		}
 		i64 imax = (1ll << (s-1ll));
@@ -1860,7 +1870,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) {
 		}
 		x->mode  = Addressing_Constant;
 		x->type  = t_untyped_bool;
-		x->value = make_exact_value_bool(comp);
+		x->value = exact_value_bool(comp);
 		return;
 	}
 
@@ -1913,10 +1923,11 @@ void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) {
 	} else {
 		if (x->mode == Addressing_Constant &&
 		    y->mode == Addressing_Constant) {
-			x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value));
+			x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
 		} else {
 			x->mode = Addressing_Value;
 
+
 			update_expr_type(c, x->expr, default_type(x->type), true);
 			update_expr_type(c, y->expr, default_type(y->type), true);
 		}
@@ -1995,7 +2006,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
 				x->type = t_untyped_integer;
 			}
 
-			x->value = exact_value_shift(be->op.kind, x_val, make_exact_value_integer(amount));
+			x->value = exact_value_shift(be->op.kind, x_val, exact_value_integer(amount));
 
 			if (is_type_typed(x->type)) {
 				check_is_expressible(c, x, base_type(x->type));
@@ -2094,7 +2105,7 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs
 			new_ptr_val -= elem_size*offset_val;
 		}
 		operand.mode = Addressing_Constant;
-		operand.value = make_exact_value_pointer(new_ptr_val);
+		operand.value = exact_value_pointer(new_ptr_val);
 	}
 
 	return operand;
@@ -2108,8 +2119,8 @@ bool check_is_castable_to(Checker *c, Operand *operand, Type *y) {
 	}
 
 	Type *x = operand->type;
-	Type *src = base_type(base_enum_type(x));
-	Type *dst = base_type(base_enum_type(y));
+	Type *src = core_type(x);
+	Type *dst = core_type(y);
 	if (are_types_identical(src, dst)) {
 		return true;
 	}
@@ -2372,7 +2383,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 			GB_ASSERT(op.kind == Token_Sub);
 			i64 bytes = a.value_pointer - b.value_pointer;
 			i64 diff = bytes/type_size_of(c->allocator, type);
-			x->value = make_exact_value_pointer(diff);
+			x->value = exact_value_pointer(diff);
 			return;
 		}
 
@@ -2517,7 +2528,7 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level
 		return;
 	}
 
-	Type *t = base_type(base_enum_type(target_type));
+	Type *t = core_type(target_type);
 	switch (t->kind) {
 	case Type_Basic:
 		if (operand->mode == Addressing_Constant) {
@@ -2678,6 +2689,11 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 		add_entity_use(c, op_expr, e);
 		expr_entity = e;
 
+		Entity *original_e = e;
+		while (e != NULL && e->kind == Entity_Alias && e->Alias.original != NULL) {
+			e = e->Alias.original;
+		}
+
 		if (e != NULL && e->kind == Entity_ImportName && selector->kind == AstNode_Ident) {
 			// IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
 			// It pretty much needs to be in this order and this way
@@ -2832,7 +2848,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 	    operand->type != NULL && is_type_untyped(operand->type) && is_type_string(operand->type)) {
 		String s = operand->value.value_string;
 		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(s.len);
+		operand->value = exact_value_integer(s.len);
 		operand->type = t_untyped_integer;
 		return NULL;
 	}
@@ -2869,7 +2885,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 		operand->value = entity->Constant.value;
 		break;
 	case Entity_Variable:
-		// TODO(bill): This is the rule I need?
+		// TODO(bill): Is this the rule I need?
 		if (operand->mode == Addressing_Immutable) {
 			// Okay
 		} else if (sel.indirect || operand->mode != Addressing_Value) {
@@ -3058,9 +3074,11 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 	case BuiltinProc_clear: {
 		Type *type = operand->type;
-		if (!is_type_dynamic_array(type) && !is_type_map(type)) {
+		bool is_pointer = is_type_pointer(type);
+		type = base_type(type_deref(type));
+		if (!is_type_dynamic_array(type) && !is_type_map(type) && !is_type_slice(type)) {
 			gbString str = type_to_string(type);
-			error_node(operand->expr, "Expected a map or dynamic array, got `%s`", str);
+			error_node(operand->expr, "Invalid type for `clear`, got `%s`", str);
 			gb_string_free(str);
 			return false;
 		}
@@ -3092,14 +3110,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 
 		Type *elem = NULL;
-		Type *slice_elem = NULL;
 		if (is_type_dynamic_array(type)) {
-			// TODO(bill): Semi-memory leaks
 			elem = type->DynamicArray.elem;
 		} else {
 			elem = type->Slice.elem;
 		}
-		slice_elem = make_type_slice(c->allocator, elem);
+		Type *slice_elem = make_type_slice(c->allocator, elem);
 
 		Type *proc_type_params = make_type_tuple(c->allocator);
 		proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2);
@@ -3158,7 +3174,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 
 		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_size_of(c->allocator, type));
+		operand->value = exact_value_integer(type_size_of(c->allocator, type));
 		operand->type = t_untyped_integer;
 
 	} break;
@@ -3171,7 +3187,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 
 		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_size_of(c->allocator, operand->type));
+		operand->value = exact_value_integer(type_size_of(c->allocator, operand->type));
 		operand->type = t_untyped_integer;
 		break;
 
@@ -3183,7 +3199,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			return false;
 		}
 		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_align_of(c->allocator, type));
+		operand->value = exact_value_integer(type_align_of(c->allocator, type));
 		operand->type = t_untyped_integer;
 	} break;
 
@@ -3195,7 +3211,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 
 		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_align_of(c->allocator, operand->type));
+		operand->value = exact_value_integer(type_align_of(c->allocator, operand->type));
 		operand->type = t_untyped_integer;
 		break;
 
@@ -3239,7 +3255,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 
 		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_offset_of_from_selection(c->allocator, type, sel));
+		operand->value = exact_value_integer(type_offset_of_from_selection(c->allocator, type, sel));
 		operand->type  = t_untyped_integer;
 	} break;
 
@@ -3288,7 +3304,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 		operand->mode = Addressing_Constant;
 		// IMPORTANT TODO(bill): Fix for anonymous fields
-		operand->value = make_exact_value_integer(type_offset_of_from_selection(c->allocator, type, sel));
+		operand->value = exact_value_integer(type_offset_of_from_selection(c->allocator, type, sel));
 		operand->type  = t_untyped_integer;
 	} break;
 
@@ -3486,113 +3502,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		operand->mode = Addressing_Value;
 	} break;
 
-#if 0
-	case BuiltinProc_ptr_offset: {
-		// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error_node(call,
-			      "Expected a pointer to `ptr_offset`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		AstNode *offset = ce->args.e[1];
-		Operand op = {0};
-		check_expr(c, &op, offset);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		Type *offset_type = base_type(op.type);
-		if (!is_type_integer(offset_type)) {
-			error_node(op.expr, "Pointer offsets for `ptr_offset` must be an integer");
-			return false;
-		}
-
-		if (operand->mode == Addressing_Constant &&
-		    op.mode == Addressing_Constant) {
-			i64 ptr = operand->value.value_pointer;
-			i64 elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
-			ptr += elem_size * op.value.value_integer;
-			operand->value.value_pointer = ptr;
-		} else {
-			operand->mode = Addressing_Value;
-		}
-
-	} break;
-
-	case BuiltinProc_ptr_sub: {
-		// ptr_sub :: proc(a, b: ^T) -> int
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error_node(call,
-			      "Expected a pointer to `ptr_add`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-		AstNode *offset = ce->args[1];
-		Operand op = {0};
-		check_expr(c, &op, offset);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		if (!is_type_pointer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error_node(call,
-			      "Expected a pointer to `ptr_add`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (base_type(op.type) == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		if (!are_types_identical(operand->type, op.type)) {
-			gbString a = type_to_string(operand->type);
-			gbString b = type_to_string(op.type);
-			defer (gb_string_free(a));
-			defer (gb_string_free(b));
-			error_node(op.expr,
-			      "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
-			return false;
-		}
-
-		operand->type = t_int;
-
-		if (operand->mode == Addressing_Constant &&
-		    op.mode == Addressing_Constant) {
-			u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
-			u8 *ptr_b = cast(u8 *)op.value.value_pointer;
-			isize elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
-			operand->value = make_exact_value_integer((ptr_a - ptr_b) / elem_size);
-		} else {
-			operand->mode = Addressing_Value;
-		}
-	} break;
-#endif
-
 	case BuiltinProc_slice_ptr: {
 		// slice_ptr :: proc(a: ^T, len: int) -> []T
+		// slice_ptr :: proc(a: ^T, len, cap: int) -> []T
 		// ^T cannot be rawptr
 		Type *ptr_type = base_type(operand->type);
 		if (!is_type_pointer(ptr_type)) {
@@ -3610,21 +3522,28 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			return false;
 		}
 
-		AstNode *len = ce->args.e[1];
+		isize arg_count = ce->args.count;
+		if (arg_count < 2 || 3 < arg_count) {
+			error_node(ce->args.e[0], "`slice_ptr` expects 2 or 3 arguments, found %td", arg_count);
+			// NOTE(bill): Return the correct type to reduce errors
+		} else {
+			// If any are constant
+			i64 sizes[2] = {0};
+			isize size_count = 0;
+			for (isize i = 1; i < arg_count; i++) {
+				i64 val = 0;
+				bool ok = check_index_value(c, ce->args.e[i], -1, &val);
+				if (ok && val >= 0) {
+					GB_ASSERT(size_count < gb_count_of(sizes));
+					sizes[size_count++] = val;
+				}
+			}
 
-		Operand op = {0};
-		check_expr(c, &op, len);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		if (!is_type_integer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			error_node(call,
-			      "Length for `slice_ptr` must be an integer, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
+			if (size_count == 2 && sizes[0] > sizes[1]) {
+				error_node(ce->args.e[1], "`slice_ptr` count and capacity are swapped");
+				// No need quit
+			}
 		}
-
 		operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
 		operand->mode = Addressing_Value;
 	} break;
@@ -4412,7 +4331,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case_end;
 
 	case_ast_node(i, Ident, node);
-		check_ident(c, o, node, NULL, type_hint);
+		check_ident(c, o, node, NULL, type_hint, false);
 	case_end;
 
 	case_ast_node(bl, BasicLit, node);
@@ -4426,24 +4345,24 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		}
 		o->mode  = Addressing_Constant;
 		o->type  = t;
-		o->value = make_exact_value_from_basic_literal(*bl);
+		o->value = exact_value_from_basic_literal(*bl);
 	case_end;
 
 	case_ast_node(bd, BasicDirective, node);
 		if (str_eq(bd->name, str_lit("file"))) {
 			o->type = t_untyped_string;
-			o->value = make_exact_value_string(bd->token.pos.file);
+			o->value = exact_value_string(bd->token.pos.file);
 		} else if (str_eq(bd->name, str_lit("line"))) {
 			o->type = t_untyped_integer;
-			o->value = make_exact_value_integer(bd->token.pos.line);
+			o->value = exact_value_integer(bd->token.pos.line);
 		} else if (str_eq(bd->name, str_lit("procedure"))) {
 			if (c->proc_stack.count == 0) {
 				error_node(node, "#procedure may only be used within procedures");
 				o->type = t_untyped_string;
-				o->value = make_exact_value_string(str_lit(""));
+				o->value = exact_value_string(str_lit(""));
 			} else {
 				o->type = t_untyped_string;
-				o->value = make_exact_value_string(c->context.proc_name);
+				o->value = exact_value_string(c->context.proc_name);
 			}
 
 		} else {
@@ -4476,13 +4395,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case_end;
 
 	case_ast_node(te, TernaryExpr, node);
-		if (c->proc_stack.count == 0) {
-			error_node(node, "A ternary expression is only allowed within a procedure");
-			goto error;
-		}
-		Operand operand = {Addressing_Invalid};
-		check_expr(c, &operand, te->cond);
-		if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
+		Operand cond = {Addressing_Invalid};
+		check_expr(c, &cond, te->cond);
+		if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
 			error_node(te->cond, "Non-boolean condition in if expression");
 		}
 
@@ -4524,6 +4439,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		o->type = x.type;
 		o->mode = Addressing_Value;
+
+		if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) &&
+		    x.mode == Addressing_Constant &&
+		    y.mode == Addressing_Constant) {
+
+			o->mode = Addressing_Constant;
+
+			if (cond.value.value_bool) {
+				o->value = x.value;
+			} else {
+				o->value = y.value;
+			}
+		}
+
 	case_end;
 
 	case_ast_node(cl, CompoundLit, node);
@@ -4850,7 +4779,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		if (is_constant) {
 			o->mode = Addressing_Constant;
-			o->value = make_exact_value_compound(node);
+			o->value = exact_value_compound(node);
 		} else {
 			o->mode = Addressing_Value;
 		}
@@ -5625,7 +5554,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 	case_end;
 
 	case_ast_node(ht, HelperType, node);
-		str = gb_string_appendc(str, "type ");
+		str = gb_string_appendc(str, "#type ");
 		str = write_expr_to_string(str, ht->type);
 	case_end;
 	}

+ 144 - 85
src/check_stmt.c

@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
 	return rhs->type;
 }
 
-bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
-	if (is_type_pointer(type)) {
-		*is_union_ptr = is_type_union(type_deref(type));
-		return *is_union_ptr;
+typedef enum MatchTypeKind {
+	MatchType_Invalid,
+	MatchType_Union,
+	MatchType_Any,
+} MatchTypeKind;
+
+MatchTypeKind check_valid_type_match_type(Type *type) {
+	type = type_deref(type);
+	if (is_type_union(type)) {
+		return MatchType_Union;
 	}
 	if (is_type_any(type)) {
-		*is_any = true;
-		return *is_any;
+		return MatchType_Any;
 	}
-	return false;
+	return MatchType_Invalid;
 }
 
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
@@ -385,6 +390,47 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
 	}
 }
 
+void check_label(Checker *c, AstNode *label) {
+	if (label == NULL) {
+		return;
+	}
+	ast_node(l, Label, label);
+	if (l->name->kind != AstNode_Ident) {
+		error_node(l->name, "A label's name must be an identifier");
+		return;
+	}
+	String name = l->name->Ident.string;
+	if (str_eq(name, str_lit("_"))) {
+		error_node(l->name, "A label's name cannot be a blank identifier");
+		return;
+	}
+
+
+	if (c->proc_stack.count == 0) {
+		error_node(l->name, "A label is only allowed within a procedure");
+		return;
+	}
+	GB_ASSERT(c->context.decl != NULL);
+
+	bool ok = true;
+	for_array(i, c->context.decl->labels) {
+		BlockLabel bl = c->context.decl->labels.e[i];
+		if (str_eq(bl.name, name)) {
+			error_node(label, "Duplicate label with the name `%.*s`", LIT(name));
+			ok = false;
+			break;
+		}
+	}
+
+	Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label);
+	add_entity(c, c->context.scope, l->name, e);
+
+	if (ok) {
+		BlockLabel bl = {name, label};
+		array_add(&c->context.decl->labels, bl);
+	}
+}
+
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
@@ -475,9 +521,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				return;
 			}
 
-			// TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just
-			// leave it?
-
 			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
 
 			// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
@@ -515,7 +558,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
 				return;
 			}
-			// TODO(bill): Check if valid assignment operator
 			Operand operand = {Addressing_Invalid};
 			AstNode binary_expr = {AstNode_BinaryExpr};
 			ast_node(be, BinaryExpr, &binary_expr);
@@ -580,7 +622,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 		if (c->context.in_defer) {
 			error(rs->token, "You cannot `return` within a defer statement");
-			// TODO(bill): Should I break here?
 			break;
 		}
 
@@ -619,7 +660,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 	case_ast_node(fs, ForStmt, node);
 		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
+
 		check_open_scope(c, node);
+		check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
 
 		if (fs->init != NULL) {
 			check_stmt(c, fs->init, 0);
@@ -648,6 +691,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
 		check_open_scope(c, node);
 
+		check_label(c, rs->label);
+
 		Type *val = NULL;
 		Type *idx = NULL;
 		Entity *entities[2] = {0};
@@ -718,7 +763,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				case Token_Ellipsis: op = Token_Lt; break;
 				default: error(ie->op, "Invalid range operator"); break;
 				}
-				bool ok = compare_exact_values(Token_Lt, a, b);
+				bool ok = compare_exact_values(op, a, b);
 				if (!ok) {
 					// TODO(bill): Better error message
 					error(ie->op, "Invalid interval range");
@@ -850,7 +895,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		} else {
 			x.mode  = Addressing_Constant;
 			x.type  = t_bool;
-			x.value = make_exact_value_bool(true);
+			x.value = exact_value_bool(true);
 
 			Token token = {0};
 			token.pos = ast_node_token(ms->body).pos;
@@ -897,7 +942,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			}
 			ast_node(cc, CaseClause, stmt);
 
-
 			for_array(j, cc->list) {
 				AstNode *expr = cc->list.e[j];
 				Operand y = {0};
@@ -982,8 +1026,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		mod_flags |= Stmt_BreakAllowed;
 		check_open_scope(c, node);
 
-		bool is_union_ptr = false;
-		bool is_any = false;
+		MatchTypeKind match_type_kind = MatchType_Invalid;
 
 		if (ms->tag->kind != AstNode_AssignStmt) {
 			error_node(ms->tag, "Expected an `in` assignment for this type match statement");
@@ -1005,7 +1048,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 		check_expr(c, &x, rhs);
 		check_assignment(c, &x, NULL, str_lit("type match expression"));
-		if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
+		match_type_kind = check_valid_type_match_type(x.type);
+		if (check_valid_type_match_type(x.type) == MatchType_Invalid) {
 			gbString str = type_to_string(x.type);
 			error_node(x.expr,
 			           "Invalid type for this type match expression, got `%s`", str);
@@ -1013,7 +1057,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			break;
 		}
 
-
 		// NOTE(bill): Check for multiple defaults
 		AstNode *first_default = NULL;
 		ast_node(bs, BlockStmt, ms->body);
@@ -1048,7 +1091,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		}
 
 
-		MapBool seen = {0};
+		MapBool seen = {0}; // Multimap
 		map_bool_init(&seen, heap_allocator());
 
 		for_array(i, bs->stmts) {
@@ -1062,74 +1105,68 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			// TODO(bill): Make robust
 			Type *bt = base_type(type_deref(x.type));
 
-
-			AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
 			Type *case_type = NULL;
-			if (type_expr != NULL) { // Otherwise it's a default expression
-				Operand y = {0};
-				check_expr_or_type(c, &y, type_expr);
-
-				if (is_union_ptr) {
-					GB_ASSERT(is_type_union(bt));
-					bool tag_type_found = false;
-					for (isize i = 0; i < bt->Record.variant_count; i++) {
-						Entity *f = bt->Record.variants[i];
-						if (are_types_identical(f->type, y.type)) {
-							tag_type_found = true;
-							break;
+			for_array(type_index, cc->list) {
+				AstNode *type_expr = cc->list.e[type_index];
+				if (type_expr != NULL) { // Otherwise it's a default expression
+					Operand y = {0};
+					check_expr_or_type(c, &y, type_expr);
+
+					if (match_type_kind == MatchType_Union) {
+						GB_ASSERT(is_type_union(bt));
+						bool tag_type_found = false;
+						for (isize i = 0; i < bt->Record.variant_count; i++) {
+							Entity *f = bt->Record.variants[i];
+							if (are_types_identical(f->type, y.type)) {
+								tag_type_found = true;
+								break;
+							}
 						}
+						if (!tag_type_found) {
+							gbString type_str = type_to_string(y.type);
+							error_node(y.expr, "Unknown tag type, got `%s`", type_str);
+							gb_string_free(type_str);
+							continue;
+						}
+						case_type = y.type;
+					} else if (match_type_kind == MatchType_Any) {
+						case_type = y.type;
+					} else {
+						GB_PANIC("Unknown type to type match statement");
 					}
-					if (!tag_type_found) {
-						gbString type_str = type_to_string(y.type);
-						error_node(y.expr, "Unknown tag type, got `%s`", type_str);
-						gb_string_free(type_str);
-						continue;
-					}
-					case_type = y.type;
-				} else if (is_any) {
-					case_type = y.type;
-				} else {
-					GB_PANIC("Unknown type to type match statement");
-				}
 
-				HashKey key = hash_pointer(y.type);
-				bool *found = map_bool_get(&seen, key);
-				if (found) {
-					TokenPos pos = cc->token.pos;
-					gbString expr_str = expr_to_string(y.expr);
-					error_node(y.expr,
-					           "Duplicate type case `%s`\n"
-					           "\tprevious type case at %.*s(%td:%td)",
-					           expr_str,
-					           LIT(pos.file), pos.line, pos.column);
-					gb_string_free(expr_str);
-					break;
+					HashKey key = hash_pointer(y.type);
+					bool *found = map_bool_get(&seen, key);
+					if (found) {
+						TokenPos pos = cc->token.pos;
+						gbString expr_str = expr_to_string(y.expr);
+						error_node(y.expr,
+						           "Duplicate type case `%s`\n"
+						           "\tprevious type case at %.*s(%td:%td)",
+						           expr_str,
+						           LIT(pos.file), pos.line, pos.column);
+						gb_string_free(expr_str);
+						break;
+					}
+					map_bool_set(&seen, key, cast(bool)true);
 				}
-				map_bool_set(&seen, key, cast(bool)true);
 			}
 
-			check_open_scope(c, stmt);
+			if (cc->list.count > 1) {
+				case_type = NULL;
+			}
 			if (case_type == NULL) {
-				if (is_union_ptr) {
-					case_type = type_deref(x.type);
-				} else {
-					case_type = x.type;
-				}
+				case_type = x.type;
 			}
-
 			add_type_info_type(c, case_type);
 
+			check_open_scope(c, stmt);
 			{
-				// NOTE(bill): Dummy type
-				Type *tt = case_type;
-				if (is_union_ptr) {
-					tt = make_type_pointer(c->allocator, case_type);
-					add_type_info_type(c, tt);
-				}
-				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, tt, true);
+				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, true);
 				tag_var->flags |= EntityFlag_Used;
 				add_entity(c, c->context.scope, lhs, tag_var);
 				add_entity_use(c, lhs, tag_var);
+				add_implicit_entity(c, stmt, tag_var);
 			}
 			check_stmt_list(c, cc->stmts, mod_flags);
 			check_close_scope(c);
@@ -1173,20 +1210,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
 			break;
 		}
+
+		if (bs->label != NULL) {
+			if (bs->label->kind != AstNode_Ident) {
+				error_node(bs->label, "A branch statement's label name must be an identifier");
+				return;
+			}
+			AstNode *ident = bs->label;
+			String name = ident->Ident.string;
+			Entity *e = scope_lookup_entity(c->context.scope, name);
+			if (e == NULL) {
+				error_node(ident, "Undeclared label name: %.*s", LIT(name));
+				return;
+			}
+			add_entity_use(c, ident, e);
+			if (e->kind != Entity_Label) {
+				error_node(ident, "`%.*s` is not a label", LIT(name));
+				return;
+			}
+		}
+
 	case_end;
 
 	case_ast_node(us, UsingStmt, node);
-		switch (us->node->kind) {
-		default:
-			// TODO(bill): Better error message for invalid using statement
-			error(us->token, "Invalid `using` statement");
-			break;
-		case_ast_node(es, ExprStmt, us->node);
-			// TODO(bill): Allow for just a LHS expression list rather than this silly code
+		if (us->list.count == 0) {
+			error(us->token, "Empty `using` list");
+			return;
+		}
+		for_array(i, us->list) {
+			AstNode *expr = unparen_expr(us->list.e[0]);
 			Entity *e = NULL;
 
 			bool is_selector = false;
-			AstNode *expr = unparen_expr(es->expr);
 			if (expr->kind == AstNode_Ident) {
 				String name = expr->Ident.string;
 				e = scope_lookup_entity(c->context.scope, name);
@@ -1194,11 +1249,14 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				Operand o = {0};
 				e = check_selector(c, &o, expr, NULL);
 				is_selector = true;
+			} else if (expr->kind == AstNode_Implicit) {
+				error(us->token, "`using` applied to an implicit value");
+				continue;
 			}
 
 			if (e == NULL) {
 				error(us->token, "`using` applied to an unknown entity");
-				return;
+				continue;
 			}
 
 			switch (e->kind) {
@@ -1296,6 +1354,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				error(us->token, "`using` cannot be applied to `nil`");
 				break;
 
+			case Entity_Label:
+				error(us->token, "`using` cannot be applied to a label");
+				break;
+
 			case Entity_Invalid:
 				error(us->token, "`using` cannot be applied to an invalid entity");
 				break;
@@ -1303,13 +1365,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			default:
 				GB_PANIC("TODO(bill): `using` other expressions?");
 			}
-		case_end;
-
 		}
 	case_end;
 
 
-
 	case_ast_node(pa, PushAllocator, node);
 		Operand op = {0};
 		check_expr(c, &op, pa->expr);

+ 57 - 31
src/checker.c

@@ -98,7 +98,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 
 	// {STR_LIT("ptr_offset"),       2, false, Expr_Expr},
 	// {STR_LIT("ptr_sub"),          2, false, Expr_Expr},
-	{STR_LIT("slice_ptr"),        2, false,  Expr_Expr},
+	{STR_LIT("slice_ptr"),        2, true,   Expr_Expr},
 	{STR_LIT("slice_to_bytes"),   1, false,  Expr_Stmt},
 
 	{STR_LIT("min"),              2, false, Expr_Expr},
@@ -163,6 +163,11 @@ bool is_operand_nil(Operand o) {
 }
 
 
+typedef struct BlockLabel {
+	String   name;
+	AstNode *label; //  AstNode_Label;
+} BlockLabel;
+
 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
 typedef struct DeclInfo {
 	Scope *scope;
@@ -175,16 +180,19 @@ typedef struct DeclInfo {
 	AstNode *proc_lit; // AstNode_ProcLit
 
 	MapBool deps; // Key: Entity *
+	Array(BlockLabel) labels;
 } DeclInfo;
 
 // ProcedureInfo stores the information needed for checking a procedure
+
+
 typedef struct ProcedureInfo {
-	AstFile * file;
-	Token     token;
-	DeclInfo *decl;
-	Type *    type; // Type_Procedure
-	AstNode * body; // AstNode_BlockStmt
-	u32       tags;
+	AstFile *             file;
+	Token                 token;
+	DeclInfo *            decl;
+	Type *                type; // Type_Procedure
+	AstNode *             body; // AstNode_BlockStmt
+	u32                   tags;
 } ProcedureInfo;
 
 // ExprInfo stores information used for "untyped" expressions
@@ -208,21 +216,21 @@ ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue
 #include "map.c"
 
 typedef struct Scope {
-	Scope *        parent;
-	Scope *        prev, *next;
-	Scope *        first_child;
-	Scope *        last_child;
-	MapEntity      elements; // Key: String
-	MapBool        implicit; // Key: Entity *
-
-	Array(Scope *) shared;
-	Array(Scope *) imported;
-	bool           is_proc;
-	bool           is_global;
-	bool           is_file;
-	bool           is_init;
-	bool           has_been_imported; // This is only applicable to file scopes
-	AstFile *      file;
+	Scope *          parent;
+	Scope *          prev, *next;
+	Scope *          first_child;
+	Scope *          last_child;
+	MapEntity        elements; // Key: String
+	MapBool          implicit; // Key: Entity *
+
+	Array(Scope *)   shared;
+	Array(Scope *)   imported;
+	bool             is_proc;
+	bool             is_global;
+	bool             is_file;
+	bool             is_init;
+	bool             has_been_imported; // This is only applicable to file scopes
+	AstFile *        file;
 } Scope;
 gb_global Scope *universal_scope = NULL;
 
@@ -278,6 +286,7 @@ typedef struct CheckerInfo {
 	MapScope             scopes;          // Key: AstNode * | Node       -> Scope
 	MapExprInfo          untyped;         // Key: AstNode * | Expression -> ExprInfo
 	MapDeclInfo          entities;        // Key: Entity *
+	MapEntity            implicits;        // Key: AstNode *
 	MapEntity            foreigns;        // Key: String
 	MapAstFile           files;           // Key: String (full path)
 	MapIsize             type_info_map;   // Key: Type *
@@ -320,6 +329,7 @@ typedef Array(DelayedEntity) DelayedEntities;
 void init_declaration_info(DeclInfo *d, Scope *scope) {
 	d->scope = scope;
 	map_bool_init(&d->deps, heap_allocator());
+	array_init(&d->labels,  heap_allocator());
 }
 
 DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
@@ -357,9 +367,9 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) {
 	Scope *s = gb_alloc_item(allocator, Scope);
 	s->parent = parent;
 	map_entity_init(&s->elements,   heap_allocator());
-	map_bool_init(&s->implicit,   heap_allocator());
-	array_init(&s->shared,   heap_allocator());
-	array_init(&s->imported, heap_allocator());
+	map_bool_init(&s->implicit,     heap_allocator());
+	array_init(&s->shared,          heap_allocator());
+	array_init(&s->imported,        heap_allocator());
 
 	if (parent != NULL && parent != universal_scope) {
 		DLIST_APPEND(parent->first_child, parent->last_child, s);
@@ -455,6 +465,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
 		if (found) {
 			Entity *e = *found;
 			if (gone_thru_proc) {
+				if (e->kind == Entity_Label) {
+					continue;
+				}
 				if (e->kind == Entity_Variable &&
 				    !e->scope->is_file &&
 				    !e->scope->is_global) {
@@ -596,7 +609,7 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
 
 
 void add_global_string_constant(gbAllocator a, String name, String value) {
-	add_global_constant(a, name, t_untyped_string, make_exact_value_string(value));
+	add_global_constant(a, name, t_untyped_string, exact_value_string(value));
 
 }
 
@@ -616,8 +629,8 @@ void init_universal_scope(void) {
 	}
 
 // Constants
-	add_global_constant(a, str_lit("true"),  t_untyped_bool,    make_exact_value_bool(true));
-	add_global_constant(a, str_lit("false"), t_untyped_bool,    make_exact_value_bool(false));
+	add_global_constant(a, str_lit("true"),  t_untyped_bool, exact_value_bool(true));
+	add_global_constant(a, str_lit("false"), t_untyped_bool, exact_value_bool(false));
 
 	add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil));
 	add_global_entity(make_entity_library_name(a,  universal_scope,
@@ -662,6 +675,7 @@ void init_checker_info(CheckerInfo *i) {
 	map_decl_info_init(&i->entities,   a);
 	map_expr_info_init(&i->untyped,    a);
 	map_entity_init(&i->foreigns,      a);
+	map_entity_init(&i->implicits,     a);
 	map_isize_init(&i->type_info_map,  a);
 	map_ast_file_init(&i->files,       a);
 	i->type_info_count = 0;
@@ -676,6 +690,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	map_decl_info_destroy(&i->entities);
 	map_expr_info_destroy(&i->untyped);
 	map_entity_destroy(&i->foreigns);
+	map_entity_destroy(&i->implicits);
 	map_isize_destroy(&i->type_info_map);
 	map_ast_file_destroy(&i->files);
 }
@@ -816,7 +831,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
 					return false;
 				}
 				error(entity->token,
-				      "Redeclararation of `%.*s` in this scope through `using`\n"
+				      "Redeclaration of `%.*s` in this scope through `using`\n"
 				      "\tat %.*s(%td:%td)",
 				      LIT(name),
 				      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
@@ -827,7 +842,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
 					return false;
 				}
 				error(entity->token,
-				      "Redeclararation of `%.*s` in this scope\n"
+				      "Redeclaration of `%.*s` in this scope\n"
 				      "\tat %.*s(%td:%td)",
 				      LIT(name),
 				      LIT(pos.file), pos.line, pos.column);
@@ -861,6 +876,12 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn
 }
 
 
+void add_implicit_entity(Checker *c, AstNode *node, Entity *e) {
+	GB_ASSERT(node != NULL);
+	GB_ASSERT(e != NULL);
+	map_entity_set(&c->info.implicits, hash_pointer(node), e);
+}
+
 
 void add_type_info_type(Checker *c, Type *t) {
 	if (t == NULL) {
@@ -1467,7 +1488,12 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
 						// TODO(bill): What if vd->type != NULL??? How to handle this case?
 						d->type_expr = init;
 						d->init_expr = init;
-					} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
+					} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
+						error_node(up_init, "#alias declarations are not yet supported");
+						continue;
+						// e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, NULL);
+						// d->init_expr = init->Alias.expr;
+					}else if (init != NULL && up_init->kind == AstNode_ProcLit) {
 						e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
 						d->proc_lit = up_init;
 						d->type_expr = vd->type;

+ 26 - 1
src/entity.c

@@ -13,13 +13,15 @@ typedef struct Type Type;
 	ENTITY_KIND(Builtin) \
 	ENTITY_KIND(ImportName) \
 	ENTITY_KIND(LibraryName) \
+	ENTITY_KIND(Alias) \
 	ENTITY_KIND(Nil) \
-	ENTITY_KIND(Count)
+	ENTITY_KIND(Label)
 
 typedef enum EntityKind {
 #define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
 	ENTITY_KINDS
 #undef ENTITY_KIND
+	Entity_Count,
 } EntityKind;
 
 String const entity_strings[] = {
@@ -95,7 +97,14 @@ struct Entity {
 			String name;
 			bool   used;
 		} LibraryName;
+		struct {
+			Entity *original;
+		} Alias;
 		i32 Nil;
+		struct {
+			String name;
+			AstNode *node;
+		} Label;
 	};
 };
 
@@ -218,12 +227,28 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
 	return entity;
 }
 
+Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type,
+                          Entity *original) {
+	Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
+	entity->Alias.original = original;
+	return entity;
+}
+
 Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
 	Token token = make_token_ident(name);
 	Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
 	return entity;
 }
 
+Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
+                          AstNode *node) {
+	Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
+	entity->Label.node = node;
+	return entity;
+}
+
+
+
 Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
 	token.string = str_lit("_");
 	return make_entity_variable(a, scope, token, NULL, false);

+ 52 - 46
src/exact_value.c

@@ -35,45 +35,45 @@ HashKey hash_exact_value(ExactValue v) {
 }
 
 
-ExactValue make_exact_value_compound(AstNode *node) {
+ExactValue exact_value_compound(AstNode *node) {
 	ExactValue result = {ExactValue_Compound};
 	result.value_compound = node;
 	return result;
 }
 
-ExactValue make_exact_value_bool(bool b) {
+ExactValue exact_value_bool(bool b) {
 	ExactValue result = {ExactValue_Bool};
 	result.value_bool = (b != 0);
 	return result;
 }
 
-ExactValue make_exact_value_string(String string) {
+ExactValue exact_value_string(String string) {
 	// TODO(bill): Allow for numbers with underscores in them
 	ExactValue result = {ExactValue_String};
 	result.value_string = string;
 	return result;
 }
 
-ExactValue make_exact_value_integer(i64 i) {
+ExactValue exact_value_integer(i64 i) {
 	ExactValue result = {ExactValue_Integer};
 	result.value_integer = i;
 	return result;
 }
 
-ExactValue make_exact_value_float(f64 f) {
+ExactValue exact_value_float(f64 f) {
 	ExactValue result = {ExactValue_Float};
 	result.value_float = f;
 	return result;
 }
 
-ExactValue make_exact_value_pointer(i64 ptr) {
+ExactValue exact_value_pointer(i64 ptr) {
 	ExactValue result = {ExactValue_Pointer};
 	result.value_pointer = ptr;
 	return result;
 }
 
 
-ExactValue make_exact_value_integer_from_string(String string) {
+ExactValue exact_value_integer_from_string(String string) {
 	// TODO(bill): Allow for numbers with underscores in them
 	i32 base = 10;
 	bool has_prefix = false;
@@ -82,6 +82,7 @@ ExactValue make_exact_value_integer_from_string(String string) {
 		case 'b': base = 2;  has_prefix = true; break;
 		case 'o': base = 8;  has_prefix = true; break;
 		case 'd': base = 10; has_prefix = true; break;
+		case 'z': base = 12; has_prefix = true; break;
 		case 'x': base = 16; has_prefix = true; break;
 		}
 	}
@@ -100,25 +101,21 @@ ExactValue make_exact_value_integer_from_string(String string) {
 			continue;
 		}
 		i64 v = 0;
-		if (gb_char_is_digit(r)) {
-			v = r - '0';
-		} else if (gb_char_is_hex_digit(r)) {
-			v = gb_hex_digit_to_int(r);
-		} else {
+		v = digit_value(r);
+		if (v >= base) {
 			break;
 		}
-
 		result *= base;
 		result += v;
 	}
 
 
-	return make_exact_value_integer(result);
+	return exact_value_integer(result);
 }
 
 
 
-ExactValue make_exact_value_float_from_string(String string) {
+ExactValue exact_value_float_from_string(String string) {
 	isize i = 0;
 	u8 *str = string.text;
 	isize len = string.len;
@@ -137,10 +134,10 @@ ExactValue make_exact_value_float_from_string(String string) {
 		if (r == '_') {
 			continue;
 		}
-		if (!gb_char_is_digit(r)) {
+		i64 v = digit_value(r);
+		if (v >= 10) {
 			break;
 		}
-		i64 v = r - '0';
 		value *= 10.0;
 		value += v;
 	}
@@ -153,29 +150,38 @@ ExactValue make_exact_value_float_from_string(String string) {
 			if (r == '_') {
 				continue;
 			}
-			if (!gb_char_is_digit(r)) {
+			i64 v = digit_value(r);
+			if (v >= 10) {
 				break;
 			}
-			value += (r-'0')/pow10;
+			value += v/pow10;
 			pow10 *= 10.0;
 		}
 	}
 
-	f64 frac = 0;
+	bool frac = false;
 	f64 scale = 1.0;
 	if ((str[i] == 'e') || (str[i] == 'E')) {
 		i++;
 
 		if (str[i] == '-') {
-			frac = 1;
+			frac = true;
 			i++;
 		} else if (str[i] == '+') {
 			i++;
 		}
 
-		u32 exp;
-		for (exp = 0; gb_char_is_digit(str[i]); i++) {
-			exp = exp * 10 + (str[i]-'0');
+		u32 exp = 0;
+		for (; i < len; i++) {
+			Rune r = cast(Rune)str[i];
+			if (r == '_') {
+				continue;
+			}
+			u32 d = cast(u32)digit_value(r);
+			if (d >= 10) {
+				break;
+			}
+			exp = exp * 10 + d;
 		}
 		if (exp > 308) exp = 308;
 
@@ -185,20 +191,20 @@ ExactValue make_exact_value_float_from_string(String string) {
 	}
 
 	f64 result = sign * (frac ? (value / scale) : (value * scale));
-	return make_exact_value_float(result);
+	return exact_value_float(result);
 }
 
 
-ExactValue make_exact_value_from_basic_literal(Token token) {
+ExactValue exact_value_from_basic_literal(Token token) {
 	switch (token.kind) {
-	case Token_String:  return make_exact_value_string(token.string);
-	case Token_Integer: return make_exact_value_integer_from_string(token.string);
-	case Token_Float:   return make_exact_value_float_from_string(token.string);
+	case Token_String:  return exact_value_string(token.string);
+	case Token_Integer: return exact_value_integer_from_string(token.string);
+	case Token_Float:   return exact_value_float_from_string(token.string);
 	case Token_Rune: {
 		Rune r = GB_RUNE_INVALID;
 		gb_utf8_decode(token.string.text, token.string.len, &r);
 		// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
-		return make_exact_value_integer(r);
+		return exact_value_integer(r);
 	}
 	default:
 		GB_PANIC("Invalid token for basic literal");
@@ -217,12 +223,12 @@ ExactValue exact_value_to_integer(ExactValue v) {
 		i64 i = cast(i64)v.value_float;
 		f64 f = cast(f64)i;
 		if (f == v.value_float) {
-			return make_exact_value_integer(i);
+			return exact_value_integer(i);
 		}
 	} break;
 
 	case ExactValue_Pointer:
-		return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
+		return exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
 	}
 	ExactValue r = {ExactValue_Invalid};
 	return r;
@@ -231,7 +237,7 @@ ExactValue exact_value_to_integer(ExactValue v) {
 ExactValue exact_value_to_float(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
-		return make_exact_value_float(cast(i64)v.value_integer);
+		return exact_value_float(cast(i64)v.value_integer);
 	case ExactValue_Float:
 		return v;
 	}
@@ -287,14 +293,14 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
 			i &= ~((~0ll)<<precision);
 		}
 
-		return make_exact_value_integer(i);
+		return exact_value_integer(i);
 	} break;
 
 	case Token_Not: {
 		switch (v.kind) {
 		case ExactValue_Invalid: return v;
 		case ExactValue_Bool:
-			return make_exact_value_bool(!v.value_bool);
+			return exact_value_bool(!v.value_bool);
 		}
 	} break;
 	}
@@ -348,7 +354,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
 			return;
 		case ExactValue_Float:
 			// TODO(bill): Is this good enough?
-			*x = make_exact_value_float(cast(f64)x->value_integer);
+			*x = exact_value_float(cast(f64)x->value_integer);
 			return;
 		}
 		break;
@@ -372,10 +378,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 
 	case ExactValue_Bool:
 		switch (op) {
-		case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
-		case Token_CmpOr:  return make_exact_value_bool(x.value_bool || y.value_bool);
-		case Token_And:    return make_exact_value_bool(x.value_bool & y.value_bool);
-		case Token_Or:     return make_exact_value_bool(x.value_bool | y.value_bool);
+		case Token_CmpAnd: return exact_value_bool(x.value_bool && y.value_bool);
+		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);
 		default: goto error;
 		}
 		break;
@@ -388,7 +394,7 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 		case Token_Add:    c = a + b;  break;
 		case Token_Sub:    c = a - b;  break;
 		case Token_Mul:    c = a * b;  break;
-		case Token_Quo:    return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
+		case Token_Quo:    return exact_value_float(fmod(cast(f64)a, cast(f64)b));
 		case Token_QuoEq:  c = a / b;  break; // NOTE(bill): Integer division
 		case Token_Mod:    c = a % b;  break;
 		case Token_And:    c = a & b;  break;
@@ -400,17 +406,17 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 		default: goto error;
 		}
 
-		return make_exact_value_integer(c);
+		return exact_value_integer(c);
 	} break;
 
 	case ExactValue_Float: {
 		f64 a = x.value_float;
 		f64 b = y.value_float;
 		switch (op) {
-		case Token_Add: return make_exact_value_float(a + b);
-		case Token_Sub: return make_exact_value_float(a - b);
-		case Token_Mul: return make_exact_value_float(a * b);
-		case Token_Quo: return make_exact_value_float(a / b);
+		case Token_Add: return exact_value_float(a + b);
+		case Token_Sub: return exact_value_float(a - b);
+		case Token_Mul: return exact_value_float(a * b);
+		case Token_Quo: return exact_value_float(a / b);
 		default: goto error;
 		}
 	} break;

File diff suppressed because it is too large
+ 292 - 112
src/gb/gb.h


File diff suppressed because it is too large
+ 273 - 184
src/ir.c


+ 8 - 12
src/ir_print.c

@@ -306,7 +306,7 @@ void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type
 }
 
 void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) {
-	type = base_type(base_enum_type(type));
+	type = core_type(type);
 	if (is_type_float(type)) {
 		value = exact_value_to_float(value);
 	} else if (is_type_integer(type)) {
@@ -942,7 +942,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		switch (uo->op) {
 		case Token_Sub:
 			if (is_type_float(elem_type)) {
-				ir_print_exact_value(f, m, make_exact_value_float(0), type);
+				ir_print_exact_value(f, m, exact_value_float(0), type);
 			} else {
 				ir_fprintf(f, "0");
 			}
@@ -1197,17 +1197,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		ir_fprintf(f, "call void ");
 		ir_print_encoded_global(f, str_lit("__bounds_check_error"), false);
 		ir_fprintf(f, "(");
-		ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string);
+		ir_print_compound_element(f, m, exact_value_string(bc->pos.file), t_string);
 		ir_fprintf(f, ", ");
 
 		ir_print_type(f, m, t_int);
 		ir_fprintf(f, " ");
-		ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int);
+		ir_print_exact_value(f, m, exact_value_integer(bc->pos.line), t_int);
 		ir_fprintf(f, ", ");
 
 		ir_print_type(f, m, t_int);
 		ir_fprintf(f, " ");
-		ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int);
+		ir_print_exact_value(f, m, exact_value_integer(bc->pos.column), t_int);
 		ir_fprintf(f, ", ");
 
 		ir_print_type(f, m, t_int);
@@ -1232,17 +1232,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		}
 
 		ir_fprintf(f, "(");
-		ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string);
+		ir_print_compound_element(f, m, exact_value_string(bc->pos.file), t_string);
 		ir_fprintf(f, ", ");
 
 		ir_print_type(f, m, t_int);
 		ir_fprintf(f, " ");
-		ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int);
+		ir_print_exact_value(f, m, exact_value_integer(bc->pos.line), t_int);
 		ir_fprintf(f, ", ");
 
 		ir_print_type(f, m, t_int);
 		ir_fprintf(f, " ");
-		ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int);
+		ir_print_exact_value(f, m, exact_value_integer(bc->pos.column), t_int);
 		ir_fprintf(f, ", ");
 
 		ir_print_type(f, m, t_int);
@@ -1408,10 +1408,6 @@ void print_llvm_ir(irGen *ir) {
 	irFileBuffer buf = {0}, *f = &buf;
 	ir_file_buffer_init(f, &ir->output_file);
 
-	if (m->layout.len > 0) {
-		ir_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
-	}
-
 	ir_print_encoded_local(f, str_lit("..string"));
 	ir_fprintf(f, " = type {i8*, ");
 	ir_print_type(f, m, t_int);

+ 5 - 4
src/main.c

@@ -2,15 +2,15 @@
 extern "C" {
 #endif
 
+#define USE_CUSTOM_BACKEND false
 
 #include "common.c"
 #include "timings.c"
 #include "build_settings.c"
 #include "tokenizer.c"
 #include "parser.c"
-// #include "printer.c"
 #include "checker.c"
-// #include "ssa.c"
+#include "ssa.c"
 #include "ir.c"
 #include "ir_opt.c"
 #include "ir_print.c"
@@ -151,6 +151,7 @@ int main(int argc, char **argv) {
 	init_scratch_memory(gb_megabytes(10));
 	init_global_error_collector();
 
+
 #if 1
 
 	init_build_context();
@@ -215,7 +216,7 @@ int main(int argc, char **argv) {
 
 
 #endif
-#if 0
+#if USE_CUSTOM_BACKEND
 	if (global_error_collector.count != 0) {
 		return 1;
 	}
@@ -224,7 +225,7 @@ int main(int argc, char **argv) {
 		return 1;
 	}
 
-	if (!ssa_generate(&checker.info)) {
+	if (!ssa_generate(&parser, &checker.info)) {
 		return 1;
 	}
 #else

+ 124 - 98
src/parser.c

@@ -139,6 +139,10 @@ AstNodeArray make_ast_node_array(AstFile *f) {
 		AstNodeArray elems; \
 		Token open, close; \
 	}) \
+	AST_NODE_KIND(Alias, "alias", struct { \
+		Token token; \
+		AstNode *expr; \
+	}) \
 AST_NODE_KIND(_ExprBegin,  "",  i32) \
 	AST_NODE_KIND(BadExpr,      "bad expression",         struct { Token begin, end; }) \
 	AST_NODE_KIND(TagExpr,      "tag expression",         struct { Token token, name; AstNode *expr; }) \
@@ -215,6 +219,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 	}) \
 	AST_NODE_KIND(ForStmt, "for statement", struct { \
 		Token    token; \
+		AstNode *label; \
 		AstNode *init; \
 		AstNode *cond; \
 		AstNode *post; \
@@ -222,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 	}) \
 	AST_NODE_KIND(RangeStmt, "range statement", struct { \
 		Token    token; \
+		AstNode *label; \
 		AstNode *value; \
 		AstNode *index; \
 		Token    in_token; \
@@ -235,20 +241,22 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 	}) \
 	AST_NODE_KIND(MatchStmt, "match statement", struct { \
 		Token token;   \
+		AstNode *label; \
 		AstNode *init; \
 		AstNode *tag;  \
 		AstNode *body; \
 	}) \
 	AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \
 		Token    token; \
+		AstNode *label; \
 		AstNode *tag;   \
 		AstNode *body;  \
 	}) \
 	AST_NODE_KIND(DeferStmt,  "defer statement",  struct { Token token; AstNode *stmt; }) \
-	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
+	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
 	AST_NODE_KIND(UsingStmt,  "using statement",  struct { \
 		Token token;   \
-		AstNode *node; \
+		AstNodeArray list; \
 	}) \
 	AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
 		Token string;     \
@@ -301,6 +309,10 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 		AstNode *cond;         \
 		bool is_system;        \
 	}) \
+	AST_NODE_KIND(Label, "label", struct { 	\
+		Token token; \
+		AstNode *name; \
+	}) \
 AST_NODE_KIND(_DeclEnd,   "", i32) \
 	AST_NODE_KIND(Field, "field", struct { \
 		AstNodeArray names;    \
@@ -398,7 +410,6 @@ String const ast_node_strings[] = {
 
 typedef struct AstNode {
 	AstNodeKind kind;
-	// AstNode *prev, *next; // NOTE(bill): allow for Linked list
 	u32 stmt_state_flags;
 	union {
 #define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
@@ -410,7 +421,9 @@ typedef struct AstNode {
 
 #define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_))
 #define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_);
+#ifndef case_end
 #define case_end } break;
+#endif
 
 
 gb_inline bool is_ast_node_expr(AstNode *node) {
@@ -445,6 +458,8 @@ Token ast_node_token(AstNode *node) {
 			return ast_node_token(node->CompoundLit.type);
 		}
 		return node->CompoundLit.open;
+	case AstNode_Alias:         return node->Alias.token;
+
 	case AstNode_TagExpr:       return node->TagExpr.token;
 	case AstNode_RunExpr:       return node->RunExpr.token;
 	case AstNode_BadExpr:       return node->BadExpr.begin;
@@ -492,6 +507,7 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_ValueDecl:      return ast_node_token(node->ValueDecl.names.e[0]);
 	case AstNode_ImportDecl:     return node->ImportDecl.token;
 	case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
+	case AstNode_Label:      return node->Label.token;
 
 
 	case AstNode_Field:
@@ -771,6 +787,13 @@ AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token o
 	result->CompoundLit.close = close;
 	return result;
 }
+AstNode *ast_alias(AstFile *f, Token token, AstNode *expr) {
+	AstNode *result = make_ast_node(f, AstNode_Alias);
+	result->Alias.token = token;
+	result->Alias.expr  = expr;
+	return result;
+}
+
 
 AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) {
 	AstNode *result = make_ast_node(f, AstNode_TernaryExpr);
@@ -910,19 +933,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
 	return result;
 }
 
-AstNode *ast_branch_stmt(AstFile *f, Token token) {
+AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) {
 	AstNode *result = make_ast_node(f, AstNode_BranchStmt);
 	result->BranchStmt.token = token;
+	result->BranchStmt.label = label;
 	return result;
 }
 
-AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) {
+AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) {
 	AstNode *result = make_ast_node(f, AstNode_UsingStmt);
 	result->UsingStmt.token = token;
-	result->UsingStmt.node  = node;
+	result->UsingStmt.list  = list;
 	return result;
 }
 
+
 AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
 	AstNode *result = make_ast_node(f, AstNode_AsmOperand);
 	result->AsmOperand.string  = string;
@@ -1124,6 +1149,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
 	return result;
 }
 
+AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) {
+	AstNode *result = make_ast_node(f, AstNode_Label);
+	result->Label.token = token;
+	result->Label.name  = name;
+	return result;
+}
+
 
 bool next_token(AstFile *f) {
 	Token prev = f->curr_token;
@@ -1762,6 +1794,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 		} else if (str_eq(name.string, str_lit("line"))) { return ast_basic_directive(f, token, name.string);
 		} else if (str_eq(name.string, str_lit("procedure"))) { return ast_basic_directive(f, token, name.string);
 		} else if (str_eq(name.string, str_lit("type"))) { return ast_helper_type(f, token, parse_type(f));
+		} else if (!lhs && str_eq(name.string, str_lit("alias"))) { return ast_alias(f, token, parse_expr(f, false));
 		} else {
 			operand = ast_tag_expr(f, token, name, parse_expr(f, false));
 		}
@@ -2129,7 +2162,7 @@ AstNode *parse_expr(AstFile *f, bool lhs) {
 
 AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
 	AstNodeArray list = make_ast_node_array(f);
-	do {
+	for (;;) {
 		AstNode *e = parse_expr(f, lhs);
 		array_add(&list, e);
 		if (f->curr_token.kind != Token_Comma ||
@@ -2137,7 +2170,7 @@ AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
 		    break;
 		}
 		next_token(f);
-	} while (true);
+	}
 
 	return list;
 }
@@ -2320,7 +2353,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
 	return parse_body(f);
 }
 
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow);
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow);
 
 
 AstNode *parse_results(AstFile *f) {
@@ -2339,7 +2372,7 @@ AstNode *parse_results(AstFile *f) {
 
 	AstNode *list = NULL;
 	expect_token(f, Token_OpenParen);
-	list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen);
+	list = parse_field_list(f, NULL, 0, Token_CloseParen);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	return list;
 }
@@ -2350,7 +2383,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
 
 	Token proc_token = expect_token(f, Token_proc);
 	expect_token(f, Token_OpenParen);
-	params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
+	params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	results = parse_results(f);
 
@@ -2369,17 +2402,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
 	return ast_proc_type(f, proc_token, params, results, tags, cc);
 }
 
-bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
-	if (separator == Token_Semicolon) {
-		expect_semicolon(f, param);
-	} else {
-		if (!allow_token(f, separator)) {
-			return true;
-		}
-	}
-	return false;
-}
-
 AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
 	if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
 		Token tok = f->curr_token;
@@ -2490,7 +2512,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
 	return idents;
 }
 
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) {
+
+bool parse_expect_field_separator(AstFile *f, AstNode *param) {
+	Token token = f->curr_token;
+	if (allow_token(f, Token_Comma)) {
+		return true;
+	}
+	if (token.kind == Token_Semicolon) {
+		next_token(f);
+		error(f->curr_token, "Expected a comma, got a semicolon");
+		return true;
+	}
+	return false;
+}
+
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) {
+	TokenKind separator = Token_Comma;
 	Token start_token = f->curr_token;
 
 	AstNodeArray params = make_ast_node_array(f);
@@ -2528,7 +2565,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 		AstNode *param = ast_field(f, names, type, set_flags);
 		array_add(&params, param);
 
-		parse_expect_separator(f, separator, type);
+		parse_expect_field_separator(f, type);
 
 		while (f->curr_token.kind != follow &&
 		       f->curr_token.kind != Token_EOF) {
@@ -2546,7 +2583,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 			AstNode *param = ast_field(f, names, type, set_flags);
 			array_add(&params, param);
 
-			if (parse_expect_separator(f, separator, param)) {
+			if (!parse_expect_field_separator(f, param)) {
 				break;
 			}
 		}
@@ -2575,7 +2612,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 
 
 AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
-	return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace);
+	return parse_field_list(f, field_count_, flags, Token_CloseBrace);
 }
 
 AstNode *parse_type_or_ident(AstFile *f) {
@@ -3041,46 +3078,6 @@ AstNode *parse_for_stmt(AstFile *f) {
 
 	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
 	return ast_for_stmt(f, token, init, cond, post, body);
-
-#if 0
-	Token token = expect_token(f, Token_for);
-	AstNodeArray names = parse_ident_list(f);
-	parse_check_name_list_for_reserves(f, names);
-	Token colon = expect_token_after(f, Token_in, "for name list");
-
-	isize prev_level = f->expr_level;
-	f->expr_level = -1;
-	AstNode *expr = parse_expr(f, false);
-	switch (f->curr_token.kind) {
-	case Token_HalfOpenRange:
-	case Token_Ellipsis: {
-		Token op = f->curr_token;
-		next_token(f);
-		AstNode *right = parse_expr(f, false);
-		expr = ast_interval_expr(f, op, expr, right);
-	} break;
-	}
-	f->expr_level = prev_level;
-
-	AstNode *value = NULL;
-	AstNode *index = NULL;
-	AstNode *body  = parse_block_stmt(f, false);
-
-	switch (names.count) {
-	case 1:
-		value = names.e[0];
-		break;
-	case 2:
-		value = names.e[0];
-		index = names.e[1];
-		break;
-	default:
-		error(token, "Expected at 1 or 2 identifiers");
-		return ast_bad_stmt(f, token, f->curr_token);
-	}
-
-	return ast_range_stmt(f, token, value, index, expr, body);
-#endif
 }
 
 
@@ -3102,9 +3099,17 @@ AstNode *parse_case_clause(AstFile *f) {
 
 AstNode *parse_type_case_clause(AstFile *f) {
 	Token token = f->curr_token;
-	AstNodeArray clause = make_ast_node_array(f);
+	AstNodeArray list = make_ast_node_array(f);
 	if (allow_token(f, Token_case)) {
-		array_add(&clause, parse_type(f));
+		for (;;) {
+			AstNode *t = parse_type(f);
+			array_add(&list, t);
+			if (f->curr_token.kind != Token_Comma ||
+			    f->curr_token.kind == Token_EOF) {
+			    break;
+			}
+			next_token(f);
+		}
 	} else {
 		expect_token(f, Token_default);
 	}
@@ -3112,7 +3117,7 @@ AstNode *parse_type_case_clause(AstFile *f) {
 	// expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax?
 	AstNodeArray stmts = parse_stmt_list(f);
 
-	return ast_case_clause(f, token, clause, stmts);
+	return ast_case_clause(f, token, list, stmts);
 }
 
 
@@ -3256,43 +3261,47 @@ AstNode *parse_stmt(AstFile *f) {
 
 	case Token_break:
 	case Token_continue:
-	case Token_fallthrough:
+	case Token_fallthrough: {
+		AstNode *label = NULL;
 		next_token(f);
-		s = ast_branch_stmt(f, token);
+		if (token.kind != Token_fallthrough &&
+		    f->curr_token.kind == Token_Ident) {
+			label = parse_ident(f);
+		}
+		s = ast_branch_stmt(f, token, label);
 		expect_semicolon(f, s);
 		return s;
+	}
 
 	case Token_using: {
 		// TODO(bill): Make using statements better
 		Token token = expect_token(f, Token_using);
-		AstNode *node = parse_stmt(f);
+		AstNodeArray list = parse_lhs_expr_list(f);
+		if (list.count == 0) {
+			syntax_error(token, "Illegal use of `using` statement");
+			expect_semicolon(f, NULL);
+			return ast_bad_stmt(f, token, f->curr_token);
+		}
 
-		switch (node->kind) {
-		case AstNode_ValueDecl:
-			if (!node->ValueDecl.is_var) {
+		if (f->curr_token.kind != Token_Colon) {
+			expect_semicolon(f, list.e[list.count-1]);
+			return ast_using_stmt(f, token, list);
+		}
+
+		AstNode *decl = parse_simple_stmt(f, false);
+		expect_semicolon(f, decl);
+
+		if (decl->kind == AstNode_ValueDecl) {
+			if (!decl->ValueDecl.is_var) {
 				syntax_error(token, "`using` may not be applied to constant declarations");
-			} else {
-				if (f->curr_proc == NULL) {
-					syntax_error(token, "`using` is not allowed at the file scope");
-				} else {
-					node->ValueDecl.flags |= VarDeclFlag_using;
-				}
+				return decl;
 			}
-			return node;
-		case AstNode_ExprStmt: {
-			AstNode *e = unparen_expr(node->ExprStmt.expr);
-			while (e->kind == AstNode_SelectorExpr) {
-				e = unparen_expr(e->SelectorExpr.selector);
-			}
-			if (e->kind == AstNode_Ident) {
-				return ast_using_stmt(f, token, node);
-			} else if (e->kind == AstNode_Implicit) {
-				syntax_error(token, "Illegal use of `using` statement with implicit value `%.*s`", LIT(e->Implicit.string));
-				return ast_bad_stmt(f, token, f->curr_token);
+			if (f->curr_proc == NULL) {
+				syntax_error(token, "`using` is not allowed at the file scope");
+			} else {
+				decl->ValueDecl.flags |= VarDeclFlag_using;
 			}
-		} break;
-
-
+			return decl;
 		}
 
 		syntax_error(token, "Illegal use of `using` statement");
@@ -3345,7 +3354,25 @@ AstNode *parse_stmt(AstFile *f) {
 		Token name = expect_token(f, Token_Ident);
 		String tag = name.string;
 
-		if (str_eq(tag, str_lit("import"))) {
+		if (str_eq(tag, str_lit("label"))) {
+			AstNode *name  = parse_ident(f);
+			AstNode *label = ast_label_decl(f, token, name);
+			AstNode *stmt  = parse_stmt(f);
+
+		#define _SET_LABEL(Kind_, label_) case GB_JOIN2(AstNode_, Kind_): (stmt->Kind_).label = label_; break
+			switch (stmt->kind) {
+			_SET_LABEL(ForStmt, label);
+			_SET_LABEL(RangeStmt, label);
+			_SET_LABEL(MatchStmt, label);
+			_SET_LABEL(TypeMatchStmt, label);
+			default:
+				syntax_error(token, "#label may only be applied to a loop");
+				break;
+			}
+		#undef _SET_LABEL
+
+			return stmt;
+		} else if (str_eq(tag, str_lit("import"))) {
 			AstNode *cond = NULL;
 			Token import_name = {0};
 
@@ -3505,7 +3532,6 @@ AstNode *parse_stmt(AstFile *f) {
 			return s;
 		}
 
-
 		if (str_eq(tag, str_lit("include"))) {
 			syntax_error(token, "#include is not a valid import declaration kind. Use #load instead");
 			s = ast_bad_stmt(f, token, f->curr_token);
@@ -3827,7 +3853,7 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
 				gb_printf_err("File permissions problem");
 				break;
 			case ParseFile_NotFound:
-				gb_printf_err("File cannot be found");
+				gb_printf_err("File cannot be found (`%.*s`)", LIT(import_path));
 				break;
 			case ParseFile_InvalidToken:
 				gb_printf_err("Invalid token found in file");

+ 2269 - 0
src/ssa.c

@@ -0,0 +1,2269 @@
+typedef struct ssaModule           ssaModule;
+typedef struct ssaValue            ssaValue;
+typedef struct ssaValueArgs        ssaValueArgs;
+typedef struct ssaBlock            ssaBlock;
+typedef struct ssaProc             ssaProc;
+typedef struct ssaEdge             ssaEdge;
+typedef struct ssaRegister         ssaRegister;
+typedef struct ssaTargetList       ssaTargetList;
+typedef enum   ssaBlockKind        ssaBlockKind;
+typedef enum   ssaBranchPrediction ssaBranchPrediction;
+
+String ssa_mangle_name(ssaModule *m, String path, Entity *e);
+
+#define MAP_TYPE ssaValue *
+#define MAP_PROC map_ssa_value_
+#define MAP_NAME MapSsaValue
+#include "map.c"
+
+typedef Array(ssaValue *) ssaValueArray;
+
+#include "ssa_op.c"
+
+#define SSA_DEFAULT_VALUE_ARG_CAPACITY 8
+struct ssaValueArgs {
+	ssaValue ** e;
+	isize       count;
+	isize       capacity;
+	ssaValue *  backing[SSA_DEFAULT_VALUE_ARG_CAPACITY];
+	gbAllocator allocator;
+};
+
+struct ssaValue {
+	i32           id;    // Unique identifier but the pointer could be used too
+	ssaOp         op;    // Operation that computes this value
+	Type *        type;
+	ssaBlock *    block; // Containing basic block
+
+	i32           uses;
+	ssaValueArgs  args;
+	ExactValue    exact_value; // Used for constants
+
+	String        comment_string;
+};
+
+enum ssaBlockKind {
+	ssaBlock_Invalid,
+
+	// NOTE(bill): These are the generic block types and for more specific
+	// architectures, these could become conditions blocks like amd64 LT or EQ
+	ssaBlock_Entry, // Entry point
+	ssaBlock_Plain,
+	ssaBlock_If,
+	ssaBlock_Ret,
+	ssaBlock_RetJmp, // Stores return value and jumps to Ret block
+	ssaBlock_Exit,
+
+	ssaBlock_Count,
+};
+
+enum ssaBranchPrediction {
+	ssaBranch_Unknown  = 0,
+	ssaBranch_Likely   = +1,
+	ssaBranch_Unlikely = -1,
+};
+
+// ssaEdge represents a control flow graph (CFG) edge
+struct ssaEdge {
+	// Succs array: Block To
+	// Preds array: Block From
+	ssaBlock *block;
+	// Index of reverse edge
+	isize     index;
+};
+
+typedef Array(ssaEdge) ssaEdgeArray;
+
+struct ssaBlock {
+	i32                  id;   // Unique identifier but the pointer could be used too
+	ssaBlockKind         kind;
+	ssaProc *            proc; // Containing procedure
+	String               name; // Optional
+
+	// Likely branch direction
+	ssaBranchPrediction likeliness;
+
+	// Determines how a block exits
+	// It depends on the type of block:
+	//  - BlockIf will be a boolean value
+	//  - BlockExit will be a memory control value
+	ssaValue *control;
+
+	ssaValueArray values;
+	ssaEdgeArray  preds;
+	ssaEdgeArray  succs;
+};
+
+struct ssaTargetList {
+	ssaTargetList *prev;
+	ssaBlock *     break_;
+	ssaBlock *     continue_;
+	ssaBlock *     fallthrough_;
+};
+
+struct ssaProc {
+	ssaModule *       module;     // Parent module
+	gbAllocator       allocator;  // Same allocator as the parent module
+	String            name;       // Mangled name
+	Entity *          entity;
+	DeclInfo *        decl_info;
+
+	Array(ssaBlock *) blocks;
+	ssaBlock *        entry;      // Entry block
+	ssaBlock *        exit;       // Exit block
+	ssaBlock *        curr_block;
+
+	ssaTargetList *   target_list;
+
+	i32               block_id;
+	i32               value_id;
+	MapSsaValue       values;   // Key: Entity *
+};
+
+struct ssaRegister {
+	i32 id;
+	i32 size;
+};
+
+struct ssaModule {
+	CheckerInfo *      info;
+	gbAllocator        allocator;
+	gbArena            arena;
+	gbAllocator        tmp_allocator;
+	gbArena            tmp_arena;
+
+	MapEntity          min_dep_map; // Key: Entity *
+	MapSsaValue        values;      // Key: Entity *
+	// List of registers for the specific architecture
+	Array(ssaRegister) registers;
+
+	ssaProc *proc; // current procedure
+
+	Entity *entry_point_entity;
+
+	u32 stmt_state_flags;
+
+	Array(ssaProc *)  procs;
+	ssaValueArray     procs_to_generate;
+};
+
+
+void ssa_push_target_list(ssaProc *p, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) {
+	ssaTargetList *tl = gb_alloc_item(p->allocator, ssaTargetList);
+	tl->prev          = p->target_list;
+	tl->break_        = break_;
+	tl->continue_     = continue_;
+	tl->fallthrough_  = fallthrough_;
+	p->target_list    = tl;
+}
+
+void ssa_pop_target_list(ssaProc *p) {
+	p->target_list = p->target_list->prev;
+}
+
+
+ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind, char *name) {
+	ssaBlock *b = gb_alloc_item(p->allocator, ssaBlock);
+	b->id = p->block_id++;
+	b->kind = kind;
+	b->proc = p;
+	if (name != NULL || name[0] != 0) {
+		b->name = make_string_c(name);
+	}
+
+	array_init(&b->values, heap_allocator());
+	array_init(&b->preds,  heap_allocator());
+	array_init(&b->succs,  heap_allocator());
+	array_add(&p->blocks, b);
+	return b;
+}
+
+void ssa_clear_block(ssaProc *p, ssaBlock *b) {
+	GB_ASSERT(b->proc != NULL);
+	array_clear(&b->values);
+	array_clear(&b->preds);
+	array_clear(&b->succs);
+	b->proc = NULL;
+	b->kind = ssaBlock_Plain;
+}
+
+
+void ssa_start_block(ssaProc *p, ssaBlock *b) {
+	GB_ASSERT(p->curr_block == NULL);
+	p->curr_block = b;
+}
+
+ssaBlock *ssa_end_block(ssaProc *p) {
+	ssaBlock *b = p->curr_block;
+	if (b == NULL) {
+		return NULL;
+	}
+	p->curr_block = NULL;
+	return b;
+}
+
+void ssa_add_edge_to(ssaBlock *b, ssaBlock *c) {
+	if (b == NULL) {
+		return;
+	}
+	GB_ASSERT(c != NULL);
+	isize i = b->succs.count;
+	isize j = b->preds.count;
+	ssaEdge s = {c, j};
+	ssaEdge p = {b, i};
+	array_add(&b->succs, s);
+	array_add(&c->preds, p);
+}
+
+void ssa_set_control(ssaBlock *b, ssaValue *v) {
+	if (b->control != NULL) {
+		b->control->uses--;
+	}
+	b->control = v;
+	if (v != NULL) {
+		v->uses++;
+	}
+}
+
+void ssa_emit_jump(ssaProc *p, ssaBlock *edge) {
+	ssa_add_edge_to(ssa_end_block(p), edge);
+}
+
+void ssa_init_value_args(ssaValueArgs *va, gbAllocator a) {
+	va->e              = va->backing;
+	va->count          = 0;
+	va->capacity       = gb_count_of(va->backing);
+	va->allocator      = a;
+}
+
+void ssa_add_arg(ssaValueArgs *va, ssaValue *arg) {
+	if (va->count >= va->capacity) {
+		isize capacity = 2*va->capacity;
+		if (va->e == va->backing) { // Replace the backing with an allocated version instead
+			ssaValue **new_args = gb_alloc_array(va->allocator, ssaValue *, capacity);
+			gb_memcopy_array(new_args, va->e, va->count);
+			va->e = new_args;
+		} else {
+			isize old_cap_size = va->capacity * gb_size_of(ssaValue *);
+			isize new_cap_size = capacity * gb_size_of(ssaValue *);
+			va->e = gb_resize(va->allocator, va->e, old_cap_size, new_cap_size);
+		}
+		va->capacity = capacity;
+	}
+	va->e[va->count++] = arg; arg->uses++;
+}
+
+
+
+ssaValue *ssa_new_value(ssaProc *p, ssaOp op, Type *t, ssaBlock *b) {
+	GB_ASSERT(b != NULL);
+	ssaValue *v = gb_alloc_item(p->allocator, ssaValue);
+	v->id    = p->value_id++;
+	v->op    = op;
+	v->type  = t;
+	v->block = b;
+	ssa_init_value_args(&v->args, p->allocator);
+	array_add(&b->values, v);
+	return v;
+}
+
+ssaValue *ssa_new_value0(ssaProc *p, ssaOp op, Type *t) {
+	ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
+	return v;
+}
+ssaValue *ssa_new_value0v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) {
+	ssaValue *v = ssa_new_value0(p, op, t);
+	v->exact_value = exact_value;
+	return v;
+}
+
+ssaValue *ssa_new_value1(ssaProc *p, ssaOp op, Type *t, ssaValue *arg) {
+	ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
+	ssa_add_arg(&v->args, arg);
+	return v;
+}
+ssaValue *ssa_new_value1v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg) {
+	ssaValue *v = ssa_new_value1(p, op, t, arg);
+	v->exact_value = exact_value;
+	return v;
+}
+ssaValue *ssa_new_value1i(ssaProc *p, ssaOp op, Type *t, i64 i, ssaValue *arg) {
+	return ssa_new_value1v(p, op, t, exact_value_integer(i), arg);
+}
+
+ssaValue *ssa_new_value2(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1) {
+	ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
+	ssa_add_arg(&v->args, arg0);
+	ssa_add_arg(&v->args, arg1);
+	return v;
+}
+ssaValue *ssa_new_value2v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1) {
+	ssaValue *v = ssa_new_value2(p, op, t, arg0, arg1);
+	v->exact_value = exact_value;
+	return v;
+}
+
+ssaValue *ssa_new_value3(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) {
+	ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
+	ssa_add_arg(&v->args, arg0);
+	ssa_add_arg(&v->args, arg1);
+	ssa_add_arg(&v->args, arg2);
+	return v;
+}
+ssaValue *ssa_new_value3v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) {
+	ssaValue *v = ssa_new_value3(p, op, t, arg0, arg1, arg2);
+	v->exact_value = exact_value;
+	return v;
+}
+
+ssaValue *ssa_new_value4(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2, ssaValue *arg3) {
+	ssaValue *v = ssa_new_value(p, op, t, p->curr_block);
+	ssa_add_arg(&v->args, arg0);
+	ssa_add_arg(&v->args, arg1);
+	ssa_add_arg(&v->args, arg2);
+	ssa_add_arg(&v->args, arg3);
+	return v;
+}
+
+ssaValue *ssa_const_val(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) {
+	return ssa_new_value0v(p, op, t, exact_value);
+}
+
+ssaValue *ssa_const_bool        (ssaProc *p, Type *t, bool   c)     { return ssa_const_val(p, ssaOp_ConstBool,   t, exact_value_bool(c)); }
+ssaValue *ssa_const_i8          (ssaProc *p, Type *t, i8     c)     { return ssa_const_val(p, ssaOp_Const8,      t, exact_value_integer(cast(i64)c)); }
+ssaValue *ssa_const_i16         (ssaProc *p, Type *t, i16    c)     { return ssa_const_val(p, ssaOp_Const16,     t, exact_value_integer(cast(i64)c)); }
+ssaValue *ssa_const_i32         (ssaProc *p, Type *t, i32    c)     { return ssa_const_val(p, ssaOp_Const32,     t, exact_value_integer(cast(i64)c)); }
+ssaValue *ssa_const_i64         (ssaProc *p, Type *t, i64    c)     { return ssa_const_val(p, ssaOp_Const64,     t, exact_value_integer(cast(i64)c)); }
+ssaValue *ssa_const_f32         (ssaProc *p, Type *t, f32    c)     { return ssa_const_val(p, ssaOp_Const32F,    t, exact_value_float(c)); }
+ssaValue *ssa_const_f64         (ssaProc *p, Type *t, f64    c)     { return ssa_const_val(p, ssaOp_Const64F,    t, exact_value_float(c)); }
+ssaValue *ssa_const_string      (ssaProc *p, Type *t, String c)     { return ssa_const_val(p, ssaOp_ConstString, t, exact_value_string(c)); }
+ssaValue *ssa_const_empty_string(ssaProc *p, Type *t)               { return ssa_const_val(p, ssaOp_ConstString, t, (ExactValue){0}); }
+ssaValue *ssa_const_slice       (ssaProc *p, Type *t, ExactValue v) { return ssa_const_val(p, ssaOp_ConstSlice,  t, v); }
+ssaValue *ssa_const_nil         (ssaProc *p, Type *t)               { return ssa_const_val(p, ssaOp_ConstNil,    t, (ExactValue){0}); }
+
+ssaValue *ssa_const_int(ssaProc *p, Type *t, i64 c) {
+	switch (8*type_size_of(p->allocator, t)) {
+	case 8:  return ssa_const_i8 (p, t, cast(i8)c);
+	case 16: return ssa_const_i16(p, t, cast(i16)c);
+	case 32: return ssa_const_i32(p, t, cast(i32)c);
+	case 64: return ssa_const_i64(p, t, cast(i64)c);
+	}
+	GB_PANIC("Unknown int size");
+	return NULL;
+}
+
+void ssa_reset_value_args(ssaValue *v) {
+	for_array(i, v->args) {
+		v->args.e[i]->uses--;
+	}
+	v->args.count = 0;
+}
+
+void ssa_reset(ssaValue *v, ssaOp op) {
+	v->op = op;
+	v->exact_value = (ExactValue){0};
+	ssa_reset_value_args(v);
+}
+
+ssaValue *ssa_emit_load(ssaProc *p, ssaValue *v) {
+	GB_ASSERT(is_type_pointer(v->type));
+	return ssa_new_value1(p, ssaOp_Load, type_deref(v->type), v);
+}
+
+ssaValue *ssa_emit_store(ssaProc *p, ssaValue *dst, ssaValue *v) {
+	GB_ASSERT(is_type_pointer(dst->type));
+#if 1
+	// NOTE(bill): Sanity check
+	Type *a = core_type(type_deref(dst->type));
+	Type *b = core_type(v->type);
+	if (!is_type_untyped(b)) {
+		GB_ASSERT_MSG(are_types_identical(a, b), "%s %s", type_to_string(a), type_to_string(b));
+	}
+#endif
+	return ssa_new_value2(p, ssaOp_Store, dst->type, dst, v);
+}
+
+bool ssa_is_op_const(ssaOp op) {
+	switch (op) {
+	case ssaOp_ConstBool:
+	case ssaOp_ConstString:
+	case ssaOp_ConstSlice:
+	case ssaOp_ConstNil:
+	case ssaOp_Const8:
+	case ssaOp_Const16:
+	case ssaOp_Const32:
+	case ssaOp_Const64:
+	case ssaOp_Const32F:
+	case ssaOp_Const64F:
+		return true;
+	}
+	return false;
+}
+
+
+
+bool ssa_is_blank_ident(AstNode *node) {
+	if (node->kind == AstNode_Ident) {
+		ast_node(i, Ident, node);
+		return is_blank_ident(i->string);
+	}
+	return false;
+}
+
+
+typedef enum ssaAddrKind {
+	ssaAddr_Default,
+	ssaAddr_Map,
+} ssaAddrKind;
+
+typedef struct ssaAddr {
+	ssaValue *  addr;
+	ssaAddrKind kind;
+} ssaAddr;
+
+ssaAddr ssa_addr(ssaValue *v) {
+	if (v != NULL) {
+		GB_ASSERT(is_type_pointer(v->type));
+	}
+	ssaAddr addr = {0};
+	addr.addr = v;
+	return addr;
+}
+
+Type *ssa_addr_type(ssaAddr addr) {
+	if (addr.addr == NULL) {
+		return NULL;
+	}
+
+	if (addr.kind == ssaAddr_Map) {
+		GB_PANIC("TODO: ssa_addr_type");
+		return NULL;
+	}
+
+	Type *t = addr.addr->type;
+	GB_ASSERT(is_type_pointer(t));
+	return type_deref(t);
+}
+
+
+
+ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_info) {
+	ssaProc *p = gb_alloc_item(m->allocator, ssaProc);
+	p->module    = m;
+	p->allocator = m->allocator;
+	p->name      = name;
+	p->entity    = entity;
+	p->decl_info = decl_info;
+
+	array_init(&p->blocks, heap_allocator());
+	map_ssa_value_init(&p->values, heap_allocator());
+
+	return p;
+}
+
+ssaAddr ssa_add_local(ssaProc *p, Entity *e, AstNode *expr) {
+	Type *t = make_type_pointer(p->allocator, e->type);
+
+	ssaBlock *cb = p->curr_block;
+	p->curr_block = p->entry;
+	ssaValue *local = ssa_new_value0(p, ssaOp_Local, t);
+	p->curr_block = cb;
+
+	map_ssa_value_set(&p->values,         hash_pointer(e), local);
+	map_ssa_value_set(&p->module->values, hash_pointer(e), local);
+	local->comment_string = e->token.string;
+
+	ssaValue *addr = ssa_new_value1(p, ssaOp_Addr, local->type, local);
+	ssa_new_value1(p, ssaOp_Zero, t, addr);
+	return ssa_addr(addr);
+}
+ssaAddr ssa_add_local_for_ident(ssaProc *p, AstNode *name) {
+	Entity **found = map_entity_get(&p->module->info->definitions, hash_pointer(name));
+	if (found) {
+		Entity *e = *found;
+		return ssa_add_local(p, e, name);
+	}
+
+	return ssa_addr(NULL);
+}
+
+ssaAddr ssa_add_local_generated(ssaProc *p, Type *t) {
+	GB_ASSERT(t != NULL);
+
+	Scope *scope = NULL;
+	if (p->curr_block) {
+		// scope = p->curr_block->scope;
+	}
+	Entity *e = make_entity_variable(p->allocator, scope, empty_token, t, false);
+	return ssa_add_local(p, e, NULL);
+}
+
+void ssa_emit_comment(ssaProc *p, String s) {
+	// ssa_new_value0v(p, ssaOp_Comment, NULL, exact_value_string(s));
+}
+
+#define SSA_MAX_STRUCT_FIELD_COUNT 4
+
+bool can_ssa_type(Type *t) {
+	i64 s = type_size_of(heap_allocator(), t);
+	if (s > 4*build_context.word_size) {
+		return false;
+	}
+	t = core_type(t);
+
+	switch (t->kind) {
+	case Type_Array:
+		return t->Array.count == 0;
+	case Type_Vector:
+		return s < 2*build_context.word_size;
+
+	case Type_DynamicArray:
+		return false;
+	case Type_Map:
+		return false;
+	case Type_Tuple:
+		if (t->Tuple.variable_count > SSA_MAX_STRUCT_FIELD_COUNT) {
+			return false;
+		}
+		for (isize i = 0; i < t->Tuple.variable_count; i++) {
+			if (!can_ssa_type(t->Tuple.variables[i]->type)) {
+				return false;
+			}
+		}
+		return true;
+	case Type_Record:
+		if (t->Record.kind == TypeRecord_Union) {
+			return false;
+		} else if (t->Record.kind == TypeRecord_Struct) {
+			if (t->Record.field_count > SSA_MAX_STRUCT_FIELD_COUNT) {
+				return false;
+			}
+			for (isize i = 0; i < t->Record.field_count; i++) {
+				if (!can_ssa_type(t->Record.fields[i]->type)) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+	return true;
+}
+
+ssaAddr   ssa_build_addr     (ssaProc *p, AstNode *expr);
+ssaValue *ssa_build_expr     (ssaProc *p, AstNode *expr);
+void      ssa_build_stmt     (ssaProc *p, AstNode *node);
+void      ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes);
+ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel);
+
+void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) {
+	if (addr.addr == NULL) {
+		return;
+	}
+	if (addr.kind == ssaAddr_Map) {
+		GB_PANIC("TODO(bill): ssa_addr_store");
+		return;
+	}
+
+	ssa_emit_store(p, addr.addr, value);
+}
+
+ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) {
+	if (addr.addr == NULL) {
+		return NULL;
+	}
+
+	if (addr.kind == ssaAddr_Map) {
+		GB_PANIC("here\n");
+		return NULL;
+	}
+
+	Type *t = addr.addr->type;
+	Type *bt = base_type(t);
+	if (bt->kind == Type_Proc) {
+		return addr.addr;
+	}
+
+	return ssa_emit_load(p, addr.addr);
+}
+
+ssaValue *ssa_get_using_variable(ssaProc *p, Entity *e) {
+	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
+	String name = e->token.string;
+	Entity *parent = e->using_parent;
+	Selection sel = lookup_field(p->allocator, parent->type, name, false);
+	GB_ASSERT(sel.entity != NULL);
+	ssaValue **pv = map_ssa_value_get(&p->module->values, hash_pointer(parent));
+	ssaValue *v = NULL;
+	if (pv != NULL) {
+		v = *pv;
+	} else {
+		v = ssa_build_addr(p, e->using_expr).addr;
+	}
+	GB_ASSERT(v != NULL);
+	GB_ASSERT(type_deref(v->type) == parent->type);
+	return ssa_emit_deep_field_ptr_index(p, v, sel);
+}
+
+ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) {
+	GB_ASSERT(e != NULL);
+
+	ssaValue *v = NULL;
+	ssaValue **found = map_ssa_value_get(&p->module->values, hash_pointer(e));
+	if (found) {
+		v = *found;
+	} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) {
+		// NOTE(bill): Calculate the using variable every time
+		v = ssa_get_using_variable(p, e);
+	}
+
+	if (v == NULL) {
+		GB_PANIC("Unknown value: %.*s, entity: %p %.*s\n", LIT(e->token.string), e, LIT(entity_strings[e->kind]));
+	}
+
+	return ssa_addr(v);
+}
+
+
+ssaValue *ssa_emit_conv(ssaProc *p, ssaValue *v, Type *t) {
+	Type *src_type = v->type;
+	if (are_types_identical(t, src_type)) {
+		return v;
+	}
+
+	Type *src = core_type(src_type);
+	Type *dst = core_type(t);
+
+	if (is_type_untyped_nil(src)) {
+		return ssa_const_nil(p, t);
+	}
+
+	// Pointer <-> Pointer
+	if (is_type_pointer(src) && is_type_pointer(dst)) {
+		return ssa_new_value1(p, ssaOp_Copy, dst, v);
+	}
+	// proc <-> proc
+	if (is_type_proc(src) && is_type_proc(dst)) {
+		return ssa_new_value1(p, ssaOp_Copy, dst, v);
+	}
+	// pointer -> proc
+	if (is_type_pointer(src) && is_type_proc(dst)) {
+		return ssa_new_value1(p, ssaOp_Copy, dst, v);
+	}
+	// proc -> pointer
+	if (is_type_proc(src) && is_type_pointer(dst)) {
+		return ssa_new_value1(p, ssaOp_Copy, dst, v);
+	}
+
+
+	gb_printf_err("ssa_emit_conv: src -> dst\n");
+	gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
+	gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
+
+
+	GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
+
+	return NULL;
+}
+
+
+// NOTE(bill): Returns NULL if not possible
+ssaValue *ssa_address_from_load_or_generate_local(ssaProc *p, ssaValue *v) {
+	if (v->op == ssaOp_Load) {
+		return v->args.e[0];
+	}
+	ssaAddr addr = ssa_add_local_generated(p, v->type);
+	ssa_new_value2(p, ssaOp_Store, addr.addr->type, addr.addr, v);
+	return addr.addr;
+}
+
+
+ssaValue *ssa_emit_array_index(ssaProc *p, ssaValue *v, ssaValue *index) {
+	GB_ASSERT(v != NULL);
+	GB_ASSERT(is_type_pointer(v->type));
+	Type *t = base_type(type_deref(v->type));
+	GB_ASSERT_MSG(is_type_array(t) || is_type_vector(t), "%s", type_to_string(t));
+	Type *elem_ptr = NULL;
+	if (is_type_array(t)) {
+		elem_ptr = make_type_pointer(p->allocator, t->Array.elem);
+	} else if (is_type_vector(t)) {
+		elem_ptr = make_type_pointer(p->allocator, t->Vector.elem);
+	}
+
+	return ssa_new_value2(p, ssaOp_ArrayIndex, elem_ptr, v, index);
+}
+
+ssaValue *ssa_emit_ptr_index(ssaProc *p, ssaValue *s, i64 index) {
+	gbAllocator a = p->allocator;
+	Type *t = base_type(type_deref(s->type));
+	Type *result_type = NULL;
+
+	if (is_type_struct(t)) {
+		GB_ASSERT(t->Record.field_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		result_type = make_type_pointer(a, t->Record.fields[index]->type);
+	} else if (is_type_union(t)) {
+		type_set_offsets(a, t);
+		GB_ASSERT(t->Record.field_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		result_type = make_type_pointer(a, t->Record.fields[index]->type);
+		i64 offset = t->Record.offsets[index];
+		ssaValue *ptr = ssa_emit_conv(p, s, t_u8_ptr);
+		ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset));
+		return ssa_emit_conv(p, ptr, result_type);
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->Tuple.variable_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
+		result_type = make_type_pointer(a, t->Tuple.variables[index]->type);
+	} else if (is_type_slice(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
+		case 1: result_type = make_type_pointer(a, t_int); break;
+		case 2: result_type = make_type_pointer(a, t_int); break;
+		}
+	} else if (is_type_string(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t_u8_ptr); break;
+		case 1: result_type = make_type_pointer(a, t_int);    break;
+		}
+	} else if (is_type_any(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t_type_info_ptr); break;
+		case 1: result_type = make_type_pointer(a, t_rawptr);        break;
+		}
+	} else if (is_type_dynamic_array(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->DynamicArray.elem)); break;
+		case 1: result_type = t_int_ptr;                                      break;
+		case 2: result_type = t_int_ptr;                                      break;
+		case 3: result_type = t_allocator_ptr;                                break;
+		}
+	} else if (is_type_dynamic_map(t)) {
+		Type *gst = t->Map.generated_struct_type;
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, gst->Record.fields[0]->type); break;
+		case 1: result_type = make_type_pointer(a, gst->Record.fields[1]->type); break;
+		}
+	}else {
+		GB_PANIC("TODO(bill): ssa_emit_ptr_index type: %s, %d", type_to_string(s->type), index);
+	}
+
+	GB_ASSERT(result_type != NULL);
+
+	return ssa_new_value1i(p, ssaOp_PtrIndex, result_type, index, s);
+}
+ssaValue *ssa_emit_value_index(ssaProc *p, ssaValue *s, i64 index) {
+	if (s->op == ssaOp_Load) {
+		if (!can_ssa_type(s->type)) {
+			ssaValue *e = ssa_emit_ptr_index(p, s->args.e[0], index);
+			return ssa_emit_load(p, e);
+		}
+	}
+	GB_ASSERT(can_ssa_type(s->type));
+
+	gbAllocator a = p->allocator;
+	Type *t = base_type(s->type);
+	Type *result_type = NULL;
+
+	if (is_type_struct(t)) {
+		GB_ASSERT(t->Record.field_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		result_type = t->Record.fields[index]->type;
+	} else if (is_type_union(t)) {
+		type_set_offsets(a, t);
+		GB_ASSERT(t->Record.field_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		Type *ptr_type = make_type_pointer(a, t->Record.fields[index]->type);
+		i64 offset = t->Record.offsets[index];
+		ssaValue *ptr = ssa_address_from_load_or_generate_local(p, s);
+		ptr = ssa_emit_conv(p, s, t_u8_ptr);
+		ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset));
+		ptr = ssa_emit_conv(p, ptr, ptr_type);
+		return ssa_emit_load(p, ptr);
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->Tuple.variable_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
+		result_type = t->Tuple.variables[index]->type;
+	} else if (is_type_slice(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
+		case 1: result_type = t_int; break;
+		case 2: result_type = t_int; break;
+		}
+	} else if (is_type_string(t)) {
+		switch (index) {
+		case 0: result_type = t_u8_ptr; break;
+		case 1: result_type = t_int;    break;
+		}
+	} else if (is_type_any(t)) {
+		switch (index) {
+		case 0: result_type = t_type_info_ptr; break;
+		case 1: result_type = t_rawptr;        break;
+		}
+	} else if (is_type_dynamic_array(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t->DynamicArray.elem); break;
+		case 1: result_type = t_int;                                      break;
+		case 2: result_type = t_int;                                      break;
+		case 3: result_type = t_allocator;                                break;
+		}
+	} else if (is_type_dynamic_map(t)) {
+		Type *gst = t->Map.generated_struct_type;
+		switch (index) {
+		case 0: result_type = gst->Record.fields[0]->type; break;
+		case 1: result_type = gst->Record.fields[1]->type; break;
+		}
+	} else {
+		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(s->type), index);
+	}
+
+	GB_ASSERT(result_type != NULL);
+
+	return ssa_new_value1i(p, ssaOp_ValueIndex, result_type, index, s);
+}
+
+
+ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel) {
+	GB_ASSERT(sel.index.count > 0);
+	Type *type = type_deref(e->type);
+
+	for_array(i, sel.index) {
+		i32 index = cast(i32)sel.index.e[i];
+		if (is_type_pointer(type)) {
+			type = type_deref(type);
+			e = ssa_emit_load(p, e);
+		}
+		type = base_type(type);
+
+
+		if (is_type_raw_union(type)) {
+			type = type->Record.fields[index]->type;
+			e = ssa_emit_conv(p, e, make_type_pointer(p->allocator, type));
+		} else if (type->kind == Type_Record) {
+			type = type->Record.fields[index]->type;
+			e = ssa_emit_ptr_index(p, e, index);
+		} else if (type->kind == Type_Tuple) {
+			type = type->Tuple.variables[index]->type;
+			e = ssa_emit_ptr_index(p, e, index);
+		}else if (type->kind == Type_Basic) {
+			switch (type->Basic.kind) {
+			case Basic_any: {
+				if (index == 0) {
+					type = t_type_info_ptr;
+				} else if (index == 1) {
+					type = t_rawptr;
+				}
+				e = ssa_emit_ptr_index(p, e, index);
+			} break;
+
+			case Basic_string:
+				e = ssa_emit_ptr_index(p, e, index);
+				break;
+
+			default:
+				GB_PANIC("un-gep-able type");
+				break;
+			}
+		} else if (type->kind == Type_Slice) {
+			e = ssa_emit_ptr_index(p, e, index);
+		} else if (type->kind == Type_DynamicArray) {
+			e = ssa_emit_ptr_index(p, e, index);
+		} else if (type->kind == Type_Vector) {
+			e = ssa_emit_array_index(p, e, ssa_const_int(p, t_int, index));
+		} else if (type->kind == Type_Array) {
+			e = ssa_emit_array_index(p, e, ssa_const_int(p, t_int, index));
+		} else if (type->kind == Type_Map) {
+			e = ssa_emit_ptr_index(p, e, 1);
+			switch (index) {
+			case 0: e = ssa_emit_ptr_index(p, e, 1); break; // count
+			case 1: e = ssa_emit_ptr_index(p, e, 2); break; // capacity
+			case 2: e = ssa_emit_ptr_index(p, e, 3); break; // allocator
+			}
+		} else {
+			GB_PANIC("un-gep-able type");
+		}
+	}
+
+	return e;
+}
+
+ssaValue *ssa_emit_deep_field_value_index(ssaProc *p, ssaValue *e, Selection sel) {
+	GB_ASSERT(sel.index.count > 0);
+	Type *type = e->type;
+	if (e->op == ssaOp_Load) {
+		if (!can_ssa_type(e->type)) {
+			ssaValue *ptr = ssa_emit_deep_field_ptr_index(p, e->args.e[0], sel);
+			return ssa_emit_load(p, ptr);
+		}
+	}
+	GB_ASSERT(can_ssa_type(e->type));
+
+	for_array(i, sel.index) {
+		i32 index = cast(i32)sel.index.e[i];
+		if (is_type_pointer(type)) {
+			e = ssa_emit_load(p, e);
+		}
+		type = base_type(type);
+
+
+		if (is_type_raw_union(type)) {
+			GB_PANIC("TODO(bill): IS THIS EVEN CORRECT?");
+			type = type->Record.fields[index]->type;
+			e = ssa_emit_conv(p, e, type);
+		} else if (type->kind == Type_Map) {
+			e = ssa_emit_value_index(p, e, 1);
+			switch (index) {
+			case 0: e = ssa_emit_value_index(p, e, 1); break; // count
+			case 1: e = ssa_emit_value_index(p, e, 2); break; // capacity
+			case 2: e = ssa_emit_value_index(p, e, 3); break; // allocator
+			}
+		} else {
+			e = ssa_emit_value_index(p, e, index);
+		}
+	}
+
+	return e;
+}
+
+
+
+
+
+ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) {
+	switch (expr->kind) {
+	case_ast_node(i, Ident, expr);
+		if (ssa_is_blank_ident(expr)) {
+			ssaAddr val = {0};
+			return val;
+		}
+		Entity *e = entity_of_ident(p->module->info, expr);
+		return ssa_build_addr_from_entity(p, e, expr);
+	case_end;
+
+	case_ast_node(pe, ParenExpr, expr);
+		return ssa_build_addr(p, unparen_expr(expr));
+	case_end;
+
+	case_ast_node(se, SelectorExpr, expr);
+		ssa_emit_comment(p, str_lit("SelectorExpr"));
+		AstNode *sel = unparen_expr(se->selector);
+		if (sel->kind == AstNode_Ident) {
+			String selector = sel->Ident.string;
+			TypeAndValue *tav = type_and_value_of_expression(p->module->info, se->expr);
+
+			if (tav == NULL) {
+				// NOTE(bill): Imports
+				Entity *imp = entity_of_ident(p->module->info, se->expr);
+				if (imp != NULL) {
+					GB_ASSERT(imp->kind == Entity_ImportName);
+				}
+				return ssa_build_addr(p, se->selector);
+			}
+
+
+			Type *type = base_type(tav->type);
+			if (tav->mode == Addressing_Type) { // Addressing_Type
+				GB_PANIC("TODO: SelectorExpr Addressing_Type");
+				// Selection sel = lookup_field(p->allocator, type, selector, true);
+				// Entity *e = sel.entity;
+				// GB_ASSERT(e->kind == Entity_Variable);
+				// GB_ASSERT(e->flags & EntityFlag_TypeField);
+				// String name = e->token.string;
+				// if (str_eq(name, str_lit("names"))) {
+				// 	ssaValue *ti_ptr = ir_type_info(p, type);
+
+				// 	ssaValue *names_ptr = NULL;
+
+				// 	if (is_type_enum(type)) {
+				// 		ssaValue *enum_info = ssa_emit_conv(p, ti_ptr, t_type_info_enum_ptr);
+				// 		names_ptr = ssa_emit_ptr_index(p, enum_info, 1);
+				// 	} else if (type->kind == Type_Record) {
+				// 		ssaValue *record_info = ssa_emit_conv(p, ti_ptr, t_type_info_record_ptr);
+				// 		names_ptr = ssa_emit_ptr_index(p, record_info, 1);
+				// 	}
+				// 	return ssa_addr(names_ptr);
+				// } else {
+				// 	GB_PANIC("Unhandled TypeField %.*s", LIT(name));
+				// }
+				GB_PANIC("Unreachable");
+			}
+
+			Selection sel = lookup_field(p->allocator, type, selector, false);
+			GB_ASSERT(sel.entity != NULL);
+
+			ssaValue *a = ssa_build_addr(p, se->expr).addr;
+			a = ssa_emit_deep_field_ptr_index(p, a, sel);
+			return ssa_addr(a);
+		} else {
+			Type *type = base_type(type_of_expr(p->module->info, se->expr));
+			GB_ASSERT(is_type_integer(type));
+			ExactValue val = type_and_value_of_expression(p->module->info, sel)->value;
+			i64 index = val.value_integer;
+
+			Selection sel = lookup_field_from_index(p->allocator, type, index);
+			GB_ASSERT(sel.entity != NULL);
+
+			ssaValue *a = ssa_build_addr(p, se->expr).addr;
+			a = ssa_emit_deep_field_ptr_index(p, a, sel);
+			return ssa_addr(a);
+		}
+	case_end;
+	}
+
+	GB_PANIC("Cannot get entity's address");
+	return ssa_addr(NULL);
+}
+
+
+Type *ssa_proper_type(Type *t) {
+	t = default_type(core_type(t));
+
+	if (t->kind == Type_Basic) {
+		switch (t->Basic.kind) {
+		case Basic_int:
+			if (build_context.word_size == 8) {
+				return t_i64;
+			}
+			return t_i32;
+		case Basic_uint:
+			if (build_context.word_size == 8) {
+				return t_u64;
+			}
+			return t_u32;
+		}
+	}
+
+	return t;
+}
+
+ssaOp ssa_determine_op(TokenKind op, Type *t) {
+	t = ssa_proper_type(t);
+	if (t->kind == Type_Basic) {
+		switch (t->Basic.kind) {
+		case Basic_bool:
+			switch (op) {
+			case Token_And:    return ssaOp_And8;
+			case Token_Or:     return ssaOp_Or8;
+			case Token_Xor:    return ssaOp_Xor8;
+			case Token_AndNot: return ssaOp_AndNot8;
+			}
+			break;
+		case Basic_i8:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add8;
+			case Token_Sub:    return ssaOp_Sub8;
+			case Token_Mul:    return ssaOp_Mul8;
+			case Token_Quo:    return ssaOp_Div8;
+			case Token_Mod:    return ssaOp_Mod8;
+			case Token_And:    return ssaOp_And8;
+			case Token_Or:     return ssaOp_Or8;
+			case Token_Xor:    return ssaOp_Xor8;
+			case Token_AndNot: return ssaOp_AndNot8;
+			case Token_Lt:     return ssaOp_Lt8;
+			case Token_LtEq:   return ssaOp_Le8;
+			case Token_Gt:     return ssaOp_Gt8;
+			case Token_GtEq:   return ssaOp_Ge8;
+			case Token_CmpEq:  return ssaOp_Eq8;
+			case Token_NotEq:  return ssaOp_Ne8;
+			}
+			break;
+		case Basic_u8:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add8;
+			case Token_Sub:    return ssaOp_Sub8;
+			case Token_Mul:    return ssaOp_Mul8;
+			case Token_Quo:    return ssaOp_Div8U;
+			case Token_Mod:    return ssaOp_Mod8U;
+			case Token_And:    return ssaOp_And8;
+			case Token_Or:     return ssaOp_Or8;
+			case Token_Xor:    return ssaOp_Xor8;
+			case Token_AndNot: return ssaOp_AndNot8;
+			case Token_Lt:     return ssaOp_Lt8;
+			case Token_LtEq:   return ssaOp_Le8;
+			case Token_Gt:     return ssaOp_Gt8;
+			case Token_GtEq:   return ssaOp_Ge8;
+			case Token_CmpEq:  return ssaOp_Eq8;
+			case Token_NotEq:  return ssaOp_Ne8;
+			}
+			break;
+		case Basic_i16:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add16;
+			case Token_Sub:    return ssaOp_Sub16;
+			case Token_Mul:    return ssaOp_Mul16;
+			case Token_Quo:    return ssaOp_Div16;
+			case Token_Mod:    return ssaOp_Mod16;
+			case Token_And:    return ssaOp_And16;
+			case Token_Or:     return ssaOp_Or16;
+			case Token_Xor:    return ssaOp_Xor16;
+			case Token_AndNot: return ssaOp_AndNot16;
+			case Token_Lt:     return ssaOp_Lt16;
+			case Token_LtEq:   return ssaOp_Le16;
+			case Token_Gt:     return ssaOp_Gt16;
+			case Token_GtEq:   return ssaOp_Ge16;
+			case Token_CmpEq:  return ssaOp_Eq16;
+			case Token_NotEq:  return ssaOp_Ne16;
+			}
+			break;
+		case Basic_u16:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add16;
+			case Token_Sub:    return ssaOp_Sub16;
+			case Token_Mul:    return ssaOp_Mul16;
+			case Token_Quo:    return ssaOp_Div16U;
+			case Token_Mod:    return ssaOp_Mod16U;
+			case Token_And:    return ssaOp_And16;
+			case Token_Or:     return ssaOp_Or16;
+			case Token_Xor:    return ssaOp_Xor16;
+			case Token_AndNot: return ssaOp_AndNot16;
+			case Token_Lt:     return ssaOp_Lt16;
+			case Token_LtEq:   return ssaOp_Le16;
+			case Token_Gt:     return ssaOp_Gt16;
+			case Token_GtEq:   return ssaOp_Ge16;
+			case Token_CmpEq:  return ssaOp_Eq16;
+			case Token_NotEq:  return ssaOp_Ne16;
+			}
+			break;
+		case Basic_i32:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add32;
+			case Token_Sub:    return ssaOp_Sub32;
+			case Token_Mul:    return ssaOp_Mul32;
+			case Token_Quo:    return ssaOp_Div32;
+			case Token_Mod:    return ssaOp_Mod32;
+			case Token_And:    return ssaOp_And32;
+			case Token_Or:     return ssaOp_Or32;
+			case Token_Xor:    return ssaOp_Xor32;
+			case Token_AndNot: return ssaOp_AndNot32;
+			case Token_Lt:     return ssaOp_Lt32;
+			case Token_LtEq:   return ssaOp_Le32;
+			case Token_Gt:     return ssaOp_Gt32;
+			case Token_GtEq:   return ssaOp_Ge32;
+			case Token_CmpEq:  return ssaOp_Eq32;
+			case Token_NotEq:  return ssaOp_Ne32;
+			}
+			break;
+		case Basic_u32:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add32;
+			case Token_Sub:    return ssaOp_Sub32;
+			case Token_Mul:    return ssaOp_Mul32;
+			case Token_Quo:    return ssaOp_Div32U;
+			case Token_Mod:    return ssaOp_Mod32U;
+			case Token_And:    return ssaOp_And32;
+			case Token_Or:     return ssaOp_Or32;
+			case Token_Xor:    return ssaOp_Xor32;
+			case Token_AndNot: return ssaOp_AndNot32;
+			case Token_Lt:     return ssaOp_Lt32;
+			case Token_LtEq:   return ssaOp_Le32;
+			case Token_Gt:     return ssaOp_Gt32;
+			case Token_GtEq:   return ssaOp_Ge32;
+			case Token_CmpEq:  return ssaOp_Eq32;
+			case Token_NotEq:  return ssaOp_Ne32;
+			}
+			break;
+		case Basic_i64:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add64;
+			case Token_Sub:    return ssaOp_Sub64;
+			case Token_Mul:    return ssaOp_Mul64;
+			case Token_Quo:    return ssaOp_Div64;
+			case Token_Mod:    return ssaOp_Mod64;
+			case Token_And:    return ssaOp_And64;
+			case Token_Or:     return ssaOp_Or64;
+			case Token_Xor:    return ssaOp_Xor64;
+			case Token_AndNot: return ssaOp_AndNot64;
+			case Token_Lt:     return ssaOp_Lt64;
+			case Token_LtEq:   return ssaOp_Le64;
+			case Token_Gt:     return ssaOp_Gt64;
+			case Token_GtEq:   return ssaOp_Ge64;
+			case Token_CmpEq:  return ssaOp_Eq64;
+			case Token_NotEq:  return ssaOp_Ne64;
+			}
+			break;
+		case Basic_u64:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add64;
+			case Token_Sub:    return ssaOp_Sub64;
+			case Token_Mul:    return ssaOp_Mul64;
+			case Token_Quo:    return ssaOp_Div64U;
+			case Token_Mod:    return ssaOp_Mod64U;
+			case Token_And:    return ssaOp_And64;
+			case Token_Or:     return ssaOp_Or64;
+			case Token_Xor:    return ssaOp_Xor64;
+			case Token_AndNot: return ssaOp_AndNot64;
+			case Token_Lt:     return ssaOp_Lt64;
+			case Token_LtEq:   return ssaOp_Le64;
+			case Token_Gt:     return ssaOp_Gt64;
+			case Token_GtEq:   return ssaOp_Ge64;
+			case Token_CmpEq:  return ssaOp_Eq64;
+			case Token_NotEq:  return ssaOp_Ne64;
+			}
+			break;
+		case Basic_f32:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add32F;
+			case Token_Sub:    return ssaOp_Sub32F;
+			case Token_Mul:    return ssaOp_Mul32F;
+			case Token_Quo:    return ssaOp_Div32F;
+			case Token_Lt:     return ssaOp_Lt32F;
+			case Token_LtEq:   return ssaOp_Le32F;
+			case Token_Gt:     return ssaOp_Gt32F;
+			case Token_GtEq:   return ssaOp_Ge32F;
+			case Token_CmpEq:  return ssaOp_Eq32F;
+			case Token_NotEq:  return ssaOp_Ne32F;
+			}
+			break;
+		case Basic_f64:
+			switch (op) {
+			case Token_Add:    return ssaOp_Add64F;
+			case Token_Sub:    return ssaOp_Sub64F;
+			case Token_Mul:    return ssaOp_Mul64F;
+			case Token_Quo:    return ssaOp_Div64F;
+			case Token_Lt:     return ssaOp_Lt64F;
+			case Token_LtEq:   return ssaOp_Le64F;
+			case Token_Gt:     return ssaOp_Gt64F;
+			case Token_GtEq:   return ssaOp_Ge64F;
+			case Token_CmpEq:  return ssaOp_Eq64F;
+			case Token_NotEq:  return ssaOp_Ne64F;
+			}
+			break;
+		}
+	}
+
+	GB_PANIC("Invalid Op for type");
+	return ssaOp_Invalid;
+}
+
+ssaValue *ssa_emit_comp(ssaProc *p, TokenKind op, ssaValue *x, ssaValue *y) {
+	GB_ASSERT(x != NULL && y != NULL);
+	Type *a = core_type(x->type);
+	Type *b = core_type(y->type);
+	if (are_types_identical(a, b)) {
+		// NOTE(bill): No need for a conversion
+	} else if (ssa_is_op_const(x->op)) {
+		x = ssa_emit_conv(p, x, y->type);
+	} else if (ssa_is_op_const(y->op)) {
+		y = ssa_emit_conv(p, y, x->type);
+	}
+
+	Type *result = t_bool;
+	if (is_type_vector(a)) {
+		result = make_type_vector(p->allocator, t_bool, a->Vector.count);
+	}
+
+	if (is_type_vector(a)) {
+		ssa_emit_comment(p, str_lit("vector.comp.begin"));
+		Type *tl = base_type(a);
+		ssaValue *lhs = ssa_address_from_load_or_generate_local(p, x);
+		ssaValue *rhs = ssa_address_from_load_or_generate_local(p, y);
+
+		GB_ASSERT(is_type_vector(result));
+		Type *elem_type = base_type(result)->Vector.elem;
+
+		ssaAddr addr = ssa_add_local_generated(p, result);
+		for (i32 i = 0; i < tl->Vector.count; i++) {
+			ssaValue *index = ssa_const_int(p, t_int, i);
+			ssaValue *x = ssa_emit_load(p, ssa_emit_array_index(p, lhs, index));
+			ssaValue *y = ssa_emit_load(p, ssa_emit_array_index(p, rhs, index));
+			ssaValue *z = ssa_emit_comp(p, op, x, y);
+			ssa_emit_store(p, ssa_emit_array_index(p, addr.addr, index), z);
+		}
+
+		ssa_emit_comment(p, str_lit("vector.comp.end"));
+		return ssa_addr_load(p, addr);
+	}
+
+	return ssa_new_value2(p, ssa_determine_op(op, x->type), x->type, x, y);
+}
+
+
+
+ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) {
+	switch (cond->kind) {
+	case_ast_node(pe, ParenExpr, cond);
+		return ssa_build_cond(p, pe->expr, yes, no);
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, cond);
+		if (ue->op.kind == Token_Not) {
+			return ssa_build_cond(p, ue->expr, no, yes);
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, cond);
+		if (be->op.kind == Token_CmpAnd) {
+			ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmd.and");
+			ssa_build_cond(p, be->left, block, no);
+			ssa_start_block(p, block);
+			return ssa_build_cond(p, be->right, yes, no);
+		} else if (be->op.kind == Token_CmpOr) {
+			ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmp.or");
+			ssa_build_cond(p, be->left, yes, block);
+			ssa_start_block(p, block);
+			return ssa_build_cond(p, be->right, yes, no);
+		}
+	case_end;
+	}
+
+	ssaValue *c = ssa_build_expr(p, cond);
+	ssaBlock *b = ssa_end_block(p);
+	b->kind = ssaBlock_If;
+	ssa_set_control(b, c);
+	ssa_add_edge_to(b, yes);
+	ssa_add_edge_to(b, no);
+	return c;
+}
+
+ssaValue *ssa_emit_logical_binary_expr(ssaProc *p, AstNode *expr) {
+	ast_node(be, BinaryExpr, expr);
+
+	ssaBlock *rhs = ssa_new_block(p, ssaBlock_Plain, "logical.cmp.rhs");
+	ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "logical.cmp.done");
+
+	GB_ASSERT(p->curr_block != NULL);
+
+	Type *type = default_type(type_of_expr(p->module->info, expr));
+
+	bool short_circuit_value = false;
+	if (be->op.kind == Token_CmpAnd) {
+		ssa_build_cond(p, be->left, rhs, done);
+		short_circuit_value = false;
+	} else if (be->op.kind == Token_CmpOr) {
+		ssa_build_cond(p, be->left, done, rhs);
+		short_circuit_value = true;
+	}
+	if (rhs->preds.count == 0) {
+		ssa_start_block(p, done);
+		return ssa_const_bool(p, type, short_circuit_value);
+	}
+
+	if (done->preds.count == 0) {
+		ssa_start_block(p, rhs);
+		return ssa_build_expr(p, be->right);
+	}
+
+	ssa_start_block(p, rhs);
+	ssaValue *short_circuit = ssa_const_bool(p, type, short_circuit_value);
+	ssaValueArgs edges = {0};
+	ssa_init_value_args(&edges, p->allocator);
+	for_array(i, done->preds) {
+		ssa_add_arg(&edges, short_circuit);
+	}
+
+	ssa_add_arg(&edges, ssa_build_expr(p, be->right));
+	ssa_emit_jump(p, done);
+	ssa_start_block(p, done);
+
+	ssaValue *phi = ssa_new_value0(p, ssaOp_Phi, type);
+	phi->args = edges;
+	return phi;
+}
+
+ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
+	expr = unparen_expr(expr);
+
+	TypeAndValue *tv = map_tav_get(&p->module->info->types, hash_pointer(expr));
+	GB_ASSERT_NOT_NULL(tv);
+
+	if (tv->value.kind != ExactValue_Invalid) {
+		Type *t = core_type(tv->type);
+		if (is_type_boolean(t)) {
+			return ssa_const_bool(p, tv->type, tv->value.value_bool);
+		} else if (is_type_string(t)) {
+			GB_ASSERT(tv->value.kind == ExactValue_String);
+			return ssa_const_string(p, tv->type, tv->value.value_string);
+		} else if(is_type_slice(t)) {
+			return ssa_const_slice(p, tv->type, tv->value);
+		} else if (is_type_integer(t)) {
+			GB_ASSERT(tv->value.kind == ExactValue_Integer);
+
+			i64 s = 8*type_size_of(p->allocator, t);
+			switch (s) {
+			case 8:  return ssa_const_i8 (p, tv->type, tv->value.value_integer);
+			case 16: return ssa_const_i16(p, tv->type, tv->value.value_integer);
+			case 32: return ssa_const_i32(p, tv->type, tv->value.value_integer);
+			case 64: return ssa_const_i64(p, tv->type, tv->value.value_integer);
+			default: GB_PANIC("Unknown integer size");
+			}
+		} else if (is_type_float(t)) {
+			GB_ASSERT(tv->value.kind == ExactValue_Float);
+			i64 s = 8*type_size_of(p->allocator, t);
+			switch (s) {
+			case 32: return ssa_const_f32(p, tv->type, tv->value.value_float);
+			case 64: return ssa_const_f64(p, tv->type, tv->value.value_float);
+			default: GB_PANIC("Unknown float size");
+			}
+		}
+		// IMPORTANT TODO(bill): Do constant record/array literals correctly
+		return ssa_const_nil(p, tv->type);
+	}
+
+	if (tv->mode == Addressing_Variable) {
+		return ssa_addr_load(p, ssa_build_addr(p, expr));
+	}
+
+
+	switch (expr->kind) {
+	case_ast_node(bl, BasicLit, expr);
+		GB_PANIC("Non-constant basic literal");
+	case_end;
+
+	case_ast_node(bd, BasicDirective, expr);
+		TokenPos pos = bd->token.pos;
+		GB_PANIC("Non-constant basic literal %.*s(%td:%td) - %.*s", LIT(pos.file), pos.line, pos.column, LIT(bd->name));
+	case_end;
+
+	case_ast_node(i, Ident, expr);
+		Entity *e = *map_entity_get(&p->module->info->uses, hash_pointer(expr));
+		if (e->kind == Entity_Builtin) {
+			Token token = ast_node_token(expr);
+			GB_PANIC("TODO(bill): ssa_build_expr Entity_Builtin `%.*s`\n"
+			         "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name),
+			         LIT(token.pos.file), token.pos.line, token.pos.column);
+			return NULL;
+		} else if (e->kind == Entity_Nil) {
+			GB_PANIC("TODO(bill): nil");
+			return NULL;
+		}
+
+		ssaValue **found = map_ssa_value_get(&p->module->values, hash_pointer(e));
+		if (found) {
+			ssaValue *v = *found;
+			if (v->op == ssaOp_Proc) {
+				return v;
+			}
+
+			ssaAddr addr = ssa_build_addr(p, expr);
+			return ssa_addr_load(p, addr);
+		}
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, expr);
+		switch (ue->op.kind) {
+		case Token_Pointer: {
+			return ssa_build_addr(p, ue->expr).addr;
+		} break;
+
+		case Token_Add:
+			return ssa_build_expr(p, ue->expr);
+
+		case Token_Not: // Boolean not
+			return ssa_new_value1(p, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr));
+		case Token_Xor: { // Bitwise not
+			ssaValue *x = ssa_build_expr(p, ue->expr);
+			isize bits = 8*type_size_of(p->allocator, x->type);
+			switch (bits) {
+			case  8: return ssa_new_value1(p, ssaOp_Not8,  tv->type, x);
+			case 16: return ssa_new_value1(p, ssaOp_Not16, tv->type, x);
+			case 32: return ssa_new_value1(p, ssaOp_Not32, tv->type, x);
+			case 64: return ssa_new_value1(p, ssaOp_Not64, tv->type, x);
+			}
+			GB_PANIC("unknown integer size");
+		} break;
+
+		case Token_Sub: { // 0-x
+			ssaValue *x = ssa_build_expr(p, ue->expr);
+			isize bits = 8*type_size_of(p->allocator, x->type);
+			if (is_type_integer(x->type)) {
+				switch (bits) {
+				case  8: return ssa_new_value1(p, ssaOp_Neg8,  tv->type, x);
+				case 16: return ssa_new_value1(p, ssaOp_Neg16, tv->type, x);
+				case 32: return ssa_new_value1(p, ssaOp_Neg32, tv->type, x);
+				case 64: return ssa_new_value1(p, ssaOp_Neg64, tv->type, x);
+				}
+			} else if (is_type_float(x->type)) {
+				switch (bits) {
+				case 32: return ssa_new_value1(p, ssaOp_Neg32F, tv->type, x);
+				case 64: return ssa_new_value1(p, ssaOp_Neg64F, tv->type, x);
+				}
+			}
+			GB_PANIC("unknown type for -x");
+		} break;
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, expr);
+		Type *type = default_type(tv->type);
+
+		switch (be->op.kind) {
+		case Token_Add:
+		case Token_Sub:
+		case Token_Mul:
+		case Token_Quo:
+		case Token_Mod:
+		case Token_And:
+		case Token_Or:
+		case Token_Xor:
+		case Token_AndNot: {
+			ssaValue *x = ssa_build_expr(p, be->left);
+			ssaValue *y = ssa_build_expr(p, be->right);
+			GB_ASSERT(x != NULL && y != NULL);
+			return ssa_new_value2(p, ssa_determine_op(be->op.kind, x->type), tv->type, x, y);
+		}
+
+		case Token_Shl:
+		case Token_Shr: {
+			GB_PANIC("TODO: shifts");
+			return NULL;
+		}
+
+		case Token_CmpEq:
+		case Token_NotEq:
+		case Token_Lt:
+		case Token_LtEq:
+		case Token_Gt:
+		case Token_GtEq: {
+			ssaValue *x = ssa_build_expr(p, be->left);
+			ssaValue *y = ssa_build_expr(p, be->right);
+			return ssa_emit_comp(p, be->op.kind, x, y);
+		} break;
+
+		case Token_CmpAnd:
+		case Token_CmpOr:
+			return ssa_emit_logical_binary_expr(p, expr);
+
+		default:
+			GB_PANIC("Invalid binary expression");
+			break;
+		}
+	case_end;
+	}
+
+
+	return NULL;
+}
+
+
+
+void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes) {
+	for_array(i, nodes) {
+		ssa_build_stmt(p, nodes.e[i]);
+	}
+}
+
+
+
+void ssa_build_when_stmt(ssaProc *p, AstNodeWhenStmt *ws) {
+	ssaValue *cond = ssa_build_expr(p, ws->cond);
+	GB_ASSERT(is_type_boolean(cond->type));
+
+	GB_ASSERT(cond->exact_value.kind == ExactValue_Bool);
+	if (cond->exact_value.value_bool) {
+		ssa_build_stmt_list(p, ws->body->BlockStmt.stmts);
+	} else if (ws->else_stmt) {
+		switch (ws->else_stmt->kind) {
+		case AstNode_BlockStmt:
+			ssa_build_stmt_list(p, ws->else_stmt->BlockStmt.stmts);
+			break;
+		case AstNode_WhenStmt:
+			ssa_build_when_stmt(p, &ws->else_stmt->WhenStmt);
+			break;
+		default:
+			GB_PANIC("Invalid `else` statement in `when` statement");
+			break;
+		}
+	}
+}
+
+void ssa_build_assign_op(ssaProc *p, ssaAddr lhs, ssaValue *value, TokenKind op) {
+	// ssaValue *old_value = ssa_addr_load(p, lhs);
+	// Type *type = old_value->type;
+
+	// ssaValue *change = value;
+	// if (is_type_pointer(type) && is_type_integer(value->type)) {
+	// 	change = ssa_emit_conv(p, value, default_type(value->type));
+	// } else {
+	// 	change = ssa_emit_conv(p, value, type);
+	// }
+	// ssaValue *new_value = ssa_emit_arith(p, op, old_value, change, type);
+	// ssa_addr_store(p, lhs, new_value);
+}
+
+
+void ssa_build_stmt(ssaProc *p, AstNode *node) {
+	if (p->curr_block == NULL) {
+		ssaBlock *dead_block = ssa_new_block(p, ssaBlock_Plain, "");
+		ssa_start_block(p, dead_block);
+	}
+
+	switch (node->kind) {
+	case_ast_node(es, EmptyStmt, node);
+	case_end;
+
+	case_ast_node(bs, BlockStmt, node);
+		ssa_build_stmt_list(p, bs->stmts);
+	case_end;
+
+	case_ast_node(us, UsingStmt, node);
+		for_array(i, us->list) {
+			AstNode *decl = unparen_expr(us->list.e[i]);
+			if (decl->kind == AstNode_ValueDecl) {
+				ssa_build_stmt(p, decl);
+			}
+		}
+	case_end;
+
+	case_ast_node(ws, WhenStmt, node);
+		ssa_build_when_stmt(p, ws);
+	case_end;
+
+	case_ast_node(s, IncDecStmt, node);
+		TokenKind op = Token_Add;
+		if (s->op.kind == Token_Dec) {
+			op = Token_Sub;
+		}
+		ssaAddr addr = ssa_build_addr(p, s->expr);
+		Type *t = ssa_addr_type(addr);
+		ssa_build_assign_op(p, addr, ssa_const_int(p, t, 1), op);
+	case_end;
+
+	case_ast_node(vd, ValueDecl, node);
+		if (vd->is_var) {
+			ssaModule *m = p->module;
+			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+			if (vd->values.count == 0) {
+				for_array(i, vd->names) {
+					AstNode *name = vd->names.e[i];
+					if (!ssa_is_blank_ident(name)) {
+						ssa_add_local_for_ident(p, name);
+					}
+				}
+			} else {
+				Array(ssaAddr) lvals = {0};
+				ssaValueArray  inits = {0};
+				array_init_reserve(&lvals, m->tmp_allocator, vd->names.count);
+				array_init_reserve(&inits, m->tmp_allocator, vd->names.count);
+
+				for_array(i, vd->names) {
+					AstNode *name = vd->names.e[i];
+					ssaAddr lval = ssa_addr(NULL);
+					if (!ssa_is_blank_ident(name)) {
+						lval = ssa_add_local_for_ident(p, name);
+					}
+
+					array_add(&lvals, lval);
+				}
+
+				for_array(i, vd->values) {
+					ssaValue *init = ssa_build_expr(p, vd->values.e[i]);
+					if (init == NULL) { // TODO(bill): remove this
+						continue;
+					}
+					Type *t = type_deref(init->type);
+					if (init->op == ssaOp_Addr && t->kind == Type_Tuple) {
+						for (isize i = 0; i < t->Tuple.variable_count; i++) {
+							Entity *e = t->Tuple.variables[i];
+							ssaValue *v = ssa_emit_ptr_index(p, init, i);
+							array_add(&inits, v);
+						}
+					} else {
+						array_add(&inits, init);
+					}
+				}
+
+				for_array(i, inits) {
+					ssa_addr_store(p, lvals.e[i], inits.e[i]);
+				}
+			}
+
+			gb_temp_arena_memory_end(tmp);
+		}
+	case_end;
+
+	case_ast_node(as, AssignStmt, node);
+		ssa_emit_comment(p, str_lit("AssignStmt"));
+
+		ssaModule *m = p->module;
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+
+		switch (as->op.kind) {
+		case Token_Eq: {
+			Array(ssaAddr) lvals = {0};
+			array_init(&lvals, m->tmp_allocator);
+
+			for_array(i, as->lhs) {
+				AstNode *lhs = as->lhs.e[i];
+				ssaAddr lval = {0};
+				if (!ssa_is_blank_ident(lhs)) {
+					lval = ssa_build_addr(p, lhs);
+				}
+				array_add(&lvals, lval);
+			}
+
+			if (as->lhs.count == as->rhs.count) {
+				if (as->lhs.count == 1) {
+					AstNode *rhs = as->rhs.e[0];
+					ssaValue *init = ssa_build_expr(p, rhs);
+					ssa_addr_store(p, lvals.e[0], init);
+				} else {
+					ssaValueArray inits;
+					array_init_reserve(&inits, m->tmp_allocator, lvals.count);
+
+					for_array(i, as->rhs) {
+						ssaValue *init = ssa_build_expr(p, as->rhs.e[i]);
+						array_add(&inits, init);
+					}
+
+					for_array(i, inits) {
+						ssa_addr_store(p, lvals.e[i], inits.e[i]);
+					}
+				}
+			} else {
+				ssaValueArray inits;
+				array_init_reserve(&inits, m->tmp_allocator, lvals.count);
+
+				for_array(i, as->rhs) {
+					ssaValue *init = ssa_build_expr(p, as->rhs.e[i]);
+					Type *t = type_deref(init->type);
+					// TODO(bill): refactor for code reuse as this is repeated a bit
+					if (init->op == ssaOp_Addr && t->kind == Type_Tuple) {
+						for (isize i = 0; i < t->Tuple.variable_count; i++) {
+							Entity *e = t->Tuple.variables[i];
+							ssaValue *v = ssa_emit_ptr_index(p, init, i);
+							array_add(&inits, v);
+						}
+					} else {
+						array_add(&inits, init);
+					}
+				}
+
+				for_array(i, inits) {
+					ssa_addr_store(p, lvals.e[i], inits.e[i]);
+				}
+			}
+		} break;
+
+		default: {
+			GB_PANIC("TODO(bill): assign operations");
+			// NOTE(bill): Only 1 += 1 is allowed, no tuples
+			// +=, -=, etc
+			// i32 op = cast(i32)as->op.kind;
+			// op += Token_Add - Token_AddEq; // Convert += to +
+			// ssaAddr lhs = ssa_build_addr(p, as->lhs.e[0]);
+			// ssaValue *value = ssa_build_expr(p, as->rhs.e[0]);
+			// ssa_build_assign_op(p, lhs, value, cast(TokenKind)op);
+		} break;
+		}
+
+		gb_temp_arena_memory_end(tmp);
+	case_end;
+
+	case_ast_node(es, ExprStmt, node);
+		// NOTE(bill): No need to use return value
+		ssa_build_expr(p, es->expr);
+	case_end;
+
+	case_ast_node(ds, DeferStmt, node);
+		GB_PANIC("TODO: DeferStmt");
+	case_end;
+
+	case_ast_node(rs, ReturnStmt, node);
+		GB_PANIC("TODO: ReturnStmt");
+	case_end;
+
+	case_ast_node(is, IfStmt, node);
+		ssa_emit_comment(p, str_lit("IfStmt"));
+		if (is->init != NULL) {
+			ssaBlock *init = ssa_new_block(p, ssaBlock_Plain, "if.init");
+			ssa_emit_jump(p, init);
+			ssa_start_block(p, init);
+			ssa_build_stmt(p, is->init);
+		}
+		ssaBlock *then  = ssa_new_block(p, ssaBlock_Plain, "if.then");
+		ssaBlock *done  = ssa_new_block(p, ssaBlock_Plain, "if.done");
+		ssaBlock *else_ = done;
+		if (is->else_stmt != NULL) {
+			else_ = ssa_new_block(p, ssaBlock_Plain, "if.else");
+		}
+		ssaBlock *b = NULL;
+
+		ssa_build_cond(p, is->cond, then, else_);
+		ssa_start_block(p, then);
+
+		// ssa_open_scope(p);
+		ssa_build_stmt(p, is->body);
+		// ssa_close_scope(p, ssaDeferExit_Default, NULL);
+
+		ssa_emit_jump(p, done);
+
+		if (is->else_stmt != NULL) {
+			ssa_start_block(p, else_);
+
+			// ssa_open_scope(p);
+			ssa_build_stmt(p, is->else_stmt);
+			// ssa_close_scope(p, ssaDeferExit_Default, NULL);
+
+			ssa_emit_jump(p, done);
+		}
+
+		ssa_start_block(p, done);
+	case_end;
+
+
+	case_ast_node(fs, ForStmt, node);
+		ssa_emit_comment(p, str_lit("ForStmt"));
+		if (fs->init != NULL) {
+			ssaBlock *init = ssa_new_block(p, ssaBlock_Plain, "for.init");
+			ssa_emit_jump(p, init);
+			ssa_start_block(p, init);
+			ssa_build_stmt(p, fs->init);
+		}
+
+		ssaBlock *body = ssa_new_block(p, ssaBlock_Plain, "for.body");
+		ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "for.done");
+		ssaBlock *loop = body;
+		if (fs->cond != NULL) {
+			loop = ssa_new_block(p, ssaBlock_Plain, "for.loop");
+		}
+		ssaBlock *post = loop;
+		if (fs->post != NULL) {
+			post = ssa_new_block(p, ssaBlock_Plain, "for.post");
+		}
+
+		ssa_emit_jump(p, loop);
+		ssa_start_block(p, loop);
+
+		if (loop != body) {
+			ssa_build_cond(p, fs->cond, body, done);
+			ssa_start_block(p, body);
+		}
+
+		ssa_push_target_list(p, done, post, NULL);
+		// ssa_open_scope(p);
+		ssa_build_stmt(p, fs->body);
+		// ssa_close_scope(p, ssaDeferExit_Default, NULL);
+		ssa_pop_target_list(p);
+
+		ssa_emit_jump(p, post);
+
+		if (fs->post != NULL) {
+			ssa_start_block(p, post);
+			ssa_build_stmt(p, fs->post);
+			ssa_emit_jump(p, post);
+		}
+
+		ssa_start_block(p, done);
+	case_end;
+
+	case_ast_node(rs, RangeStmt, node);
+		GB_PANIC("TODO: RangeStmt");
+	case_end;
+
+	case_ast_node(rs, MatchStmt, node);
+		GB_PANIC("TODO: MatchStmt");
+	case_end;
+
+	case_ast_node(rs, TypeMatchStmt, node);
+		GB_PANIC("TODO: TypeMatchStmt");
+	case_end;
+
+	case_ast_node(bs, BranchStmt, node);
+		ssaBlock *b = NULL;
+		switch (bs->token.kind) {
+		case Token_break:
+			for (ssaTargetList *t = p->target_list; t != NULL && b == NULL; t = t->prev) {
+				b = t->break_;
+			}
+			break;
+		case Token_continue:
+			for (ssaTargetList *t = p->target_list; t != NULL && b == NULL; t = t->prev) {
+				b = t->continue_;
+			}
+			break;
+		case Token_fallthrough:
+			for (ssaTargetList *t = p->target_list; t != NULL && b == NULL; t = t->prev) {
+				b = t->fallthrough_;
+			}
+			break;
+		}
+		if (b != NULL) {
+			// ssa_emit_defer_stmts(p, irDeferExit_Branch, b);
+		}
+		switch (bs->token.kind) {
+		case Token_break:       ssa_emit_comment(p, str_lit("break"));       break;
+		case Token_continue:    ssa_emit_comment(p, str_lit("continue"));    break;
+		case Token_fallthrough: ssa_emit_comment(p, str_lit("fallthrough")); break;
+		}
+		ssa_emit_jump(p, b);
+	case_end;
+
+	case_ast_node(pa, PushAllocator, node);
+		GB_PANIC("TODO: PushAllocator");
+	case_end;
+	case_ast_node(pc, PushContext, node);
+		GB_PANIC("TODO: PushContext");
+	case_end;
+	}
+}
+
+void ssa_print_value(gbFile *f, ssaValue *v) {
+	if (v == NULL) {
+		gb_fprintf(f, "nil");
+	}
+	gb_fprintf(f, "v%d", v->id);
+}
+
+void ssa_print_exact_value(gbFile *f, ssaValue *v) {
+	Type *t = default_type(v->type);
+	ExactValue ev = v->exact_value;
+	switch (ev.kind) {
+	case ExactValue_Bool:
+		if (ev.value_bool == false) {
+			gb_fprintf(f, " [false]");
+		} else {
+			gb_fprintf(f, " [true]");
+		}
+		break;
+	case ExactValue_Integer:
+		if (is_type_unsigned(t)) {
+			gb_fprintf(f, " [%llu]", ev.value_integer);
+		} else {
+			gb_fprintf(f, " [%lld]", ev.value_integer);
+		}
+		break;
+	case ExactValue_Float:
+		if (is_type_f32(t)) {
+			f32 fp = cast(f32)ev.value_float;
+			u32 x = *cast(u32 *)&fp;
+			gb_fprintf(f, " [0x%x]", x);
+		} else if (is_type_f64(t)) {
+			f64 fp = cast(f64)ev.value_float;
+			u64 x = *cast(u64 *)&fp;
+			gb_fprintf(f, " [0x%llx]", x);
+		} else {
+			GB_PANIC("unhandled integer");
+		}
+		break;
+	case ExactValue_String:
+		gb_fprintf(f, " [%.*s]", LIT(ev.value_string));
+		break;
+	case ExactValue_Pointer:
+		gb_fprintf(f, " [0x%llx]", ev.value_pointer);
+		break;
+	}
+}
+
+
+void ssa_print_reg_value(gbFile *f, ssaValue *v) {
+	gb_fprintf(f, "    ");
+	gb_fprintf(f, "v%d = %.*s", v->id, LIT(ssa_op_strings[v->op]));
+
+	if (v->type != NULL) {
+		gbString type_str = type_to_string(default_type(v->type));
+		gb_fprintf(f, " %s", type_str);
+		gb_string_free(type_str);
+	}
+
+	ssa_print_exact_value(f, v);
+
+	for_array(i, v->args) {
+		gb_fprintf(f, " ");
+		ssa_print_value(f, v->args.e[i]);
+	}
+
+	if (v->comment_string.len > 0) {
+		gb_fprintf(f, " ; %.*s", LIT(v->comment_string));
+	}
+
+	gb_fprintf(f, "\n");
+
+}
+
+void ssa_print_proc(gbFile *f, ssaProc *p) {
+	gbString type_str = type_to_string(p->entity->type);
+	gb_fprintf(f, "%.*s %s\n", LIT(p->name), type_str);
+	gb_string_free(type_str);
+
+	bool *printed = gb_alloc_array(heap_allocator(), bool, p->value_id+1);
+
+	for_array(i, p->blocks) {
+		ssaBlock *b = p->blocks.e[i];
+		gb_fprintf(f, "  b%d:", b->id);
+		if (b->preds.count > 0) {
+			gb_fprintf(f, " <-");
+			for_array(j, b->preds) {
+				ssaBlock *pred = b->preds.e[j].block;
+				gb_fprintf(f, " b%d", pred->id);
+			}
+		}
+		gb_fprintf(f, "\n");
+
+		isize n = 0;
+		for_array(j, b->values) {
+			ssaValue *v = b->values.e[j];
+			if (v->op != ssaOp_Phi) {
+				continue;
+			}
+			ssa_print_reg_value(f, v);
+			printed[v->id] = true;
+			n++;
+		}
+
+		while (n < b->values.count) {
+			isize m = 0;
+			for_array(j, b->values) {
+				ssaValue *v = b->values.e[j];
+				if (printed[v->id]) {
+					continue;
+				}
+				bool skip = false;
+				for_array(k, v->args) {
+					ssaValue *w = v->args.e[k];
+					if (w != NULL && w->block == b && !printed[w->id]) {
+						skip = true;
+						break;
+					}
+				}
+
+				if (skip) {
+					break;
+				}
+
+				ssa_print_reg_value(f, v);
+				printed[v->id] = true;
+				n++;
+			}
+			if (m == n) {
+				gb_fprintf(f, "!!!!DepCycle!!!!\n");
+				for_array(k, b->values) {
+					ssaValue *v = b->values.e[k];
+					if (printed[v->id]) {
+						continue;
+					}
+
+					ssa_print_reg_value(f, v);
+					printed[v->id] = true;
+					n++;
+				}
+			}
+		}
+
+		if (b->kind == ssaBlock_Plain) {
+			GB_ASSERT(b->succs.count == 1);
+			ssaBlock *next = b->succs.e[0].block;
+			gb_fprintf(f, "    ");
+			gb_fprintf(f, "jump b%d", next->id);
+			gb_fprintf(f, "\n");
+		} else if (b->kind == ssaBlock_If) {
+			GB_ASSERT(b->succs.count == 2);
+			ssaBlock *yes = b->succs.e[0].block;
+			ssaBlock *no = b->succs.e[1].block;
+			gb_fprintf(f, "    ");
+			gb_fprintf(f, "branch v%d, b%d, b%d", b->control->id, yes->id, no->id);
+			gb_fprintf(f, "\n");
+		} else if (b->kind == ssaBlock_Exit) {
+			gb_fprintf(f, "    ");
+			gb_fprintf(f, "exit");
+			gb_fprintf(f, "\n");
+		} else if (b->kind == ssaBlock_Ret) {
+			gb_fprintf(f, "    ");
+			gb_fprintf(f, "ret");
+			gb_fprintf(f, "\n");
+		}
+	}
+
+	gb_free(heap_allocator(), printed);
+}
+
+
+void ssa_opt_proc(ssaProc *p) {
+}
+
+void ssa_build_proc(ssaModule *m, ssaProc *p) {
+	p->module = m;
+	m->proc = p;
+
+	if (p->decl_info->proc_lit == NULL ||
+	    p->decl_info->proc_lit->kind != AstNode_ProcLit) {
+		return;
+	}
+
+	ast_node(pl, ProcLit, p->decl_info->proc_lit);
+	if (pl->body == NULL) {
+		return;
+	}
+	p->entry = ssa_new_block(p, ssaBlock_Entry, "entry");
+
+	ssa_start_block(p, p->entry);
+	ssa_build_stmt(p, pl->body);
+
+	p->exit = ssa_new_block(p, ssaBlock_Exit, "exit");
+	ssa_emit_jump(p, p->exit);
+
+	ssa_opt_proc(p);
+
+	ssa_print_proc(gb_file_get_standard(gbFileStandard_Error), p);
+}
+
+
+
+bool ssa_generate(Parser *parser, CheckerInfo *info) {
+	if (global_error_collector.count != 0) {
+		return false;
+	}
+
+	ssaModule m = {0};
+	{ // Init ssaModule
+		m.info = info;
+
+		isize token_count = parser->total_token_count;
+		isize arena_size = 4 * token_count * gb_max3(gb_size_of(ssaValue), gb_size_of(ssaBlock), gb_size_of(ssaProc));
+
+		gb_arena_init_from_allocator(&m.arena,     heap_allocator(), arena_size);
+		gb_arena_init_from_allocator(&m.tmp_arena, heap_allocator(), arena_size);
+		m.tmp_allocator = gb_arena_allocator(&m.tmp_arena);
+		m.allocator     = gb_arena_allocator(&m.arena);
+
+		map_ssa_value_init(&m.values,    heap_allocator());
+		array_init(&m.registers,         heap_allocator());
+		array_init(&m.procs,             heap_allocator());
+		array_init(&m.procs_to_generate, heap_allocator());
+	}
+
+	isize global_variable_max_count = 0;
+	Entity *entry_point = NULL;
+	bool has_dll_main = false;
+	bool has_win_main = false;
+
+	for_array(i, info->entities.entries) {
+		MapDeclInfoEntry *entry = &info->entities.entries.e[i];
+		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
+		String name = e->token.string;
+		if (e->kind == Entity_Variable) {
+			global_variable_max_count++;
+		} else if (e->kind == Entity_Procedure && !e->scope->is_global) {
+			if (e->scope->is_init && str_eq(name, str_lit("main"))) {
+				entry_point = e;
+			}
+			if ((e->Procedure.tags & ProcTag_export) != 0 ||
+			    (e->Procedure.link_name.len > 0) ||
+			    (e->scope->is_file && e->Procedure.link_name.len > 0)) {
+				if (!has_dll_main && str_eq(name, str_lit("DllMain"))) {
+					has_dll_main = true;
+				} else if (!has_win_main && str_eq(name, str_lit("WinMain"))) {
+					has_win_main = true;
+				}
+			}
+		}
+	}
+
+
+	m.entry_point_entity = entry_point;
+	m.min_dep_map = generate_minimum_dependency_map(info, entry_point);
+
+	for_array(i, info->entities.entries) {
+		MapDeclInfoEntry *entry = &info->entities.entries.e[i];
+		Entity *e = cast(Entity *)entry->key.ptr;
+		String name = e->token.string;
+		DeclInfo *decl = entry->value;
+		Scope *scope = e->scope;
+
+		if (!scope->is_file) {
+			continue;
+		}
+
+		if (map_entity_get(&m.min_dep_map, hash_pointer(e)) == NULL) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
+
+		if (!scope->is_global) {
+			if (e->kind == Entity_Procedure && (e->Procedure.tags & ProcTag_export) != 0) {
+			} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
+				// Handle later
+			} else if (scope->is_init && e->kind == Entity_Procedure && str_eq(name, str_lit("main"))) {
+			} else {
+				name = ssa_mangle_name(&m, e->token.pos.file, e);
+			}
+		}
+
+
+		switch (e->kind) {
+		case Entity_TypeName:
+			break;
+
+		case Entity_Variable: {
+
+		} break;
+
+		case Entity_Procedure: {
+			ast_node(pd, ProcLit, decl->proc_lit);
+			String original_name = name;
+			AstNode *body = pd->body;
+			if (e->Procedure.is_foreign) {
+				name = e->token.string; // NOTE(bill): Don't use the mangled name
+			}
+			if (pd->foreign_name.len > 0) {
+				name = pd->foreign_name;
+			} else if (pd->link_name.len > 0) {
+				name = pd->link_name;
+			}
+
+			if (e == entry_point) {
+				ssaProc *p = ssa_new_proc(&m, name, e, decl);
+				ssa_build_proc(&m, p);
+			}
+
+			// ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name);
+			// p->Proc.tags = pd->tags;
+
+			// ssa_module_add_value(m, e, p);
+			// HashKey hash_name = hash_string(name);
+			// if (map_ssa_value_get(&m.members, hash_name) == NULL) {
+				// map_ssa_value_set(&m.members, hash_name, p);
+			// }
+		} break;
+		}
+	}
+
+	return true;
+}
+
+
+
+
+
+
+String ssa_mangle_name(ssaModule *m, String path, Entity *e) {
+	// NOTE(bill): prefix names not in the init scope
+	// TODO(bill): make robust and not just rely on the file's name
+	String name = e->token.string;
+	CheckerInfo *info = m->info;
+	gbAllocator a = m->allocator;
+	AstFile *file = *map_ast_file_get(&info->files, hash_string(path));
+
+	char *str = gb_alloc_array(a, char, path.len+1);
+	gb_memmove(str, path.text, path.len);
+	str[path.len] = 0;
+	for (isize i = 0; i < path.len; i++) {
+		if (str[i] == '\\') {
+			str[i] = '/';
+		}
+	}
+
+	char const *base = gb_path_base_name(str);
+	char const *ext = gb_path_extension(base);
+	isize base_len = ext-1-base;
+
+	isize max_len = base_len + 1 + 10 + 1 + name.len;
+	bool is_overloaded = check_is_entity_overloaded(e);
+	if (is_overloaded) {
+		max_len += 21;
+	}
+
+	u8 *new_name = gb_alloc_array(a, u8, max_len);
+	isize new_name_len = gb_snprintf(
+		cast(char *)new_name, max_len,
+		"%.*s-%u.%.*s",
+		cast(int)base_len, base,
+		file->id,
+		LIT(name));
+	if (is_overloaded) {
+		char *str = cast(char *)new_name + new_name_len-1;
+		isize len = max_len-new_name_len;
+		isize extra = gb_snprintf(str, len, "-%tu", cast(usize)cast(uintptr)e);
+		new_name_len += extra-1;
+	}
+
+	return make_string(new_name, new_name_len-1);
+}

+ 277 - 0
src/ssa_op.c

@@ -0,0 +1,277 @@
+#define SSA_OPS \
+	SSA_OP(Invalid)\
+\
+	SSA_OP(Unknown)\
+\
+	SSA_OP(Comment) /* Does nothing */\
+\
+	SSA_OP(SP)    /* Stack Pointer */\
+	SSA_OP(SB)    /* Stack Base */\
+	SSA_OP(Addr)  /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\
+\
+	SSA_OP(Local)\
+	SSA_OP(Global)\
+	SSA_OP(Proc)\
+\
+	SSA_OP(Load)\
+	SSA_OP(Store)\
+	SSA_OP(Move)\
+	SSA_OP(LoadReg)\
+	SSA_OP(StoreReg)\
+	SSA_OP(Zero) /* Zero initialize */\
+\
+	SSA_OP(ArrayIndex)  /* Index for a fixed array */\
+	SSA_OP(PtrIndex)    /* Index for a struct/tuple/etc */\
+	SSA_OP(PtrOffset)\
+	SSA_OP(ValueIndex) /* Extract for a value from a register */\
+\
+	SSA_OP(Phi)\
+	SSA_OP(Copy)\
+\
+	/* TODO(bill): calling conventions */\
+	SSA_OP(CallOdin)\
+	SSA_OP(CallC)\
+	SSA_OP(CallStd)\
+	SSA_OP(CallFast)\
+\
+	SSA_OP(BoundsCheck)\
+	SSA_OP(SliceBoundsCheck)\
+\
+	/* Built in operations/procedures */\
+	SSA_OP(Bswap16)\
+	SSA_OP(Bswap32)\
+	SSA_OP(Bswap64)\
+\
+	SSA_OP(Assume)\
+	SSA_OP(DebugTrap)\
+	SSA_OP(Trap)\
+	SSA_OP(ReadCycleCounter)\
+\
+\
+	SSA_OP(ConstBool)\
+	SSA_OP(ConstString)\
+	SSA_OP(ConstSlice)\
+	SSA_OP(ConstNil)\
+	SSA_OP(Const8)\
+	SSA_OP(Const16)\
+	SSA_OP(Const32)\
+	SSA_OP(Const64)\
+	SSA_OP(Const32F)\
+	SSA_OP(Const64F)\
+\
+	/* These should be all the operations I could possibly need for the mean time */\
+	SSA_OP(Add8)\
+	SSA_OP(Add16)\
+	SSA_OP(Add32)\
+	SSA_OP(Add64)\
+	SSA_OP(AddPtr)\
+	SSA_OP(Add32F)\
+	SSA_OP(Add64F)\
+	SSA_OP(Sub8)\
+	SSA_OP(Sub16)\
+	SSA_OP(Sub32)\
+	SSA_OP(Sub64)\
+	SSA_OP(SubPtr)\
+	SSA_OP(Sub32F)\
+	SSA_OP(Sub64F)\
+	SSA_OP(Mul8)\
+	SSA_OP(Mul16)\
+	SSA_OP(Mul32)\
+	SSA_OP(Mul64)\
+	SSA_OP(Mul32F)\
+	SSA_OP(Mul64F)\
+	SSA_OP(Div8)\
+	SSA_OP(Div8U)\
+	SSA_OP(Div16)\
+	SSA_OP(Div16U)\
+	SSA_OP(Div32)\
+	SSA_OP(Div32U)\
+	SSA_OP(Div64)\
+	SSA_OP(Div64U)\
+	SSA_OP(Div32F)\
+	SSA_OP(Div64F)\
+	SSA_OP(Mod8)\
+	SSA_OP(Mod8U)\
+	SSA_OP(Mod16)\
+	SSA_OP(Mod16U)\
+	SSA_OP(Mod32)\
+	SSA_OP(Mod32U)\
+	SSA_OP(Mod64)\
+	SSA_OP(Mod64U)\
+\
+	SSA_OP(And8)\
+	SSA_OP(And16)\
+	SSA_OP(And32)\
+	SSA_OP(And64)\
+	SSA_OP(Or8)\
+	SSA_OP(Or16)\
+	SSA_OP(Or32)\
+	SSA_OP(Or64)\
+	SSA_OP(Xor8)\
+	SSA_OP(Xor16)\
+	SSA_OP(Xor32)\
+	SSA_OP(Xor64)\
+	SSA_OP(AndNot8)\
+	SSA_OP(AndNot16)\
+	SSA_OP(AndNot32)\
+	SSA_OP(AndNot64)\
+\
+	SSA_OP(Lsh8x8)\
+	SSA_OP(Lsh8x16)\
+	SSA_OP(Lsh8x32)\
+	SSA_OP(Lsh8x64)\
+	SSA_OP(Lsh16x8)\
+	SSA_OP(Lsh16x16)\
+	SSA_OP(Lsh16x32)\
+	SSA_OP(Lsh16x64)\
+	SSA_OP(Lsh32x8)\
+	SSA_OP(Lsh32x16)\
+	SSA_OP(Lsh32x32)\
+	SSA_OP(Lsh32x64)\
+	SSA_OP(Lsh64x8)\
+	SSA_OP(Lsh64x16)\
+	SSA_OP(Lsh64x32)\
+	SSA_OP(Lsh64x64)\
+	SSA_OP(Rsh8x8)\
+	SSA_OP(Rsh8x16)\
+	SSA_OP(Rsh8x32)\
+	SSA_OP(Rsh8x64)\
+	SSA_OP(Rsh16x8)\
+	SSA_OP(Rsh16x16)\
+	SSA_OP(Rsh16x32)\
+	SSA_OP(Rsh16x64)\
+	SSA_OP(Rsh32x8)\
+	SSA_OP(Rsh32x16)\
+	SSA_OP(Rsh32x32)\
+	SSA_OP(Rsh32x64)\
+	SSA_OP(Rsh64x8)\
+	SSA_OP(Rsh64x16)\
+	SSA_OP(Rsh64x32)\
+	SSA_OP(Rsh64x64)\
+	SSA_OP(Rsh8Ux8)\
+	SSA_OP(Rsh8Ux16)\
+	SSA_OP(Rsh8Ux32)\
+	SSA_OP(Rsh8Ux64)\
+	SSA_OP(Rsh16Ux8)\
+	SSA_OP(Rsh16Ux16)\
+	SSA_OP(Rsh16Ux32)\
+	SSA_OP(Rsh16Ux64)\
+	SSA_OP(Rsh32Ux8)\
+	SSA_OP(Rsh32Ux16)\
+	SSA_OP(Rsh32Ux32)\
+	SSA_OP(Rsh32Ux64)\
+	SSA_OP(Rsh64Ux8)\
+	SSA_OP(Rsh64Ux16)\
+	SSA_OP(Rsh64Ux32)\
+	SSA_OP(Rsh64Ux64)\
+\
+	SSA_OP(Eq8)\
+	SSA_OP(Eq16)\
+	SSA_OP(Eq32)\
+	SSA_OP(Eq64)\
+	SSA_OP(EqPtr)\
+	SSA_OP(Eq32F)\
+	SSA_OP(Eq64F)\
+	SSA_OP(Ne8)\
+	SSA_OP(Ne16)\
+	SSA_OP(Ne32)\
+	SSA_OP(Ne64)\
+	SSA_OP(NePtr)\
+	SSA_OP(Ne32F)\
+	SSA_OP(Ne64F)\
+	SSA_OP(Lt8)\
+	SSA_OP(Lt16)\
+	SSA_OP(Lt32)\
+	SSA_OP(Lt64)\
+	SSA_OP(LtPtr)\
+	SSA_OP(Lt32F)\
+	SSA_OP(Lt64F)\
+	SSA_OP(Gt8)\
+	SSA_OP(Gt16)\
+	SSA_OP(Gt32)\
+	SSA_OP(Gt64)\
+	SSA_OP(GtPtr)\
+	SSA_OP(Gt32F)\
+	SSA_OP(Gt64F)\
+	SSA_OP(Le8)\
+	SSA_OP(Le16)\
+	SSA_OP(Le32)\
+	SSA_OP(Le64)\
+	SSA_OP(LePtr)\
+	SSA_OP(Le32F)\
+	SSA_OP(Le64F)\
+	SSA_OP(Ge8)\
+	SSA_OP(Ge16)\
+	SSA_OP(Ge32)\
+	SSA_OP(Ge64)\
+	SSA_OP(GePtr)\
+	SSA_OP(Ge32F)\
+	SSA_OP(Ge64F)\
+\
+	SSA_OP(NotB)\
+	SSA_OP(EqB)\
+	SSA_OP(NeB)\
+\
+	SSA_OP(Neg8)\
+	SSA_OP(Neg16)\
+	SSA_OP(Neg32)\
+	SSA_OP(Neg64)\
+	SSA_OP(Neg32F)\
+	SSA_OP(Neg64F)\
+\
+	SSA_OP(Not8)\
+	SSA_OP(Not16)\
+	SSA_OP(Not32)\
+	SSA_OP(Not64)\
+\
+	SSA_OP(SignExt8to16)\
+	SSA_OP(SignExt8to32)\
+	SSA_OP(SignExt8to64)\
+	SSA_OP(SignExt16to32)\
+	SSA_OP(SignExt16to64)\
+	SSA_OP(SignExt32to64)\
+	SSA_OP(ZeroExt8to16)\
+	SSA_OP(ZeroExt8to32)\
+	SSA_OP(ZeroExt8to64)\
+	SSA_OP(ZeroExt16to32)\
+	SSA_OP(ZeroExt16to64)\
+	SSA_OP(ZeroExt32to64)\
+	SSA_OP(Trunc16to8)\
+	SSA_OP(Trunc32to8)\
+	SSA_OP(Trunc32to16)\
+	SSA_OP(Trunc64to8)\
+	SSA_OP(Trunc64to16)\
+	SSA_OP(Trunc64to32)\
+\
+	SSA_OP(Cvt32to32F)\
+	SSA_OP(Cvt32to64F)\
+	SSA_OP(Cvt64to32F)\
+	SSA_OP(Cvt64to64F)\
+	SSA_OP(Cvt32Fto32)\
+	SSA_OP(Cvt32Fto64)\
+	SSA_OP(Cvt64Fto32)\
+	SSA_OP(Cvt64Fto64)\
+	SSA_OP(Cvt32Fto64F)\
+	SSA_OP(Cvt64Fto32F)\
+	SSA_OP(Cvt32Uto32F)\
+	SSA_OP(Cvt32Uto64F)\
+	SSA_OP(Cvt32Fto32U)\
+	SSA_OP(Cvt64Fto32U)\
+	SSA_OP(Cvt64Uto32F)\
+	SSA_OP(Cvt64Uto64F)\
+	SSA_OP(Cvt32Fto64U)\
+	SSA_OP(Cvt64Fto64U)\
+
+
+enum ssaOp {
+#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
+	SSA_OPS
+#undef SSA_OP
+};
+typedef enum ssaOp ssaOp;
+
+String const ssa_op_strings[] = {
+#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
+	SSA_OPS
+#undef SSA_OP
+};

+ 18 - 18
src/tokenizer.c

@@ -484,15 +484,9 @@ gb_inline i32 digit_value(Rune r) {
 	return 16; // NOTE(bill): Larger than highest possible
 }
 
-gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) {
-	if (allow_underscore) {
-		while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
-			advance_to_next_rune(t);
-		}
-	} else {
-		while (digit_value(t->curr_rune) < base) {
-			advance_to_next_rune(t);
-		}
+gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
+	while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
+		advance_to_next_rune(t);
 	}
 }
 
@@ -506,7 +500,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
 
 	if (seen_decimal_point) {
 		token.kind = Token_Float;
-		scan_mantissa(t, 10, true);
+		scan_mantissa(t, 10);
 		goto exponent;
 	}
 
@@ -515,31 +509,37 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
 		advance_to_next_rune(t);
 		if (t->curr_rune == 'b') { // Binary
 			advance_to_next_rune(t);
-			scan_mantissa(t, 2, true);
+			scan_mantissa(t, 2);
 			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
 			}
 		} else if (t->curr_rune == 'o') { // Octal
 			advance_to_next_rune(t);
-			scan_mantissa(t, 8, true);
+			scan_mantissa(t, 8);
 			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
 			}
 		} else if (t->curr_rune == 'd') { // Decimal
 			advance_to_next_rune(t);
-			scan_mantissa(t, 10, true);
+			scan_mantissa(t, 10);
+			if (t->curr - prev <= 2) {
+				token.kind = Token_Invalid;
+			}
+		} else if (t->curr_rune == 'z') { // Dozenal
+			advance_to_next_rune(t);
+			scan_mantissa(t, 12);
 			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
 			}
 		} else if (t->curr_rune == 'x') { // Hexadecimal
 			advance_to_next_rune(t);
-			scan_mantissa(t, 16, true);
+			scan_mantissa(t, 16);
 			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
 			}
 		} else {
 			seen_decimal_point = false;
-			scan_mantissa(t, 10, true);
+			scan_mantissa(t, 10);
 
 			if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
 				seen_decimal_point = true;
@@ -551,7 +551,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
 		return token;
 	}
 
-	scan_mantissa(t, 10, true);
+	scan_mantissa(t, 10);
 
 fraction:
 	if (t->curr_rune == '.') {
@@ -564,7 +564,7 @@ fraction:
 			goto end;
 		}
 		token.kind = Token_Float;
-		scan_mantissa(t, 10, true);
+		scan_mantissa(t, 10);
 	}
 
 exponent:
@@ -574,7 +574,7 @@ exponent:
 		if (t->curr_rune == '-' || t->curr_rune == '+') {
 			advance_to_next_rune(t);
 		}
-		scan_mantissa(t, 10, false);
+		scan_mantissa(t, 10);
 	}
 
 end:

+ 31 - 13
src/types.c

@@ -375,6 +375,10 @@ Type *base_enum_type(Type *t) {
 	return t;
 }
 
+Type *core_type(Type *t) {
+	return base_type(base_enum_type(t));
+}
+
 void set_base_type(Type *t, Type *base) {
 	if (t && t->kind == Type_Named) {
 		t->Named.base = base;
@@ -530,28 +534,28 @@ bool is_type_named(Type *t) {
 	return t->kind == Type_Named;
 }
 bool is_type_boolean(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Boolean) != 0;
 	}
 	return false;
 }
 bool is_type_integer(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Integer) != 0;
 	}
 	return false;
 }
 bool is_type_unsigned(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Unsigned) != 0;
 	}
 	return false;
 }
 bool is_type_numeric(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Numeric) != 0;
 	}
@@ -586,7 +590,7 @@ bool is_type_untyped(Type *t) {
 	return false;
 }
 bool is_type_ordered(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	switch (t->kind) {
 	case Type_Basic:
 		return (t->Basic.flags & BasicFlag_Ordered) != 0;
@@ -598,28 +602,28 @@ bool is_type_ordered(Type *t) {
 	return false;
 }
 bool is_type_constant_type(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_ConstantType) != 0;
 	}
 	return false;
 }
 bool is_type_float(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Float) != 0;
 	}
 	return false;
 }
 bool is_type_f32(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return t->Basic.kind == Basic_f32;
 	}
 	return false;
 }
 bool is_type_f64(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		return t->Basic.kind == Basic_f64;
 	}
@@ -737,7 +741,7 @@ bool is_type_untyped_nil(Type *t) {
 
 
 bool is_type_valid_for_keys(Type *t) {
-	t = base_type(base_enum_type(t));
+	t = core_type(t);
 	if (is_type_untyped(t)) {
 		return false;
 	}
@@ -798,7 +802,7 @@ bool is_type_comparable(Type *t) {
 		return true;
 	case Type_Record: {
 		if (is_type_enum(t)) {
-			return is_type_comparable(base_enum_type(t));
+			return is_type_comparable(core_type(t));
 		}
 		return false;
 	} break;
@@ -1032,7 +1036,21 @@ typedef enum ProcTypeOverloadKind {
 } ProcTypeOverloadKind;
 
 
+bool has_encountered_null_proc_type = false;
 ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
+	if(x == NULL && y == NULL) {
+		if(!has_encountered_null_proc_type) {
+			printf("The compiler has encountered a NULL proc type.\n"
+			       "  This is probably not an error in your code, and\n"
+			       "  the compile is probably still successful.\n"
+			       "  This does mean that (at least once), there could be a\n"
+			       "  bad procedure overload in your program, and Odin wouldn't catch it.\n"
+			       "  As far as I know, this is a porting bug, and doesn't occur on mainline Odin.\n"
+			       "  Be careful, and sorry about this bug :(\n");
+			has_encountered_null_proc_type = true;
+		}
+		return ProcOverload_ParamCount;
+	}
  	if (!is_type_proc(x)) return ProcOverload_NotProcedure;
  	if (!is_type_proc(y)) return ProcOverload_NotProcedure;
 	TypeProc px = base_type(x)->Proc;
@@ -1215,7 +1233,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 		// NOTE(bill): Underlying memory address cannot be changed
 		if (str_eq(field_name, count_str)) {
 			// HACK(bill): Memory leak
-			sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Array.count));
+			sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, exact_value_integer(type->Array.count));
 			return sel;
 		}
 	} else if (type->kind == Type_Vector) {
@@ -1223,7 +1241,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 		// NOTE(bill): Vectors are not addressable
 		if (str_eq(field_name, count_str)) {
 			// HACK(bill): Memory leak
-			sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Vector.count));
+			sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, exact_value_integer(type->Vector.count));
 			return sel;
 		}
 

+ 1 - 0
src/unicode.c

@@ -6,6 +6,7 @@
 
 #pragma warning(pop)
 
+
 bool rune_is_letter(Rune r) {
 	if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) ||
 	    r == '_') {

+ 66 - 44
src/utf8proc/utf8proc.c

@@ -166,24 +166,24 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, ut
   if (uc < 0x00) {
     return 0;
   } else if (uc < 0x80) {
-    dst[0] = uc;
+    dst[0] = (utf8proc_uint8_t) uc;
     return 1;
   } else if (uc < 0x800) {
-    dst[0] = 0xC0 + (uc >> 6);
-    dst[1] = 0x80 + (uc & 0x3F);
+    dst[0] = (utf8proc_uint8_t)(0xC0 + (uc >> 6));
+    dst[1] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F));
     return 2;
   // Note: we allow encoding 0xd800-0xdfff here, so as not to change
   // the API, however, these are actually invalid in UTF-8
   } else if (uc < 0x10000) {
-    dst[0] = 0xE0 + (uc >> 12);
-    dst[1] = 0x80 + ((uc >> 6) & 0x3F);
-    dst[2] = 0x80 + (uc & 0x3F);
+    dst[0] = (utf8proc_uint8_t)(0xE0 + (uc >> 12));
+    dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F));
+    dst[2] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F));
     return 3;
   } else if (uc < 0x110000) {
-    dst[0] = 0xF0 + (uc >> 18);
-    dst[1] = 0x80 + ((uc >> 12) & 0x3F);
-    dst[2] = 0x80 + ((uc >> 6) & 0x3F);
-    dst[3] = 0x80 + (uc & 0x3F);
+    dst[0] = (utf8proc_uint8_t)(0xF0 + (uc >> 18));
+    dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 12) & 0x3F));
+    dst[2] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F));
+    dst[3] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F));
     return 4;
   } else return 0;
 }
@@ -193,28 +193,28 @@ static utf8proc_ssize_t unsafe_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t
    if (uc < 0x00) {
       return 0;
    } else if (uc < 0x80) {
-      dst[0] = uc;
+      dst[0] = (utf8proc_uint8_t)uc;
       return 1;
    } else if (uc < 0x800) {
-      dst[0] = 0xC0 + (uc >> 6);
-      dst[1] = 0x80 + (uc & 0x3F);
+      dst[0] = (utf8proc_uint8_t)(0xC0 + (uc >> 6));
+      dst[1] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F));
       return 2;
    } else if (uc == 0xFFFF) {
-       dst[0] = 0xFF;
+       dst[0] = (utf8proc_uint8_t)0xFF;
        return 1;
    } else if (uc == 0xFFFE) {
-       dst[0] = 0xFE;
+       dst[0] = (utf8proc_uint8_t)0xFE;
        return 1;
    } else if (uc < 0x10000) {
-      dst[0] = 0xE0 + (uc >> 12);
-      dst[1] = 0x80 + ((uc >> 6) & 0x3F);
-      dst[2] = 0x80 + (uc & 0x3F);
+      dst[0] = (utf8proc_uint8_t)(0xE0 + (uc >> 12));
+      dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F));
+      dst[2] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F));
       return 3;
    } else if (uc < 0x110000) {
-      dst[0] = 0xF0 + (uc >> 18);
-      dst[1] = 0x80 + ((uc >> 12) & 0x3F);
-      dst[2] = 0x80 + ((uc >> 6) & 0x3F);
-      dst[3] = 0x80 + (uc & 0x3F);
+      dst[0] = (utf8proc_uint8_t)(0xF0 + (uc >> 18));
+      dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 12) & 0x3F));
+      dst[2] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F));
+      dst[3] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F));
       return 4;
    } else return 0;
 }
@@ -383,7 +383,7 @@ UTF8PROC_DLLEXPORT int utf8proc_charwidth(utf8proc_int32_t c) {
 }
 
 UTF8PROC_DLLEXPORT utf8proc_category_t utf8proc_category(utf8proc_int32_t c) {
-  return (utf8proc_category_t)utf8proc_get_property(c)->category;
+  return utf8proc_get_property(c)->category;
 }
 
 UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t c) {
@@ -391,11 +391,9 @@ UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t c) {
   return s[utf8proc_category(c)];
 }
 
-
-
 #define utf8proc_decompose_lump(replacement_uc) \
-  return utf8proc_decompose_char((utf8proc_int32_t)(replacement_uc), dst, bufsize, \
-  (utf8proc_option_t)((utf8proc_int32_t)options & ~UTF8PROC_LUMP), last_boundclass)
+  return utf8proc_decompose_char((replacement_uc), dst, bufsize, \
+  options & ~UTF8PROC_LUMP, last_boundclass)
 
 UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, utf8proc_option_t options, int *last_boundclass) {
   const utf8proc_property_t *property;
@@ -458,12 +456,12 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc,
       category == UTF8PROC_CATEGORY_ME) return 0;
   }
   if (options & UTF8PROC_CASEFOLD) {
-    if ((utf8proc_int16_t)property->casefold_seqindex != UINT16_MAX) {
+    if (property->casefold_seqindex != UINT16_MAX) {
       return seqindex_write_char_decomposed(property->casefold_seqindex, dst, bufsize, options, last_boundclass);
     }
   }
   if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) {
-    if ((utf8proc_int16_t)property->decomp_seqindex != UINT16_MAX &&
+    if (property->decomp_seqindex != UINT16_MAX &&
         (!property->decomp_type || (options & UTF8PROC_COMPAT))) {
       return seqindex_write_char_decomposed(property->decomp_seqindex, dst, bufsize, options, last_boundclass);
     }
@@ -485,6 +483,14 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc,
 UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose(
   const utf8proc_uint8_t *str, utf8proc_ssize_t strlen,
   utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options
+) {
+    return utf8proc_decompose_custom(str, strlen, buffer, bufsize, options, NULL, NULL);
+}
+
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_custom(
+  const utf8proc_uint8_t *str, utf8proc_ssize_t strlen,
+  utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options,
+  utf8proc_custom_func custom_func, void *custom_data
 ) {
   /* strlen will be ignored, if UTF8PROC_NULLTERM is set in options */
   utf8proc_ssize_t wpos = 0;
@@ -511,6 +517,9 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose(
         rpos += utf8proc_iterate(str + rpos, strlen - rpos, &uc);
         if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8;
       }
+      if (custom_func != NULL) {
+        uc = custom_func(uc, custom_data);   /* user-specified custom mapping */
+      }
       decomp_result = utf8proc_decompose_char(
         uc, buffer + wpos, (bufsize > wpos) ? (bufsize - wpos) : 0, options,
         &boundclass
@@ -545,9 +554,8 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose(
   return wpos;
 }
 
-UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options) {
-  /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored
-     ASSERT: 'buffer' has one spare byte of free space at the end! */
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_normalize_utf32(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options) {
+  /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored */
   if (options & (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS | UTF8PROC_STRIPCC)) {
     utf8proc_ssize_t rpos;
     utf8proc_ssize_t wpos = 0;
@@ -621,7 +629,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
           starter_property = unsafe_get_property(*starter);
         }
         if (starter_property->comb_index < 0x8000 &&
-            (utf8proc_int16_t)current_property->comb_index != UINT16_MAX &&
+            current_property->comb_index != UINT16_MAX &&
             current_property->comb_index >= 0x8000) {
           int sidx = starter_property->comb_index;
           int idx = (current_property->comb_index & 0x3FFF) - utf8proc_combinations[sidx];
@@ -655,6 +663,14 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
     }
     length = wpos;
   }
+  return length;
+}
+
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options) {
+  /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored
+     ASSERT: 'buffer' has one spare byte of free space at the end! */
+  length = utf8proc_normalize_utf32(buffer, length, options);
+  if (length < 0) return length;
   {
     utf8proc_ssize_t rpos, wpos = 0;
     utf8proc_int32_t uc;
@@ -676,15 +692,22 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
 
 UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map(
   const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options
+) {
+    return utf8proc_map_custom(str, strlen, dstptr, options, NULL, NULL);
+}
+
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map_custom(
+  const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options,
+  utf8proc_custom_func custom_func, void *custom_data
 ) {
   utf8proc_int32_t *buffer;
   utf8proc_ssize_t result;
   *dstptr = NULL;
-  result = utf8proc_decompose(str, strlen, NULL, 0, options);
+  result = utf8proc_decompose_custom(str, strlen, NULL, 0, options, custom_func, custom_data);
   if (result < 0) return result;
   buffer = (utf8proc_int32_t *) malloc(result * sizeof(utf8proc_int32_t) + 1);
   if (!buffer) return UTF8PROC_ERROR_NOMEM;
-  result = utf8proc_decompose(str, strlen, buffer, result, options);
+  result = utf8proc_decompose_custom(str, strlen, buffer, result, options, custom_func, custom_data);
   if (result < 0) {
     free(buffer);
     return result;
@@ -705,29 +728,28 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map(
 
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str) {
   utf8proc_uint8_t *retval;
-  utf8proc_map(str, 0, &retval, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE |
-    UTF8PROC_DECOMPOSE));
+  utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
+    UTF8PROC_DECOMPOSE);
   return retval;
 }
 
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str) {
   utf8proc_uint8_t *retval;
-  utf8proc_map(str, 0, &retval, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE |
-    UTF8PROC_COMPOSE));
+  utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
+    UTF8PROC_COMPOSE);
   return retval;
 }
 
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str) {
   utf8proc_uint8_t *retval;
-  utf8proc_map(str, 0, &retval, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE |
-    UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT));
+  utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
+    UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT);
   return retval;
 }
 
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str) {
   utf8proc_uint8_t *retval;
-  utf8proc_map(str, 0, &retval, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE |
-    UTF8PROC_COMPOSE | UTF8PROC_COMPAT));
+  utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
+    UTF8PROC_COMPOSE | UTF8PROC_COMPAT);
   return retval;
 }
-

+ 80 - 24
src/utf8proc/utf8proc.h

@@ -71,14 +71,15 @@
 /** The MAJOR version number (increased when backwards API compatibility is broken). */
 #define UTF8PROC_VERSION_MAJOR 2
 /** The MINOR version number (increased when new functionality is added in a backwards-compatible manner). */
-#define UTF8PROC_VERSION_MINOR 0
+#define UTF8PROC_VERSION_MINOR 1
 /** The PATCH version (increased for fixes that do not change the API). */
-#define UTF8PROC_VERSION_PATCH 2
+#define UTF8PROC_VERSION_PATCH 0
 /** @} */
 
 #include <stdlib.h>
-#include <sys/types.h>
-#ifdef _MSC_VER
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+// MSVC prior to 2013 lacked stdbool.h and inttypes.h
 typedef signed char utf8proc_int8_t;
 typedef unsigned char utf8proc_uint8_t;
 typedef short utf8proc_int16_t;
@@ -93,12 +94,18 @@ typedef int utf8proc_ssize_t;
 typedef unsigned int utf8proc_size_t;
 #  endif
 #  ifndef __cplusplus
+// emulate C99 bool
 typedef unsigned char utf8proc_bool;
-// enum {false, true};
+#    ifndef __bool_true_false_are_defined
+#      define false 0
+#      define true 1
+#      define __bool_true_false_are_defined 1
+#    endif
 #  else
 typedef bool utf8proc_bool;
 #  endif
 #else
+#  include <stddef.h>
 #  include <stdbool.h>
 #  include <inttypes.h>
 typedef int8_t utf8proc_int8_t;
@@ -108,22 +115,12 @@ typedef uint16_t utf8proc_uint16_t;
 typedef int32_t utf8proc_int32_t;
 typedef uint32_t utf8proc_uint32_t;
 typedef size_t utf8proc_size_t;
-typedef ssize_t utf8proc_ssize_t;
+typedef ptrdiff_t utf8proc_ssize_t;
 typedef bool utf8proc_bool;
 #endif
 #include <limits.h>
 
-#ifdef _WIN32
-#  ifdef UTF8PROC_EXPORTS
-#    define UTF8PROC_DLLEXPORT __declspec(dllexport)
-#  else
-#    define UTF8PROC_DLLEXPORT /*__declspec(dllimport)*/
-#  endif
-#elif __GNUC__ >= 4
-#  define UTF8PROC_DLLEXPORT __attribute__ ((visibility("default")))
-#else
-#  define UTF8PROC_DLLEXPORT
-#endif
+#define UTF8PROC_DLLEXPORT
 
 #ifdef __cplusplus
 extern "C" {
@@ -134,7 +131,7 @@ extern "C" {
 #endif
 
 #ifndef UINT16_MAX
-#  define UINT16_MAX ~(utf8proc_uint16_t)0
+#  define UINT16_MAX 65535U
 #endif
 
 /**
@@ -373,6 +370,13 @@ typedef enum {
   UTF8PROC_BOUNDCLASS_E_BASE_GAZ         = 18, /**< E_BASE + GLUE_AFTER_ZJW */
 } utf8proc_boundclass_t;
 
+/**
+ * Function pointer type passed to @ref utf8proc_map_custom and
+ * @ref utf8proc_decompose_custom, which is used to specify a user-defined
+ * mapping of codepoints to be applied in conjunction with other mappings.
+ */
+typedef utf8proc_int32_t (*utf8proc_custom_func)(utf8proc_int32_t codepoint, void *data);
+
 /**
  * Array containing the byte lengths of a UTF-8 encoded codepoint based
  * on the first byte.
@@ -480,6 +484,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(
  * `buffer` (which must contain at least `bufsize` entries).  In case of
  * success, the number of codepoints written is returned; in case of an
  * error, a negative error code is returned (@ref utf8proc_errmsg).
+ * See @ref utf8proc_decompose_custom to supply additional transformations.
  *
  * If the number of written codepoints would be bigger than `bufsize`, the
  * required buffer size is returned, while the buffer will be overwritten with
@@ -490,9 +495,47 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose(
   utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options
 );
 
+/**
+ * The same as @ref utf8proc_decompose, but also takes a `custom_func` mapping function
+ * that is called on each codepoint in `str` before any other transformations
+ * (along with a `custom_data` pointer that is passed through to `custom_func`).
+ * The `custom_func` argument is ignored if it is `NULL`.  See also @ref utf8proc_map_custom.
+ */
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_custom(
+  const utf8proc_uint8_t *str, utf8proc_ssize_t strlen,
+  utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options,
+  utf8proc_custom_func custom_func, void *custom_data
+);
+
+/**
+ * Normalizes the sequence of `length` codepoints pointed to by `buffer`
+ * in-place (i.e., the result is also stored in `buffer`).
+ *
+ * @param buffer the (native-endian UTF-32) unicode codepoints to re-encode.
+ * @param length the length (in codepoints) of the buffer.
+ * @param options a bitwise or (`|`) of one or more of the following flags:
+ * - @ref UTF8PROC_NLF2LS  - convert LF, CRLF, CR and NEL into LS
+ * - @ref UTF8PROC_NLF2PS  - convert LF, CRLF, CR and NEL into PS
+ * - @ref UTF8PROC_NLF2LF  - convert LF, CRLF, CR and NEL into LF
+ * - @ref UTF8PROC_STRIPCC - strip or convert all non-affected control characters
+ * - @ref UTF8PROC_COMPOSE - try to combine decomposed codepoints into composite
+ *                           codepoints
+ * - @ref UTF8PROC_STABLE  - prohibit combining characters that would violate
+ *                           the unicode versioning stability
+ *
+ * @return
+ * In case of success, the length (in codepoints) of the normalized UTF-32 string is
+ * returned; otherwise, a negative error code is returned (@ref utf8proc_errmsg).
+ *
+ * @warning The entries of the array pointed to by `str` have to be in the
+ *          range `0x0000` to `0x10FFFF`. Otherwise, the program might crash!
+ */
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_normalize_utf32(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options);
+
 /**
  * Reencodes the sequence of `length` codepoints pointed to by `buffer`
  * UTF-8 data in-place (i.e., the result is also stored in `buffer`).
+ * Can optionally normalize the UTF-32 sequence prior to UTF-8 conversion.
  *
  * @param buffer the (native-endian UTF-32) unicode codepoints to re-encode.
  * @param length the length (in codepoints) of the buffer.
@@ -505,10 +548,12 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose(
  *                           codepoints
  * - @ref UTF8PROC_STABLE  - prohibit combining characters that would violate
  *                           the unicode versioning stability
+ * - @ref UTF8PROC_CHARBOUND - insert 0xFF bytes before each grapheme cluster
  *
  * @return
- * In case of success, the length (in bytes) of the resulting UTF-8 string is
- * returned; otherwise, a negative error code is returned (@ref utf8proc_errmsg).
+ * In case of success, the length (in bytes) of the resulting nul-terminated
+ * UTF-8 string is returned; otherwise, a negative error code is returned
+ * (@ref utf8proc_errmsg).
  *
  * @warning The amount of free space pointed to by `buffer` must
  *          exceed the amount of the input data by one byte, and the
@@ -595,7 +640,8 @@ UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t codepoi
  * in any case the result will be NULL terminated (though it might
  * contain NULL characters with the string if `str` contained NULL
  * characters). Other flags in the `options` field are passed to the
- * functions defined above, and regarded as described.
+ * functions defined above, and regarded as described.  See also
+ * @ref utfproc_map_custom to supply a custom codepoint transformation.
  *
  * In case of success the length of the new string is returned,
  * otherwise a negative error code is returned.
@@ -607,6 +653,17 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map(
   const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options
 );
 
+/**
+ * Like @ref utf8proc_map, but also takes a `custom_func` mapping function
+ * that is called on each codepoint in `str` before any other transformations
+ * (along with a `custom_data` pointer that is passed through to `custom_func`).
+ * The `custom_func` argument is ignored if it is `NULL`.
+ */
+UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map_custom(
+  const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options,
+  utf8proc_custom_func custom_func, void *custom_data
+);
+
 /** @name Unicode normalization
  *
  * Returns a pointer to newly allocated memory of a NFD, NFC, NFKD or NFKC
@@ -619,9 +676,9 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map(
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str);
 /** NFC normalization (@ref UTF8PROC_COMPOSE). */
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str);
-/** NFD normalization (@ref UTF8PROC_DECOMPOSE and @ref UTF8PROC_COMPAT). */
+/** NFKD normalization (@ref UTF8PROC_DECOMPOSE and @ref UTF8PROC_COMPAT). */
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str);
-/** NFD normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT). */
+/** NFKC normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT). */
 UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str);
 /** @} */
 
@@ -630,4 +687,3 @@ UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str);
 #endif
 
 #endif
-

+ 10 - 15
src/utf8proc/utf8proc_data.c

@@ -1,7 +1,4 @@
-#pragma warning(push)
-#pragma warning(disable: 4838)
-
-const utf8proc_uint16_t utf8proc_sequences[] = {
+static const utf8proc_uint16_t utf8proc_sequences[] = {
   97, 98, 99, 100, 101, 102, 103,
   104, 105, 106, 107, 108, 109, 110, 111,
   112, 113, 114, 115, 116, 117, 118, 119,
@@ -1179,7 +1176,7 @@ const utf8proc_uint16_t utf8proc_sequences[] = {
   56603, 55354, 56604, 55354, 56605, 55354, 56606, 55354,
   56607, 55354, 56608, 55354, 56609, };
 
-const utf8proc_uint16_t utf8proc_stage1table[] = {
+static const utf8proc_uint16_t utf8proc_stage1table[] = {
   0, 256, 512, 768, 1024, 1280, 1536,
   1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584,
   3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632,
@@ -1726,7 +1723,7 @@ const utf8proc_uint16_t utf8proc_stage1table[] = {
   18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432,
   38656, };
 
-const utf8proc_uint16_t utf8proc_stage2table[] = {
+static const utf8proc_uint16_t utf8proc_stage2table[] = {
   1, 2, 2, 2, 2, 2, 2,
   2, 2, 3, 4, 3, 5, 6, 2,
   2, 2, 2, 2, 2, 2, 2, 2,
@@ -5899,7 +5896,7 @@ const utf8proc_uint16_t utf8proc_stage2table[] = {
   540, 540, 540, 1180, 0, 0, 0, 0,
   0, 1154, 1154, 1154, 1154, 1154, 1154, 1154,
   1154, 1154, 1154, 0, 0, 0, 0, 1103,
-  1158, 0, 0, 0, 0, 0, 0, 0,
+  1103, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0,
@@ -6593,7 +6590,7 @@ const utf8proc_uint16_t utf8proc_stage2table[] = {
   3984, 3984, 3984, 3984, 3984, 3984, 3984, 0,
   0, };
 
-const utf8proc_property_t utf8proc_properties[] = {
+static const utf8proc_property_t utf8proc_properties[] = {
   {0, 0, 0, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX,  false,false,false,false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL},
   {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL},
@@ -7850,7 +7847,7 @@ const utf8proc_property_t utf8proc_properties[] = {
   {UTF8PROC_CATEGORY_MN, 122, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND},
   {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
-  {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NOBREAK, 1335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
+  {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NOBREAK, 1335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_MN, 216, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND},
   {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
@@ -10478,7 +10475,7 @@ const utf8proc_property_t utf8proc_properties[] = {
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1470, UINT16_MAX, 1470, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1478, UINT16_MAX, 1478, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5132, UINT16_MAX, 5132, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
-  {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1480, UINT16_MAX, 1480, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
+  {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1480, UINT16_MAX, 1480, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5133, UINT16_MAX, 5133, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5134, UINT16_MAX, 5134, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1482, UINT16_MAX, 1482, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
@@ -12168,7 +12165,7 @@ const utf8proc_property_t utf8proc_properties[] = {
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6787, UINT16_MAX, 6787, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6789, UINT16_MAX, 6789, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6791, UINT16_MAX, 6791, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
-  {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6793, UINT16_MAX, 6793, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
+  {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6793, UINT16_MAX, 6793, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6795, UINT16_MAX, 6795, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6797, UINT16_MAX, 6797, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6799, UINT16_MAX, 6799, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
@@ -12204,7 +12201,7 @@ const utf8proc_property_t utf8proc_properties[] = {
   {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9104, UINT16_MAX, 9104, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9106, UINT16_MAX, 9106, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9108, UINT16_MAX, 9108, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
-  {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9110, UINT16_MAX, 9110, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
+  {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9110, UINT16_MAX, 9110, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9112, UINT16_MAX, 9112, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9114, UINT16_MAX, 9114, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
   {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9116, UINT16_MAX, 9116, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER},
@@ -13423,7 +13420,7 @@ const utf8proc_property_t utf8proc_properties[] = {
   {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7975, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER},
 };
 
-const utf8proc_uint16_t utf8proc_combinations[] = {
+static const utf8proc_uint16_t utf8proc_combinations[] = {
   0, 46, 192, 193, 194, 195, 196, 197, 0,
   256, 258, 260, 550, 461, 0, 0, 512,
   514, 0, 0, 0, 0, 0, 0, 0,
@@ -14386,5 +14383,3 @@ const utf8proc_uint16_t utf8proc_combinations[] = {
 72, 75,
   1, 53694, 1, 53696,
 };
-
-#pragma warning(pop)

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