Procházet zdrojové kódy

Merge https://github.com/gingerBill/Odin

Zachary Pierson před 8 roky
rodič
revize
ce0d874efd
18 změnil soubory, kde provedl 1717 přidání a 693 odebrání
  1. 1 1
      build.bat
  2. 4 314
      code/demo.odin
  3. 58 13
      core/_preload.odin
  4. 68 7
      core/fmt.odin
  5. 8 8
      core/math.odin
  6. 115 52
      core/os_windows.odin
  7. 7 20
      core/strconv.odin
  8. 42 88
      core/types.odin
  9. 386 38
      src/check_expr.c
  10. 56 18
      src/checker.c
  11. 6 0
      src/common.c
  12. 318 14
      src/exact_value.c
  13. 358 6
      src/ir.c
  14. 142 38
      src/ir_print.c
  15. 1 1
      src/main.c
  16. 2 0
      src/parser.c
  17. 11 3
      src/tokenizer.c
  18. 134 72
      src/types.c

+ 1 - 1
build.bat

@@ -4,7 +4,7 @@
 set exe_name=odin.exe
 
 :: Debug = 0, Release = 1
-set release_mode=1
+set release_mode=0
 set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
 
 if %release_mode% EQU 0 ( rem Debug

+ 4 - 314
code/demo.odin

@@ -1,320 +1,10 @@
-#import "atomic.odin";
-#import "hash.odin";
-#import "mem.odin";
-#import "opengl.odin";
-#import "strconv.odin";
-#import "sync.odin";
-#import win32 "sys/windows.odin";
-
 #import "fmt.odin";
 #import "os.odin";
 #import "math.odin";
 
-
 main :: proc() {
-when true {
-/*
-	Added:
-		* Unexported entities and fields using an underscore prefix
-			- See `sync.odin` and explain
-
-	Removed:
-	 * Maybe/option types
-	 * Remove `type` keyword and other "reserved" keywords
-	 * ..< 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
-	 * match x in y {} // For type match statements
-	 * Version numbering now starts from 0.1.0 and uses the convention:
-	 	- major.minor.patch
-	 * Core library additions to Windows specific stuff
- */
-
-	{
-		Fruit :: enum {
-			APPLE,
-			BANANA,
-			COCONUT,
-		}
-		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 in i+1..10 {
-				if j == 2 {
-					fmt.println(i, j);
-					continue thing;
-				}
-				if j == 3 {
-					break thing;
-				}
-			}
-		}
-
-		// Works with, `for`, `for in`, `match`, `match in`
-		// NOTE(bill): This solves most of the problems I need `goto` for
-	}
-
-	{
-		t := type_info(int);
-		using Type_Info;
-		match i in t {
-		case Integer, Float:
-			fmt.println("It's a number");
-		}
-
-
-		x: any = 123;
-	#label foo
-		match i in x {
-		case int, f32:
-			fmt.println("It's an int or f32");
-			break foo;
-		}
-	}
-
-	{
-		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);
-		defer free(m);
-
-		m[1.0] = 1278;
-		m[2.0] = 7643;
-		m[3.0] = 564;
-		_, ok := m[3.0];
-		c := m[3.0];
-		assert(ok && c == 564);
-
-		fmt.print("map[");
-		i := 0;
-		for val, key in m {
-			if i > 0 {
-				fmt.print(", ");
-			}
-			fmt.printf("%v=%v", key, val);
-			i += 1;
-		}
-		fmt.println("]");
-	}
-	{
-		m := map[string]u32{
-			"a" = 56,
-			"b" = 13453,
-			"c" = 7654,
-		};
-		defer free(m);
-
-		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
-	}
-
-	{
-		Vector3 :: struct{x, y, z: f32};
-		Quaternion :: struct{x, y, z, w: f32};
-
-		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,
-			},
-		}
-
-		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};
-
-		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");
-		}
-
-		if frog, ok := union_cast(Frog)entity; ok {
-			fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
-		}
-
-		// Panics if not the correct type
-		frog: Frog;
-		frog = union_cast(Frog)entity;
-		frog, _ = union_cast(Frog)entity; // ignore error and force cast
-	}
-}
+	x := 1+2i+3j+4k;
+	y := conj(x);
+	z := x/y;
+	fmt.println(z, abs(z));
 }
-

+ 58 - 13
core/_preload.odin

@@ -2,9 +2,7 @@
 
 #import "os.odin";
 #import "fmt.odin";
-#import "mem.odin";
 #import "utf8.odin";
-#import "hash.odin";
 
 // IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
 // #shared_global_scope due to  the internals of the compiler.
@@ -41,6 +39,8 @@ Type_Info :: union {
 	Named{name: string, base: ^Type_Info},
 	Integer{size: int, signed: bool},
 	Float{size: int},
+	Complex{size: int},
+	Quaternion{size: int},
 	String{},
 	Boolean{},
 	Any{},
@@ -129,8 +129,6 @@ __trap             :: proc()        #foreign __llvm_core "llvm.trap";
 read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
 
 
-
-
 // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
 Allocator_Mode :: enum u8 {
 	ALLOC,
@@ -230,7 +228,7 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
 		return nil;
 	}
 
-	mem.copy(new_memory, old_memory, min(old_size, new_size));;
+	__mem_copy(new_memory, old_memory, min(old_size, new_size));;
 	free(old_memory);
 	return new_memory;
 }
@@ -289,7 +287,7 @@ __string_eq :: proc(a, b: string) -> bool {
 }
 
 __string_cmp :: proc(a, b: string) -> int {
-	return mem.compare(cast([]byte)a, cast([]byte)b);
+	return __mem_compare(a.data, b.data, min(a.count, b.count));
 }
 
 __string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
@@ -299,6 +297,26 @@ __string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <=
 __string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
 
 
+__complex64_eq  :: proc(a, b: complex64)  -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
+__complex64_ne  :: proc(a, b: complex64)  -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
+
+__complex128_eq :: proc(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
+__complex128_ne :: proc(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
+
+
+__quaternion128_eq :: proc(a, b: quaternion128) -> bool #inline {
+	return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
+}
+__quaternion128_ne :: proc(a, b: quaternion128) -> bool #inline {
+	return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
+}
+__quaternion256_eq :: proc(a, b: quaternion256) -> bool #inline {
+	return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
+}
+__quaternion256_ne :: proc(a, b: quaternion256) -> bool #inline {
+	return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
+}
+
 __assert :: proc(file: string, line, column: int, msg: string) #inline {
 	fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
 	            file, line, column, msg);
@@ -380,6 +398,26 @@ __mem_compare :: proc(a, b: ^byte, n: int) -> int {
 	return 0;
 }
 
+__sqrt_f32 :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
+__sqrt_f64 :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
+__abs_complex64 :: proc(x: complex64) -> f32 #inline {
+	r, i := real(x), imag(x);
+	return __sqrt_f32(r*r + i*i);
+}
+__abs_complex128 :: proc(x: complex128) -> f64 #inline {
+	r, i := real(x), imag(x);
+	return __sqrt_f64(r*r + i*i);
+}
+__abs_quaternion128 :: proc(x: quaternion128) -> f32 #inline {
+	r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
+	return __sqrt_f32(r*r + i*i + j*j + k*k);
+}
+__abs_quaternion256 :: proc(x: quaternion256) -> f64 #inline {
+	r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
+	return __sqrt_f64(r*r + i*i + j*j + k*k);
+}
+
+
 
 Raw_Any :: struct #ordered {
 	type_info: ^Type_Info,
@@ -459,7 +497,7 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
 	}
 	data := cast(^byte)array.data;
 	assert(data != nil);
-	mem.copy(data + (elem_size*array.count), items, elem_size * item_count);
+	__mem_copy(data + (elem_size*array.count), items, elem_size * item_count);
 	array.count += item_count;
 	return array.count;
 }
@@ -478,7 +516,7 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in
 	}
 	data := cast(^byte)array.data;
 	assert(data != nil);
-	mem.zero(data + (elem_size*array.count), elem_size);
+	__mem_zero(data + (elem_size*array.count), elem_size);
 	array.count++;
 	return array.count;
 }
@@ -495,7 +533,7 @@ __slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
 	if item_count > 0 {
 		data := cast(^byte)slice.data;
 		assert(data != nil);
-		mem.copy(data + (elem_size*slice.count), items, elem_size * item_count);
+		__mem_copy(data + (elem_size*slice.count), items, elem_size * item_count);
 		slice.count += item_count;
 	}
 	return slice.count;
@@ -505,7 +543,14 @@ __slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
 // Map stuff
 
 __default_hash :: proc(data: []byte) -> u64 {
-	return hash.fnv64a(data);
+	fnv64a :: proc(data: []byte) -> u64 {
+		h: u64 = 0xcbf29ce484222325;
+		for b in data {
+			h = (h ~ cast(u64)b) * 0x100000001b3;
+		}
+		return h;
+	}
+	return fnv64a(data);
 }
 __default_hash_string :: proc(s: string) -> u64 {
 	return __default_hash(cast([]byte)s);
@@ -576,7 +621,7 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
 		e := __dynamic_map_get_entry(new_header, j);
 		e.next = fr.entry_index;
 		ndata := cast(^byte)e;
-		mem.copy(ndata+value_offset, data+value_offset, entry_size-value_offset);
+		__mem_copy(ndata+value_offset, data+value_offset, entry_size-value_offset);
 		if __dynamic_map_full(new_header) {
 			__dynamic_map_grow(new_header);
 		}
@@ -617,7 +662,7 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
 	{
 		data := cast(^byte)__dynamic_map_get_entry(h, index);
 		val := data+value_offset;
-		mem.copy(val, value, entry_size-value_offset);
+		__mem_copy(val, value, entry_size-value_offset);
 	}
 
 	if __dynamic_map_full(h) {
@@ -697,7 +742,7 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
 	if fr.entry_index == m.entries.count-1 {
 		m.entries.count--;
 	}
-	mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size);
+	__mem_copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size);
 	last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
 	if last.entry_prev >= 0 {
 		__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;

+ 68 - 7
core/fmt.odin

@@ -110,6 +110,16 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
 		case 4: write_string(buf, "f32");
 		case 8: write_string(buf, "f64");
 		}
+	case Complex:
+		match info.size {
+		case 8:  write_string(buf, "complex64");
+		case 16: write_string(buf, "complex128");
+		}
+	case Quaternion:
+		match info.size {
+		case 16: write_string(buf, "quaternion128");
+		case 32: write_string(buf, "quaternion");
+		}
 	case String:  write_string(buf, "string");
 	case Boolean: write_string(buf, "bool");
 	case Pointer:
@@ -731,10 +741,12 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 			fmt_value(fi, any{info.base, v.data}, verb);
 		}
 
-	case Boolean: fmt_arg(fi, v, verb);
-	case Float:   fmt_arg(fi, v, verb);
-	case Integer: fmt_arg(fi, v, verb);
-	case String:  fmt_arg(fi, v, verb);
+	case Boolean:    fmt_arg(fi, v, verb);
+	case Integer:    fmt_arg(fi, v, verb);
+	case Float:      fmt_arg(fi, v, verb);
+	case Complex:    fmt_arg(fi, v, verb);
+	case Quaternion: fmt_arg(fi, v, verb);
+	case String:     fmt_arg(fi, v, verb);
 
 	case Pointer:
 		if v.type_info == type_info(^Type_Info) {
@@ -883,6 +895,51 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 	}
 }
 
+fmt_complex :: proc(fi: ^Fmt_Info, c: complex128, bits: int, verb: rune) {
+	match verb {
+	case 'f', 'F', 'v':
+		r := real(c);
+		i := imag(c);
+		fmt_float(fi, r, bits/2, verb);
+		if !fi.plus && i >= 0 {
+			write_rune(fi.buf, '+');
+		}
+		fmt_float(fi, i, bits/2, verb);
+		write_rune(fi.buf, 'i');
+
+	default:
+		fmt_bad_verb(fi, verb);
+		return;
+	}
+}
+
+fmt_quaternion :: proc(fi: ^Fmt_Info, c: quaternion256, bits: int, verb: rune) {
+	match verb {
+	case 'f', 'F', 'v':
+		r := real(c);
+		i := imag(c);
+		j := jmag(c);
+		k := kmag(c);
+		fmt_float(fi, r, bits/4, verb);
+
+		if !fi.plus && i >= 0 { write_rune(fi.buf, '+'); }
+		fmt_float(fi, i, bits/4, verb);
+		write_rune(fi.buf, 'i');
+
+		if !fi.plus && j >= 0 { write_rune(fi.buf, '+'); }
+		fmt_float(fi, j, bits/4, verb);
+		write_rune(fi.buf, 'j');
+
+		if !fi.plus && k >= 0 { write_rune(fi.buf, '+'); }
+		fmt_float(fi, k, bits/4, verb);
+		write_rune(fi.buf, 'k');
+
+	default:
+		fmt_bad_verb(fi, verb);
+		return;
+	}
+}
+
 fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 	if arg.data == nil || arg.type_info == nil {
 		write_string(fi.buf, "<nil>");
@@ -903,9 +960,13 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 	base_arg := arg;
 	base_arg.type_info = type_info_base(base_arg.type_info);
 	match a in base_arg {
-	case bool:    fmt_bool(fi, a, verb);
-	case f32:     fmt_float(fi, cast(f64)a, 32, verb);
-	case f64:     fmt_float(fi, a, 64, verb);
+	case bool:          fmt_bool(fi, a, verb);
+	case f32:           fmt_float(fi, cast(f64)a, 32, verb);
+	case f64:           fmt_float(fi, a, 64, verb);
+	case complex64:     fmt_complex(fi, cast(complex128)a, 64, verb);
+	case complex128:    fmt_complex(fi, a, 128, verb);
+	case quaternion128: fmt_quaternion(fi, cast(quaternion256)a, 128, verb);
+	case quaternion256: fmt_quaternion(fi, a, 256, 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);

+ 8 - 8
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(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
+sin  :: proc(θ: 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(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
+cos  :: proc(θ: 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(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); }
+tan  :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); }
 
 pow  :: proc(x, power: f32) -> f32 #foreign __llvm_core "llvm.pow.f32";
 pow  :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
@@ -43,8 +43,8 @@ pow  :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
 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; }
 
-sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
-sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
+sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
+sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
 
 bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
 bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";

+ 115 - 52
core/os_windows.odin

@@ -1,6 +1,4 @@
-#import w "sys/windows.odin";
-#import "fmt.odin";
-
+#import win32 "sys/windows.odin";
 
 Handle    :: int;
 File_Time :: u64;
@@ -50,6 +48,8 @@ WSAECONNRESET:             Errno : 10054;
 ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
 
 
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments();
 
 
 open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
@@ -59,22 +59,22 @@ open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
 
 	access: u32;
 	match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
-	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;
+	case O_RDONLY: access = win32.FILE_GENERIC_READ;
+	case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
+	case O_RDWR:   access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
 	}
 
 	if mode&O_CREAT != 0 {
-		access |= w.FILE_GENERIC_WRITE;
+		access |= win32.FILE_GENERIC_WRITE;
 	}
 	if mode&O_APPEND != 0 {
-		access &~= w.FILE_GENERIC_WRITE;
-		access |=  w.FILE_APPEND_DATA;
+		access &~= win32.FILE_GENERIC_WRITE;
+		access |=  win32.FILE_APPEND_DATA;
 	}
 
-	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};
+	share_mode := cast(u32)(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
+	sa: ^win32.Security_Attributes = nil;
+	sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1};
 	if mode&O_CLOEXEC == 0 {
 		sa = ^sa_inherit;
 	}
@@ -82,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 = w.CREATE_NEW;
+		create_mode = win32.CREATE_NEW;
 	case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
-		create_mode = w.CREATE_ALWAYS;
+		create_mode = win32.CREATE_ALWAYS;
 	case mode&O_CREAT == O_CREAT:
-		create_mode = w.OPEN_ALWAYS;
+		create_mode = win32.OPEN_ALWAYS;
 	case mode&O_TRUNC == O_TRUNC:
-		create_mode = w.TRUNCATE_EXISTING;
+		create_mode = win32.TRUNCATE_EXISTING;
 	default:
-		create_mode = w.OPEN_EXISTING;
+		create_mode = win32.OPEN_EXISTING;
 	}
 
 	buf: [300]byte;
 	copy(buf[..], cast([]byte)path);
 
-	handle := cast(Handle)w.CreateFileA(^buf[0], access, share_mode, sa, create_mode, w.FILE_ATTRIBUTE_NORMAL, nil);
+	handle := cast(Handle)win32.CreateFileA(^buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil);
 	if handle != INVALID_HANDLE {
 		return handle, ERROR_NONE;
 	}
-	err := w.GetLastError();
+	err := win32.GetLastError();
 	return INVALID_HANDLE, cast(Errno)err;
 }
 
 close :: proc(fd: Handle) {
-	w.CloseHandle(cast(w.Handle)fd);
+	win32.CloseHandle(cast(win32.Handle)fd);
 }
 
 write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	bytes_written: i32;
-	e := w.WriteFile(cast(w.Handle)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
-	if e == w.FALSE {
-		err := w.GetLastError();
+	e := win32.WriteFile(cast(win32.Handle)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
+	if e == win32.FALSE {
+		err := win32.GetLastError();
 		return 0, cast(Errno)err;
 	}
 	return cast(int)bytes_written, ERROR_NONE;
@@ -120,31 +120,30 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 
 read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	bytes_read: i32;
-	e := w.ReadFile(cast(w.Handle)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
-	if e == w.FALSE {
-		err := w.GetLastError();
+	e := win32.ReadFile(cast(win32.Handle)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
+	if e == win32.FALSE {
+		err := win32.GetLastError();
 		return 0, cast(Errno)err;
 	}
 	return cast(int)bytes_read, ERROR_NONE;
 }
 
 seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
-	using w;
 	w: u32;
 	match whence {
-	case 0: w = FILE_BEGIN;
-	case 1: w = FILE_CURRENT;
-	case 2: w = FILE_END;
+	case 0: w = win32.FILE_BEGIN;
+	case 1: w = win32.FILE_CURRENT;
+	case 2: w = win32.FILE_END;
 	}
 	hi := cast(i32)(offset>>32);
 	lo := cast(i32)(offset);
-	ft := GetFileType(cast(Handle)fd);
-	if ft == FILE_TYPE_PIPE {
+	ft := win32.GetFileType(cast(win32.Handle)fd);
+	if ft == win32.FILE_TYPE_PIPE {
 		return 0, ERROR_FILE_IS_PIPE;
 	}
-	dw_ptr := SetFilePointer(cast(Handle)fd, lo, ^hi, w);
-	if dw_ptr == INVALID_SET_FILE_POINTER {
-		err := GetLastError();
+	dw_ptr := win32.SetFilePointer(cast(win32.Handle)fd, lo, ^hi, w);
+	if dw_ptr == win32.INVALID_SET_FILE_POINTER {
+		err := win32.GetLastError();
 		return 0, cast(Errno)err;
 	}
 	return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
@@ -152,14 +151,14 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
 
 
 // NOTE(bill): Uses startup to initialize it
-stdin  := get_std_handle(w.STD_INPUT_HANDLE);
-stdout := get_std_handle(w.STD_OUTPUT_HANDLE);
-stderr := get_std_handle(w.STD_ERROR_HANDLE);
+stdin  := get_std_handle(win32.STD_INPUT_HANDLE);
+stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
+stderr := get_std_handle(win32.STD_ERROR_HANDLE);
 
 
 get_std_handle :: proc(h: int) -> Handle {
-	fd := w.GetStdHandle(cast(i32)h);
-	w.SetHandleInformation(fd, w.HANDLE_FLAG_INHERIT, 0);
+	fd := win32.GetStdHandle(cast(i32)h);
+	win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
 	return cast(Handle)fd;
 }
 
@@ -169,23 +168,23 @@ get_std_handle :: proc(h: int) -> Handle {
 
 
 last_write_time :: proc(fd: Handle) -> File_Time {
-	file_info: w.By_Handle_File_Information;
-	w.GetFileInformationByHandle(cast(w.Handle)fd, ^file_info);
+	file_info: win32.By_Handle_File_Information;
+	win32.GetFileInformationByHandle(cast(win32.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: w.Filetime;
-	data: w.File_Attribute_Data;
+	last_write_time: win32.Filetime;
+	data: win32.File_Attribute_Data;
 	buf: [1024]byte;
 
 	assert(buf.count > name.count);
 
 	copy(buf[..], cast([]byte)name);
 
-	if w.GetFileAttributesExA(^buf[0], w.GetFileExInfoStandard, ^data) != 0 {
+	if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
 		last_write_time = data.last_write_time;
 	}
 
@@ -209,7 +208,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 	defer close(fd);
 
 	length: i64;
-	file_size_ok := w.GetFileSizeEx(cast(w.Handle)fd, ^length) != 0;
+	file_size_ok := win32.GetFileSizeEx(cast(win32.Handle)fd, ^length) != 0;
 	if !file_size_ok {
 		return nil, false;
 	}
@@ -232,7 +231,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 			to_read = MAX;
 		}
 
-		w.ReadFile(cast(w.Handle)fd, ^data[total_read], to_read, ^single_read_length, nil);
+		win32.ReadFile(cast(win32.Handle)fd, ^data[total_read], to_read, ^single_read_length, nil);
 		if single_read_length <= 0 {
 			free(data);
 			return nil, false;
@@ -247,7 +246,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 
 
 heap_alloc :: proc(size: int) -> rawptr {
-	return w.HeapAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, size);
+	return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	if new_size == 0 {
@@ -257,25 +256,89 @@ heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	if ptr == nil {
 		return heap_alloc(new_size);
 	}
-	return w.HeapReAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, ptr, new_size);
+	return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
 }
 heap_free :: proc(ptr: rawptr) {
 	if ptr == nil {
 		return;
 	}
-	w.HeapFree(w.GetProcessHeap(), 0, ptr);
+	win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
 }
 
 
 exit :: proc(code: int) {
-	w.ExitProcess(cast(u32)code);
+	win32.ExitProcess(cast(u32)code);
 }
 
 
 
 current_thread_id :: proc() -> int {
-	return cast(int)w.GetCurrentThreadId();
+	return cast(int)win32.GetCurrentThreadId();
 }
 
 
 
+
+_alloc_command_line_arguments :: proc() -> []string {
+	alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
+		wstr_len := 0;
+		for (wstr+wstr_len)^ != 0 {
+			wstr_len++;
+		}
+		len := 2*wstr_len-1;
+		buf := new_slice(byte, len+1);
+		str := slice_ptr(wstr, wstr_len+1);
+
+
+		i, j := 0, 0;
+		for str[j] != 0 {
+			match {
+			case str[j] < 0x80:
+				if i+1 > len {
+					return "";
+				}
+				buf[i] = cast(byte)str[j]; i++;
+				j++;
+			case str[j] < 0x800:
+				if i+2 > len {
+					return "";
+				}
+				buf[i] = cast(byte)(0xc0 + (str[j]>>6));   i++;
+				buf[i] = cast(byte)(0x80 + (str[j]&0x3f)); i++;
+				j++;
+			case 0xd800 <= str[j] && str[j] < 0xdc00:
+				if i+4 > len {
+					return "";
+				}
+				c := cast(rune)((str[j] - 0xd800) << 10) + cast(rune)((str[j+1]) - 0xdc00) + 0x10000;
+				buf[i] = cast(byte)(0xf0 +  (c >> 18));         i++;
+				buf[i] = cast(byte)(0x80 + ((c >> 12) & 0x3f)); i++;
+				buf[i] = cast(byte)(0x80 + ((c >>  6) & 0x3f)); i++;
+				buf[i] = cast(byte)(0x80 + ((c      ) & 0x3f)); i++;
+				j += 2;
+			case 0xdc00 <= str[j] && str[j] < 0xe000:
+				return "";
+			default:
+				if i+3 > len {
+					return "";
+				}
+				buf[i] = 0xe0 + cast(byte) (str[j] >> 12);         i++;
+				buf[i] = 0x80 + cast(byte)((str[j] >>  6) & 0x3f); i++;
+				buf[i] = 0x80 + cast(byte)((str[j]      ) & 0x3f); i++;
+				j++;
+			}
+		}
+
+		return cast(string)buf[..i];
+	}
+
+	arg_count: i32;
+	arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), ^arg_count);
+	arg_list := new_slice(string, arg_count);
+	for _, i in arg_list {
+		arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
+	}
+	return arg_list;
+}
+
+

+ 7 - 20
core/strconv.odin

@@ -313,29 +313,16 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i
 	neg: bool;
 	u, neg = is_integer_negative(u, is_signed, bit_size);
 
-	if is_pow2(cast(i64)base) {
-		b := cast(u64)base;
-		m := cast(uint)b - 1;
-		for u >= b {
-			i--;
-			a[i] = digits[cast(uint)u & m];
-			u >>= b;
-		}
-		i--;
-		a[i] = digits[cast(uint)u];
-	} else {
-		b := cast(u64)base;
-		for u >= b {
-			i--;
-			q := u / b;
-			a[i] = digits[cast(uint)(u-q*b)];
-			u = q;
-		}
-
+	for b := cast(u64)base; u >= b; {
 		i--;
-		a[i] = digits[cast(uint)u];
+		q := u / b;
+		a[i] = digits[cast(uint)(u-q*b)];
+		u = q;
 	}
 
+	i--;
+	a[i] = digits[cast(uint)u];
+
 	if flags&Int_Flag.PREFIX != 0 {
 		ok := true;
 		match base {

+ 42 - 88
core/types.odin

@@ -1,146 +1,100 @@
 is_signed :: proc(info: ^Type_Info) -> bool {
-	if is_integer(info) {
-		i := union_cast(^Type_Info.Integer)info;
+	info = type_info_base(info);
+	if i, ok := union_cast(^Type_Info.Integer)info; ok {
 		return i.signed;
 	}
-	if is_float(info) {
+	if _, ok := union_cast(^Type_Info.Float)info; ok {
 		return true;
 	}
 	return false;
 }
 is_integer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Integer: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Integer)type_info_base(info);
+	return ok;
 }
 is_float :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Float: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Float)type_info_base(info);
+	return ok;
+}
+is_complex :: proc(info: ^Type_Info) -> bool {
+	if info == nil { return false; }
+	_, ok := union_cast(^Type_Info.Complex)type_info_base(info);
+	return ok;
 }
 is_any :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Any: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Any)type_info_base(info);
+	return ok;
 }
 is_string :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.String: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.String)type_info_base(info);
+	return ok;
 }
 is_boolean :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Boolean: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Boolean)type_info_base(info);
+	return ok;
 }
 is_pointer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Pointer: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Pointer)type_info_base(info);
+	return ok;
 }
 is_procedure :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Procedure: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Procedure)type_info_base(info);
+	return ok;
 }
 is_array :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Array: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Array)type_info_base(info);
+	return ok;
 }
 is_dynamic_array :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Dynamic_Array: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Dynamic_Array)type_info_base(info);
+	return ok;
 }
 is_dynamic_map :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Map: return i.count == 0;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Map)type_info_base(info);
+	return ok;
 }
 is_slice :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Slice: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Slice)type_info_base(info);
+	return ok;
 }
 is_vector :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Vector: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Vector)type_info_base(info);
+	return ok;
 }
 is_tuple :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Tuple: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Tuple)type_info_base(info);
+	return ok;
 }
 is_struct :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Struct: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Struct)type_info_base(info);
+	return ok;
 }
 is_union :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Union: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Union)type_info_base(info);
+	return ok;
 }
 is_raw_union :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Raw_Union: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Raw_Union)type_info_base(info);
+	return ok;
 }
 is_enum :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false; }
-
-	match i in type_info_base(info) {
-	case Type_Info.Enum: return true;
-	}
-	return false;
+	_, ok := union_cast(^Type_Info.Enum)type_info_base(info);
+	return ok;
 }

+ 386 - 38
src/check_expr.c

@@ -1754,19 +1754,69 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
 		if (v.kind != ExactValue_Float) {
 			return false;
 		}
+		if (out_value) *out_value = v;
+
 
 		switch (type->Basic.kind) {
-		// case Basic_f16:
 		case Basic_f32:
 		case Basic_f64:
-		// case Basic_f128:
-			if (out_value) *out_value = v;
 			return true;
 
 		case Basic_UntypedFloat:
 			return true;
 		}
-	} else if (is_type_pointer(type)) {
+	} else if (is_type_complex(type)) {
+		ExactValue v = exact_value_to_complex(in_value);
+		if (v.kind != ExactValue_Complex) {
+			return false;
+		}
+
+		switch (type->Basic.kind) {
+		case Basic_complex64:
+		case Basic_complex128: {
+			ExactValue real = exact_value_real(v);
+			ExactValue imag = exact_value_imag(v);
+			if (real.kind != ExactValue_Invalid &&
+			    imag.kind != ExactValue_Invalid) {
+				if (out_value) *out_value = exact_binary_operator_value(Token_Add, real, exact_value_make_imag(imag));
+				return true;
+			}
+		} break;
+		case Basic_UntypedComplex:
+			return true;
+		}
+
+		return false;
+	} else if (is_type_quaternion(type)) {
+		ExactValue v = exact_value_to_quaternion(in_value);
+		if (v.kind != ExactValue_Quaternion) {
+			return false;
+		}
+
+		switch (type->Basic.kind) {
+		case Basic_quaternion128:
+		case Basic_quaternion256: {
+			ExactValue real = exact_value_real(v);
+			ExactValue imag = exact_value_imag(v);
+			ExactValue jmag = exact_value_jmag(v);
+			ExactValue kmag = exact_value_kmag(v);
+			if (real.kind != ExactValue_Invalid &&
+			    imag.kind != ExactValue_Invalid &&
+			    jmag.kind != ExactValue_Invalid &&
+			    kmag.kind != ExactValue_Invalid) {
+				ExactValue ov = exact_binary_operator_value(Token_Add, real, exact_value_make_imag(imag));
+				ov = exact_binary_operator_value(Token_Add, ov, exact_value_make_jmag(jmag));
+				ov = exact_binary_operator_value(Token_Add, ov, exact_value_make_kmag(kmag));
+				if (out_value) *out_value = ov;
+				return true;
+			}
+		} break;
+		case Basic_UntypedQuaternion:
+			return true;
+		}
+
+		return false;
+	}else if (is_type_pointer(type)) {
 		if (in_value.kind == ExactValue_Pointer) {
 			return true;
 		}
@@ -2190,6 +2240,14 @@ bool check_is_castable_to(Checker *c, Operand *operand, Type *y) {
 		}
 	}
 
+	if (is_type_complex(src) && is_type_complex(dst)) {
+		return true;
+	}
+
+	if (is_type_quaternion(src) && is_type_quaternion(dst)) {
+		return true;
+	}
+
 	// Cast between pointers
 	if (is_type_pointer(src) && is_type_pointer(dst)) {
 		Type *s = base_type(type_deref(src));
@@ -2590,6 +2648,8 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level
 				break;
 			case Basic_UntypedInteger:
 			case Basic_UntypedFloat:
+			case Basic_UntypedComplex:
+			case Basic_UntypedQuaternion:
 			case Basic_UntypedRune:
 				if (!is_type_numeric(target_type)) {
 					operand->mode = Addressing_Invalid;
@@ -3568,6 +3628,279 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		operand->mode = Addressing_Value;
 	} break;
 
+	case BuiltinProc_complex: {
+		// complex :: proc(real, imag: float_type) -> complex_type
+		Operand x = *operand;
+		Operand y = {0};
+
+		// NOTE(bill): Invalid will be the default till fixed
+		operand->type = t_invalid;
+		operand->mode = Addressing_Invalid;
+
+		check_expr(c, &y, ce->args.e[1]);
+		if (y.mode == Addressing_Invalid) {
+			return false;
+		}
+
+		convert_to_typed(c, &x, y.type, 0); if (x.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &y, x.type, 0); if (y.mode == Addressing_Invalid) return false;
+		if (x.mode == Addressing_Constant &&
+		    y.mode == Addressing_Constant) {
+			if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) {
+				x.type = t_untyped_float;
+			}
+			if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) {
+				y.type = t_untyped_float;
+			}
+		}
+
+		if (!are_types_identical(x.type, y.type)) {
+			gbString tx = type_to_string(x.type);
+			gbString ty = type_to_string(y.type);
+			error_node(call, "Mismatched types to `complex`, `%s` vs `%s`", tx, ty);
+			gb_string_free(ty);
+			gb_string_free(tx);
+			return false;
+		}
+
+		if (!is_type_float(x.type)) {
+			gbString s = type_to_string(x.type);
+			error_node(call, "Arguments have type `%s`, expected a floating point", s);
+			gb_string_free(s);
+			return false;
+		}
+
+		if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) {
+			operand->value = exact_binary_operator_value(Token_Add, x.value, y.value);
+			operand->mode = Addressing_Constant;
+		} else {
+			operand->mode = Addressing_Value;
+		}
+
+		BasicKind kind = core_type(x.type)->Basic.kind;
+		switch (kind) {
+		case Basic_f32:          operand->type = t_complex64;       break;
+		case Basic_f64:          operand->type = t_complex128;      break;
+		case Basic_UntypedFloat: operand->type = t_untyped_complex; break;
+		default: GB_PANIC("Invalid type"); break;
+		}
+	} break;
+
+	case BuiltinProc_quaternion: {
+		// quaternion :: proc(real, imag, jmag, kmag: float_type) -> quaternion_type
+		Operand x = *operand;
+		Operand y = {0};
+		Operand z = {0};
+		Operand w = {0};
+
+		GB_PANIC("BuiltinProc_quaternion");
+
+		// NOTE(bill): Invalid will be the default till fixed
+		operand->type = t_invalid;
+		operand->mode = Addressing_Invalid;
+
+		check_expr(c, &y, ce->args.e[1]); if (y.mode == Addressing_Invalid) return false;
+		check_expr(c, &z, ce->args.e[2]); if (z.mode == Addressing_Invalid) return false;
+		check_expr(c, &w, ce->args.e[3]); if (w.mode == Addressing_Invalid) return false;
+
+		convert_to_typed(c, &x, y.type, 0); if (x.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &x, z.type, 0); if (x.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &x, w.type, 0); if (x.mode == Addressing_Invalid) return false;
+
+		convert_to_typed(c, &y, z.type, 0); if (y.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &y, w.type, 0); if (y.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &y, x.type, 0); if (y.mode == Addressing_Invalid) return false;
+
+		convert_to_typed(c, &z, y.type, 0); if (z.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &z, w.type, 0); if (z.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &z, x.type, 0); if (z.mode == Addressing_Invalid) return false;
+
+		convert_to_typed(c, &w, x.type, 0); if (w.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &w, y.type, 0); if (w.mode == Addressing_Invalid) return false;
+		convert_to_typed(c, &w, z.type, 0); if (w.mode == Addressing_Invalid) return false;
+
+		if (x.mode == Addressing_Constant &&
+		    y.mode == Addressing_Constant &&
+		    z.mode == Addressing_Constant &&
+		    w.mode == Addressing_Constant) {
+			if (is_type_numeric(x.type) &&
+			    exact_value_imag(x.value).value_float == 0 &&
+			    exact_value_jmag(x.value).value_float == 0 &&
+			    exact_value_kmag(x.value).value_float == 0) {
+				x.type = t_untyped_float;
+			}
+			if (is_type_numeric(y.type) &&
+			    exact_value_imag(y.value).value_float == 0 &&
+			    exact_value_jmag(y.value).value_float == 0 &&
+			    exact_value_kmag(y.value).value_float == 0) {
+				y.type = t_untyped_float;
+			}
+			if (is_type_numeric(z.type) &&
+			    exact_value_imag(z.value).value_float == 0 &&
+			    exact_value_jmag(z.value).value_float == 0 &&
+			    exact_value_kmag(z.value).value_float == 0) {
+				z.type = t_untyped_float;
+			}
+			if (is_type_numeric(w.type) &&
+			    exact_value_imag(w.value).value_float == 0 &&
+			    exact_value_jmag(w.value).value_float == 0 &&
+			    exact_value_kmag(w.value).value_float == 0) {
+				w.type = t_untyped_float;
+			}
+		}
+
+		if (!are_types_identical(x.type, y.type)) {
+			gbString tx = type_to_string(x.type);
+			gbString ty = type_to_string(y.type);
+			gbString tz = type_to_string(z.type);
+			gbString tw = type_to_string(w.type);
+			error_node(call, "Mismatched types to `complex`, `%s`, `%s` `%s`, `%s`", tx, ty, tz, tw);
+			gb_string_free(tw);
+			gb_string_free(tz);
+			gb_string_free(ty);
+			gb_string_free(tx);
+			return false;
+		}
+
+		if (!is_type_float(x.type)) {
+			gbString s = type_to_string(x.type);
+			error_node(call, "Arguments have type `%s`, expected a floating point", s);
+			gb_string_free(s);
+			return false;
+		}
+
+		if (x.mode == Addressing_Constant &&
+		    y.mode == Addressing_Constant &&
+		    z.mode == Addressing_Constant &&
+		    w.mode == Addressing_Constant) {
+			ExactValue v = exact_binary_operator_value(Token_Add, x.value, y.value);
+			v = exact_binary_operator_value(Token_Add, v, z.value);
+			v = exact_binary_operator_value(Token_Add, v, w.value);
+			operand->value = v;
+			operand->mode = Addressing_Constant;
+		} else {
+			operand->mode = Addressing_Value;
+		}
+
+		BasicKind kind = core_type(x.type)->Basic.kind;
+		switch (kind) {
+		case Basic_complex64:         x.type = t_f32;           break;
+		case Basic_complex128:        x.type = t_f64;           break;
+		case Basic_UntypedComplex:    x.type = t_untyped_float; break;
+		case Basic_quaternion128:     x.type = t_f32;           break;
+		case Basic_quaternion256:     x.type = t_f64;           break;
+		case Basic_UntypedQuaternion: x.type = t_untyped_float; break;
+		default: GB_PANIC("Invalid type"); break;
+		}
+	} break;
+
+	case BuiltinProc_real:
+	case BuiltinProc_imag:
+	case BuiltinProc_jmag:
+	case BuiltinProc_kmag: {
+		// real :: proc(x: type) -> float_type
+		// imag :: proc(x: type) -> float_type
+		// jmag :: proc(x: type) -> float_type
+		// kmag :: proc(x: type) -> float_type
+
+		Operand *x = operand;
+		if (is_type_untyped(x->type)) {
+			if (x->mode == Addressing_Constant) {
+				if (is_type_numeric(x->type)) {
+					if (id == BuiltinProc_jmag ||
+					    id == BuiltinProc_kmag) {
+						x->type = t_untyped_quaternion;
+					} else {
+						x->type = t_untyped_complex;
+					}
+				}
+			} else {
+				if (id == BuiltinProc_jmag ||
+				    id == BuiltinProc_kmag) {
+					convert_to_typed(c, x, t_quaternion256, 0);
+				} else {
+					convert_to_typed(c, x, t_complex128, 0);
+				}
+				if (x->mode == Addressing_Invalid) {
+					return false;
+				}
+			}
+		}
+
+		if (id == BuiltinProc_jmag ||
+		    id == BuiltinProc_kmag) {
+			if (!is_type_quaternion(x->type)) {
+				gbString s = type_to_string(x->type);
+				error_node(call, "Argument has type `%s`, expected a complex type", s);
+				gb_string_free(s);
+				return false;
+			}
+		} else {
+			if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) {
+				gbString s = type_to_string(x->type);
+				error_node(call, "Argument has type `%s`, expected a complex or quaternion type", s);
+				gb_string_free(s);
+				return false;
+			}
+		}
+
+		if (x->mode == Addressing_Constant) {
+			switch (id) {
+			case BuiltinProc_real: x->value = exact_value_real(x->value); break;
+			case BuiltinProc_imag: x->value = exact_value_imag(x->value); break;
+			case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break;
+			case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break;
+			}
+		} else {
+			x->mode = Addressing_Value;
+		}
+
+		BasicKind kind = core_type(x->type)->Basic.kind;
+		switch (kind) {
+		case Basic_complex64:         x->type = t_f32;           break;
+		case Basic_complex128:        x->type = t_f64;           break;
+		case Basic_UntypedComplex:    x->type = t_untyped_float; break;
+		case Basic_quaternion128:     x->type = t_f32;           break;
+		case Basic_quaternion256:     x->type = t_f64;           break;
+		case Basic_UntypedQuaternion: x->type = t_untyped_float; break;
+		default: GB_PANIC("Invalid type"); break;
+		}
+	} break;
+
+	case BuiltinProc_conj: {
+		// conj :: proc(x: type) -> type
+		Operand *x = operand;
+		if (is_type_complex(x->type)) {
+			if (x->mode == Addressing_Constant) {
+				ExactValue v = exact_value_to_complex(x->value);
+				f64 r = v.value_complex.real;
+				f64 i = v.value_complex.imag;
+				x->value = exact_value_complex(r, i);
+				x->mode = Addressing_Constant;
+			} else {
+				x->mode = Addressing_Value;
+			}
+		} else if (is_type_quaternion(x->type)) {
+			if (x->mode == Addressing_Constant) {
+				ExactValue v = exact_value_to_quaternion(x->value);
+				f64 r = v.value_quaternion.real;
+				f64 i = v.value_quaternion.imag;
+				f64 j = v.value_quaternion.jmag;
+				f64 k = v.value_quaternion.kmag;
+				x->value = exact_value_quaternion(r, i, j, k);
+				x->mode = Addressing_Constant;
+			} else {
+				x->mode = Addressing_Value;
+			}
+		} else {
+			gbString s = type_to_string(x->type);
+			error_node(call, "Expected a complex or quaternion, got `%s`", s);
+			gb_string_free(s);
+			return false;
+		}
+
+	} break;
+
 	case BuiltinProc_slice_ptr: {
 		// slice_ptr :: proc(a: ^T, len: int) -> []T
 		// slice_ptr :: proc(a: ^T, len, cap: int) -> []T
@@ -3575,16 +3908,13 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		Type *ptr_type = base_type(operand->type);
 		if (!is_type_pointer(ptr_type)) {
 			gbString type_str = type_to_string(operand->type);
-			error_node(call,
-			      "Expected a pointer to `slice_ptr`, got `%s`",
-			      type_str);
+			error_node(call, "Expected a pointer to `slice_ptr`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
 
 		if (ptr_type == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
+			error_node(call, "`rawptr` cannot have pointer arithmetic");
 			return false;
 		}
 
@@ -3629,11 +3959,11 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	} break;
 
 	case BuiltinProc_min: {
-		// min :: proc(a, b: comparable) -> comparable
+		// min :: proc(a, b: ordered) -> ordered
 		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !(is_type_numeric(type) || is_type_string(type))) {
+		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
 			gbString type_str = type_to_string(operand->type);
-			error_node(call, "Expected a comparable numeric type to `min`, got `%s`", type_str);
+			error_node(call, "Expected a ordered numeric type to `min`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
@@ -3645,10 +3975,10 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		if (b.mode == Addressing_Invalid) {
 			return false;
 		}
-		if (!is_type_comparable(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
+		if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
 			gbString type_str = type_to_string(b.type);
 			error_node(call,
-			      "Expected a comparable numeric type to `min`, got `%s`",
+			      "Expected a ordered numeric type to `min`, got `%s`",
 			      type_str);
 			gb_string_free(type_str);
 			return false;
@@ -3695,12 +4025,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	} break;
 
 	case BuiltinProc_max: {
-		// min :: proc(a, b: comparable) -> comparable
+		// min :: proc(a, b: ordered) -> ordered
 		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !(is_type_numeric(type) || is_type_string(type))) {
+		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
 			gbString type_str = type_to_string(operand->type);
 			error_node(call,
-			      "Expected a comparable numeric or string type to `max`, got `%s`",
+			      "Expected a ordered numeric or string type to `max`, got `%s`",
 			      type_str);
 			gb_string_free(type_str);
 			return false;
@@ -3713,10 +4043,10 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		if (b.mode == Addressing_Invalid) {
 			return false;
 		}
-		if (!is_type_comparable(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
+		if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
 			gbString type_str = type_to_string(b.type);
 			error_node(call,
-			      "Expected a comparable numeric or string type to `max`, got `%s`",
+			      "Expected a ordered numeric or string type to `max`, got `%s`",
 			      type_str);
 			gb_string_free(type_str);
 			return false;
@@ -3764,12 +4094,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 	case BuiltinProc_abs: {
 		// abs :: proc(n: numeric) -> numeric
-		Type *type = base_type(operand->type);
-		if (!is_type_numeric(type)) {
+		if (!is_type_numeric(operand->type) && !is_type_vector(operand->type)) {
 			gbString type_str = type_to_string(operand->type);
-			error_node(call,
-			      "Expected a numeric type to `abs`, got `%s`",
-			      type_str);
+			error_node(call, "Expected a numeric type to `abs`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
@@ -3782,6 +4109,18 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			case ExactValue_Float:
 				operand->value.value_float = gb_abs(operand->value.value_float);
 				break;
+			case ExactValue_Complex: {
+				f64 r = operand->value.value_complex.real;
+				f64 i = operand->value.value_complex.imag;
+				operand->value = exact_value_float(gb_sqrt(r*r + i*i));
+			} break;
+			case ExactValue_Quaternion: {
+				f64 r = operand->value.value_complex.real;
+				f64 i = operand->value.value_complex.imag;
+				f64 j = operand->value.value_complex.imag;
+				f64 k = operand->value.value_complex.imag;
+				operand->value = exact_value_float(gb_sqrt(r*r + i*i + j*j + k*k));
+			} break;
 			default:
 				GB_PANIC("Invalid numeric constant");
 				break;
@@ -3790,17 +4129,20 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			operand->mode = Addressing_Value;
 		}
 
-		operand->type = type;
+		if (is_type_complex(operand->type)) {
+			operand->type = base_complex_elem_type(operand->type);
+		} else if (is_type_quaternion(operand->type)) {
+			operand->type = base_quaternion_elem_type(operand->type);
+		}
+		GB_ASSERT(!is_type_complex(operand->type) && !is_type_quaternion(operand->type));
 	} break;
 
 	case BuiltinProc_clamp: {
-		// clamp :: proc(a, min, max: comparable) -> comparable
+		// clamp :: proc(a, min, max: ordered) -> ordered
 		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !(is_type_numeric(type) || is_type_string(type))) {
+		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
 			gbString type_str = type_to_string(operand->type);
-			error_node(call,
-			      "Expected a comparable numeric or string type to `clamp`, got `%s`",
-			      type_str);
+			error_node(call, "Expected a ordered numeric or string type to `clamp`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
@@ -3815,11 +4157,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		if (y.mode == Addressing_Invalid) {
 			return false;
 		}
-		if (!is_type_comparable(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) {
+		if (!is_type_ordered(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) {
 			gbString type_str = type_to_string(y.type);
-			error_node(call,
-			      "Expected a comparable numeric or string type to `clamp`, got `%s`",
-			      type_str);
+			error_node(call, "Expected a ordered numeric or string type to `clamp`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
@@ -3828,11 +4168,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		if (z.mode == Addressing_Invalid) {
 			return false;
 		}
-		if (!is_type_comparable(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) {
+		if (!is_type_ordered(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) {
 			gbString type_str = type_to_string(z.type);
-			error_node(call,
-			      "Expected a comparable numeric or string type to `clamp`, got `%s`",
-			      type_str);
+			error_node(call, "Expected a ordered numeric or string type to `clamp`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
@@ -4407,6 +4745,16 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 		case Token_Float:   t = t_untyped_float;   break;
 		case Token_String:  t = t_untyped_string;  break;
 		case Token_Rune:    t = t_untyped_rune;    break;
+		case Token_Imag: {
+			String s = bl->string;
+			Rune r = s.text[s.len-1];
+			switch (r) {
+			case 'i': t = t_untyped_complex; break;
+			case 'j': case 'k':
+				t = t_untyped_quaternion;
+				break;
+			}
+		} break;
 		default:            GB_PANIC("Unknown literal"); break;
 		}
 		o->mode  = Addressing_Constant;

+ 56 - 18
src/checker.c

@@ -52,6 +52,14 @@ typedef enum BuiltinProcId {
 
 	BuiltinProc_swizzle,
 
+	BuiltinProc_complex,
+	BuiltinProc_quaternion,
+	BuiltinProc_real,
+	BuiltinProc_imag,
+	BuiltinProc_jmag,
+	BuiltinProc_kmag,
+	BuiltinProc_conj,
+
 	// BuiltinProc_ptr_offset,
 	// BuiltinProc_ptr_sub,
 	BuiltinProc_slice_ptr,
@@ -87,8 +95,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("type_info"),        1, false, Expr_Expr},
 	{STR_LIT("type_info_of_val"), 1, false, Expr_Expr},
 
-	{STR_LIT("compile_assert"),   1, false, Expr_Stmt},
-	{STR_LIT("assert"),           1, false, Expr_Stmt},
+	{STR_LIT("compile_assert"),   1, false, Expr_Expr},
+	{STR_LIT("assert"),           1, false, Expr_Expr},
 	{STR_LIT("panic"),            1, false, Expr_Stmt},
 
 	{STR_LIT("copy"),             2, false, Expr_Expr},
@@ -96,6 +104,14 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 
 	{STR_LIT("swizzle"),          1, true,  Expr_Expr},
 
+	{STR_LIT("complex"),          2, false, Expr_Expr},
+	{STR_LIT("quaternion"),       4, false, Expr_Expr},
+	{STR_LIT("real"),             1, false, Expr_Expr},
+	{STR_LIT("imag"),             1, false, Expr_Expr},
+	{STR_LIT("jmag"),             1, false, Expr_Expr},
+	{STR_LIT("kmag"),             1, false, Expr_Expr},
+	{STR_LIT("conj"),             1, false, Expr_Expr},
+
 	// {STR_LIT("ptr_offset"),       2, false, Expr_Expr},
 	// {STR_LIT("ptr_sub"),          2, false, Expr_Expr},
 	{STR_LIT("slice_ptr"),        2, true,   Expr_Expr},
@@ -940,6 +956,24 @@ void add_type_info_type(Checker *c, Type *t) {
 			add_type_info_type(c, t_type_info_ptr);
 			add_type_info_type(c, t_rawptr);
 			break;
+
+		case Basic_complex64:
+			add_type_info_type(c, t_type_info_float);
+			add_type_info_type(c, t_f32);
+			break;
+		case Basic_complex128:
+			add_type_info_type(c, t_type_info_float);
+			add_type_info_type(c, t_f64);
+			break;
+
+		case Basic_quaternion128:
+			add_type_info_type(c, t_type_info_float);
+			add_type_info_type(c, t_f32);
+			break;
+		case Basic_quaternion256:
+			add_type_info_type(c, t_type_info_float);
+			add_type_info_type(c, t_f64);
+			break;
 		}
 	} break;
 
@@ -1128,31 +1162,35 @@ void init_preload(Checker *c) {
 
 
 
-		if (record->variant_count != 19) {
+		if (record->variant_count != 21) {
 			compiler_error("Invalid `Type_Info` layout");
 		}
 		t_type_info_named         = record->variants[ 1]->type;
 		t_type_info_integer       = record->variants[ 2]->type;
 		t_type_info_float         = record->variants[ 3]->type;
-		t_type_info_string        = record->variants[ 4]->type;
-		t_type_info_boolean       = record->variants[ 5]->type;
-		t_type_info_any           = record->variants[ 6]->type;
-		t_type_info_pointer       = record->variants[ 7]->type;
-		t_type_info_procedure     = record->variants[ 8]->type;
-		t_type_info_array         = record->variants[ 9]->type;
-		t_type_info_dynamic_array = record->variants[10]->type;
-		t_type_info_slice         = record->variants[11]->type;
-		t_type_info_vector        = record->variants[12]->type;
-		t_type_info_tuple         = record->variants[13]->type;
-		t_type_info_struct        = record->variants[14]->type;
-		t_type_info_raw_union     = record->variants[15]->type;
-		t_type_info_union         = record->variants[16]->type;
-		t_type_info_enum          = record->variants[17]->type;
-		t_type_info_map           = record->variants[18]->type;
+		t_type_info_complex       = record->variants[ 4]->type;
+		t_type_info_quaternion    = record->variants[ 5]->type;
+		t_type_info_string        = record->variants[ 6]->type;
+		t_type_info_boolean       = record->variants[ 7]->type;
+		t_type_info_any           = record->variants[ 8]->type;
+		t_type_info_pointer       = record->variants[ 9]->type;
+		t_type_info_procedure     = record->variants[10]->type;
+		t_type_info_array         = record->variants[11]->type;
+		t_type_info_dynamic_array = record->variants[12]->type;
+		t_type_info_slice         = record->variants[13]->type;
+		t_type_info_vector        = record->variants[14]->type;
+		t_type_info_tuple         = record->variants[15]->type;
+		t_type_info_struct        = record->variants[16]->type;
+		t_type_info_raw_union     = record->variants[17]->type;
+		t_type_info_union         = record->variants[18]->type;
+		t_type_info_enum          = record->variants[19]->type;
+		t_type_info_map           = record->variants[20]->type;
 
 		t_type_info_named_ptr         = make_type_pointer(c->allocator, t_type_info_named);
 		t_type_info_integer_ptr       = make_type_pointer(c->allocator, t_type_info_integer);
 		t_type_info_float_ptr         = make_type_pointer(c->allocator, t_type_info_float);
+		t_type_info_complex_ptr       = make_type_pointer(c->allocator, t_type_info_complex);
+		t_type_info_quaternion_ptr   = make_type_pointer(c->allocator, t_type_info_quaternion);
 		t_type_info_string_ptr        = make_type_pointer(c->allocator, t_type_info_string);
 		t_type_info_boolean_ptr       = make_type_pointer(c->allocator, t_type_info_boolean);
 		t_type_info_any_ptr           = make_type_pointer(c->allocator, t_type_info_any);

+ 6 - 0
src/common.c

@@ -2,6 +2,8 @@
 #define GB_IMPLEMENTATION
 #include "gb/gb.h"
 
+#include <math.h>
+
 gbAllocator heap_allocator(void) {
 	return gb_heap_allocator();
 }
@@ -104,6 +106,10 @@ i16 f32_to_f16(f32 value) {
 	}
 }
 
+f64 gb_sqrt(f64 x) {
+	return sqrt(x);
+}
+
 
 
 #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)

+ 318 - 14
src/exact_value.c

@@ -5,6 +5,14 @@
 
 typedef struct AstNode AstNode;
 
+typedef struct Complex128 {
+	f64 real, imag;
+} Complex128;
+
+typedef struct Quaternion256 {
+	f64 real, imag, jmag, kmag;
+} Quaternion256;
+
 typedef enum ExactValueKind {
 	ExactValue_Invalid,
 
@@ -12,6 +20,8 @@ typedef enum ExactValueKind {
 	ExactValue_String,
 	ExactValue_Integer,
 	ExactValue_Float,
+	ExactValue_Complex,
+	ExactValue_Quaternion,
 	ExactValue_Pointer,
 	ExactValue_Compound, // TODO(bill): Is this good enough?
 
@@ -21,12 +31,14 @@ typedef enum ExactValueKind {
 typedef struct ExactValue {
 	ExactValueKind kind;
 	union {
-		bool     value_bool;
-		String   value_string;
-		i64      value_integer; // NOTE(bill): This must be an integer and not a pointer
-		f64      value_float;
-		i64      value_pointer;
-		AstNode *value_compound;
+		bool          value_bool;
+		String        value_string;
+		i64           value_integer; // NOTE(bill): This must be an integer and not a pointer
+		f64           value_float;
+		i64           value_pointer;
+		Complex128    value_complex;
+		Quaternion256 value_quaternion;
+		AstNode *     value_compound;
 	};
 } ExactValue;
 
@@ -66,6 +78,23 @@ ExactValue exact_value_float(f64 f) {
 	return result;
 }
 
+ExactValue exact_value_complex(f64 real, f64 imag) {
+	ExactValue result = {ExactValue_Complex};
+	result.value_complex.real = real;
+	result.value_complex.imag = imag;
+	return result;
+}
+
+ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
+	ExactValue result = {ExactValue_Quaternion};
+	result.value_quaternion.real = real;
+	result.value_quaternion.imag = imag;
+	result.value_quaternion.jmag = jmag;
+	result.value_quaternion.kmag = kmag;
+	return result;
+}
+
+
 ExactValue exact_value_pointer(i64 ptr) {
 	ExactValue result = {ExactValue_Pointer};
 	result.value_pointer = ptr;
@@ -113,9 +142,7 @@ ExactValue exact_value_integer_from_string(String string) {
 	return exact_value_integer(result);
 }
 
-
-
-ExactValue exact_value_float_from_string(String string) {
+f64 float_from_string(String string) {
 	isize i = 0;
 	u8 *str = string.text;
 	isize len = string.len;
@@ -190,8 +217,11 @@ ExactValue exact_value_float_from_string(String string) {
 		while (exp >   0) { scale *= 10.0; exp -=  1; }
 	}
 
-	f64 result = sign * (frac ? (value / scale) : (value * scale));
-	return exact_value_float(result);
+	return sign * (frac ? (value / scale) : (value * scale));
+}
+
+ExactValue exact_value_float_from_string(String string) {
+	return exact_value_float(float_from_string(string));
 }
 
 
@@ -200,6 +230,18 @@ ExactValue exact_value_from_basic_literal(Token token) {
 	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_Imag: {
+		String str = token.string;
+		Rune last_rune = cast(Rune)str.text[str.len-1];
+		str.len--; // Ignore the `i|j|k`
+		f64 imag = float_from_string(str);
+
+		switch (last_rune) {
+		case 'i': return exact_value_complex(0, imag);
+		case 'j': return exact_value_quaternion(0, 0, imag, 0);
+		case 'k': return exact_value_quaternion(0, 0, 0, imag);
+		}
+	}
 	case Token_Rune: {
 		Rune r = GB_RUNE_INVALID;
 		gb_utf8_decode(token.string.text, token.string.len, &r);
@@ -245,6 +287,128 @@ ExactValue exact_value_to_float(ExactValue v) {
 	return r;
 }
 
+ExactValue exact_value_to_complex(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+		return exact_value_complex(cast(i64)v.value_integer, 0);
+	case ExactValue_Float:
+		return exact_value_complex(v.value_float, 0);
+	case ExactValue_Complex:
+		return v;
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+
+
+ExactValue exact_value_to_quaternion(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+		return exact_value_quaternion(cast(i64)v.value_integer, 0, 0, 0);
+	case ExactValue_Float:
+		return exact_value_quaternion(v.value_float, 0, 0, 0);
+	case ExactValue_Complex:
+		return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0);
+	case ExactValue_Quaternion:
+		return v;
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+
+
+ExactValue exact_value_real(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+	case ExactValue_Float:
+		return v;
+	case ExactValue_Complex:
+		return exact_value_float(v.value_complex.real);
+	case ExactValue_Quaternion:
+		return exact_value_float(v.value_quaternion.real);
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+
+ExactValue exact_value_imag(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+	case ExactValue_Float:
+		return exact_value_integer(0);
+	case ExactValue_Complex:
+		return exact_value_float(v.value_complex.imag);
+	case ExactValue_Quaternion:
+		return exact_value_float(v.value_quaternion.imag);
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+
+ExactValue exact_value_jmag(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+	case ExactValue_Float:
+	case ExactValue_Complex:
+		return exact_value_integer(0);
+	case ExactValue_Quaternion:
+		return exact_value_float(v.value_quaternion.jmag);
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+ExactValue exact_value_kmag(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+	case ExactValue_Float:
+	case ExactValue_Complex:
+		return exact_value_integer(0);
+	case ExactValue_Quaternion:
+		return exact_value_float(v.value_quaternion.kmag);
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+
+
+ExactValue exact_value_make_imag(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+		return exact_value_complex(0, exact_value_to_float(v).value_float);
+	case ExactValue_Float:
+		return exact_value_complex(0, v.value_float);
+	default:
+		GB_PANIC("Expected an integer or float type for `exact_value_make_imag`");
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+ExactValue exact_value_make_jmag(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+		return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0);
+	case ExactValue_Float:
+		return exact_value_quaternion(0, 0, v.value_float, 0);
+	default:
+		GB_PANIC("Expected an integer or float type for `exact_value_make_jmag`");
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+ExactValue exact_value_make_kmag(ExactValue v) {
+	switch (v.kind) {
+	case ExactValue_Integer:
+		return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float);
+	case ExactValue_Float:
+		return exact_value_quaternion(0, 0, 0, v.value_float);
+	default:
+		GB_PANIC("Expected an integer or float type for `exact_value_make_kmag`");
+	}
+	ExactValue r = {ExactValue_Invalid};
+	return r;
+}
+
+
 
 ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
 	switch (op) {
@@ -253,6 +417,8 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
 		case ExactValue_Invalid:
 		case ExactValue_Integer:
 		case ExactValue_Float:
+		case ExactValue_Complex:
+		case ExactValue_Quaternion:
 			return v;
 		}
 	} break;
@@ -271,6 +437,18 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
 			i.value_float = -i.value_float;
 			return i;
 		}
+		case ExactValue_Complex: {
+			f64 real = v.value_complex.real;
+			f64 imag = v.value_complex.imag;
+			return exact_value_complex(-real, -imag);
+		}
+		case ExactValue_Quaternion: {
+			f64 real = v.value_quaternion.real;
+			f64 imag = v.value_quaternion.imag;
+			f64 jmag = v.value_quaternion.jmag;
+			f64 kmag = v.value_quaternion.kmag;
+			return exact_value_quaternion(-real, -imag, -jmag, -kmag);
+		}
 		}
 	} break;
 
@@ -324,8 +502,12 @@ i32 exact_value_order(ExactValue v) {
 		return 2;
 	case ExactValue_Float:
 		return 3;
-	case ExactValue_Pointer:
+	case ExactValue_Complex:
 		return 4;
+	case ExactValue_Quaternion:
+		return 5;
+	case ExactValue_Pointer:
+		return 6;
 
 	default:
 		GB_PANIC("How'd you get here? Invalid Value.kind");
@@ -346,6 +528,8 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
 
 	case ExactValue_Bool:
 	case ExactValue_String:
+	case ExactValue_Complex:
+	case ExactValue_Quaternion:
 		return;
 
 	case ExactValue_Integer:
@@ -356,16 +540,30 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
 			// TODO(bill): Is this good enough?
 			*x = exact_value_float(cast(f64)x->value_integer);
 			return;
+		case ExactValue_Complex:
+			*x = exact_value_complex(cast(f64)x->value_integer, 0);
+			return;
+		case ExactValue_Quaternion:
+			*x = exact_value_quaternion(cast(f64)x->value_integer, 0, 0, 0);
+			return;
 		}
 		break;
 
 	case ExactValue_Float:
-		if (y->kind == ExactValue_Float)
+		switch (y->kind) {
+		case ExactValue_Float:
+			return;
+		case ExactValue_Complex:
+			*x = exact_value_to_complex(*x);
 			return;
+		case ExactValue_Quaternion:
+			*x = exact_value_to_quaternion(*x);
+			return;
+		}
 		break;
 	}
 
-	compiler_error("How'd you get here? Invalid ExactValueKind");
+	compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
 }
 
 // TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
@@ -420,6 +618,84 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 		default: goto error;
 		}
 	} break;
+
+	case ExactValue_Complex: {
+		y = exact_value_to_complex(y);
+		f64 a = x.value_complex.real;
+		f64 b = x.value_complex.imag;
+		f64 c = y.value_complex.real;
+		f64 d = y.value_complex.imag;
+		f64 real = 0;
+		f64 imag = 0;
+		switch (op) {
+		case Token_Add:
+			real = a + c;
+			imag = b + d;
+			break;
+		case Token_Sub:
+			real = a - c;
+			imag = b - d;
+			break;
+		case Token_Mul:
+			real = (a*c - b*d);
+			imag = (b*c + a*d);
+			break;
+		case Token_Quo: {
+			f64 s = c*c + d*d;
+			real = (a*c + b*d)/s;
+			imag = (b*c - a*d)/s;
+		} break;
+		default: goto error;
+		}
+		return exact_value_complex(real, imag);
+	} break;
+
+	case ExactValue_Quaternion: {
+		y = exact_value_to_quaternion(y);
+		f64 a = x.value_quaternion.real;
+		f64 b = x.value_quaternion.imag;
+		f64 c = x.value_quaternion.jmag;
+		f64 d = x.value_quaternion.kmag;
+
+		f64 e = x.value_quaternion.real;
+		f64 f = x.value_quaternion.imag;
+		f64 g = x.value_quaternion.jmag;
+		f64 h = x.value_quaternion.kmag;
+
+		f64 real = 0;
+		f64 imag = 0;
+		f64 jmag = 0;
+		f64 kmag = 0;
+		switch (op) {
+		case Token_Add:
+			real = a + e;
+			imag = b + f;
+			jmag = c + g;
+			kmag = d + h;
+			break;
+		case Token_Sub:
+			real = a - e;
+			imag = b - f;
+			jmag = c - g;
+			kmag = d - h;
+			break;
+		case Token_Mul:
+			real = a*f + b*e + c*h - d*g;
+			imag = a*g - b*h + c*e + d*f;
+			jmag = a*h + b*g - c*f + d*e;
+			kmag = a*e - b*f - c*g - d*h;
+			break;
+		case Token_Quo: {
+			f64 s = e*e + f*f + g*g + h*h;
+			real = (+a*e + b*f + c*g + d*h)/s;
+			imag = (-a*f + b*e - c*h + d*h)/s;
+			jmag = (-a*g + b*h + c*e - d*f)/s;
+			kmag = (-a*h - b*g + c*f + d*e)/s;
+		} break;
+		default: goto error;
+		}
+		return exact_value_quaternion(real, imag, jmag, kmag);
+	} break;
 	}
 
 error:
@@ -480,6 +756,34 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 		}
 	} break;
 
+	case ExactValue_Complex: {
+		f64 a = x.value_complex.real;
+		f64 b = x.value_complex.imag;
+		f64 c = y.value_complex.real;
+		f64 d = y.value_complex.imag;
+		switch (op) {
+		case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
+		case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
+		}
+	} break;
+
+	case ExactValue_Quaternion: {
+		f64 a = x.value_quaternion.real;
+		f64 b = x.value_quaternion.imag;
+		f64 c = x.value_quaternion.jmag;
+		f64 d = x.value_quaternion.kmag;
+
+		f64 e = y.value_quaternion.real;
+		f64 f = y.value_quaternion.imag;
+		f64 g = y.value_quaternion.jmag;
+		f64 h = y.value_quaternion.kmag;
+
+		switch (op) {
+		case Token_CmpEq: return cmp_f64(a, e) == 0 && cmp_f64(b, f) == 0 && cmp_f64(c, g) == 0 && cmp_f64(d, h) == 0;
+		case Token_NotEq: return cmp_f64(a, e) != 0 || cmp_f64(b, f) != 0 || cmp_f64(c, g) != 0 || cmp_f64(d, h) != 0;
+		}
+	} break;
+
 	case ExactValue_String: {
 		String a = x.value_string;
 		String b = y.value_string;

+ 358 - 6
src/ir.c

@@ -1351,6 +1351,7 @@ irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) {
 	return ir_emit(p, ir_instr_store(p, address, value));
 }
 irValue *ir_emit_load(irProcedure *p, irValue *address) {
+	GB_ASSERT(address != NULL);
 	return ir_emit(p, ir_instr_load(p, address));
 }
 irValue *ir_emit_select(irProcedure *p, irValue *cond, irValue *t, irValue *f) {
@@ -1653,6 +1654,7 @@ irValue *ir_addr_load(irProcedure *proc, irAddr addr) {
 }
 
 irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index);
+irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index);
 
 irValue *ir_emit_ptr_offset(irProcedure *proc, irValue *ptr, irValue *offset) {
 	offset = ir_emit_conv(proc, offset, t_int);
@@ -1716,6 +1718,180 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
 		return ir_emit_load(proc, res);
 	}
 
+	if (is_type_complex(t_left)) {
+		ir_emit_comment(proc, str_lit("complex.arith.begin"));
+		Type *ft = base_complex_elem_type(t_left);
+
+		irValue *res = ir_add_local_generated(proc, type);
+		irValue *a = ir_emit_struct_ev(proc, left,  0);
+		irValue *b = ir_emit_struct_ev(proc, left,  1);
+		irValue *c = ir_emit_struct_ev(proc, right, 0);
+		irValue *d = ir_emit_struct_ev(proc, right, 1);
+
+		irValue *real = NULL;
+		irValue *imag = NULL;
+
+		switch (op) {
+		case Token_Add:
+			real = ir_emit_arith(proc, Token_Add, a, c, ft);
+			imag = ir_emit_arith(proc, Token_Add, b, d, ft);
+			break;
+		case Token_Sub:
+			real = ir_emit_arith(proc, Token_Sub, a, c, ft);
+			imag = ir_emit_arith(proc, Token_Sub, b, d, ft);
+			break;
+		case Token_Mul: {
+			irValue *x = ir_emit_arith(proc, Token_Mul, a, c, ft);
+			irValue *y = ir_emit_arith(proc, Token_Mul, b, d, ft);
+			real = ir_emit_arith(proc, Token_Sub, x, y, ft);
+			irValue *z = ir_emit_arith(proc, Token_Mul, b, c, ft);
+			irValue *w = ir_emit_arith(proc, Token_Mul, a, d, ft);
+			imag = ir_emit_arith(proc, Token_Add, z, w, ft);
+		} break;
+		case Token_Quo: {
+			irValue *s1 = ir_emit_arith(proc, Token_Mul, c, c, ft);
+			irValue *s2 = ir_emit_arith(proc, Token_Mul, d, d, ft);
+			irValue *s  = ir_emit_arith(proc, Token_Add, s1, s2, ft);
+
+			irValue *x = ir_emit_arith(proc, Token_Mul, a, c, ft);
+			irValue *y = ir_emit_arith(proc, Token_Mul, b, d, ft);
+			real = ir_emit_arith(proc, Token_Add, x, y, ft);
+			real = ir_emit_arith(proc, Token_Quo, real, s, ft);
+
+			irValue *z = ir_emit_arith(proc, Token_Mul, b, c, ft);
+			irValue *w = ir_emit_arith(proc, Token_Mul, a, d, ft);
+			imag = ir_emit_arith(proc, Token_Sub, z, w, ft);
+			imag = ir_emit_arith(proc, Token_Quo, imag, s, ft);
+		} break;
+		}
+
+		ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag);
+
+		ir_emit_comment(proc, str_lit("complex.end.begin"));
+		return ir_emit_load(proc, res);
+	} else if (is_type_quaternion(t_left)) {
+		ir_emit_comment(proc, str_lit("quaternion.arith.begin"));
+		Type *ft = base_quaternion_elem_type(t_left);
+
+		irValue *res = ir_add_local_generated(proc, type);
+		irValue *a = ir_emit_struct_ev(proc, left,  0);
+		irValue *b = ir_emit_struct_ev(proc, left,  1);
+		irValue *c = ir_emit_struct_ev(proc, left,  2);
+		irValue *d = ir_emit_struct_ev(proc, left,  3);
+
+		irValue *e = ir_emit_struct_ev(proc, right, 0);
+		irValue *f = ir_emit_struct_ev(proc, right, 1);
+		irValue *g = ir_emit_struct_ev(proc, right, 2);
+		irValue *h = ir_emit_struct_ev(proc, right, 3);
+
+		irValue *real = NULL;
+		irValue *imag = NULL;
+		irValue *jmag = NULL;
+		irValue *kmag = NULL;
+
+		switch (op) {
+		case Token_Add:
+			real = ir_emit_arith(proc, Token_Add, a, e, ft);
+			imag = ir_emit_arith(proc, Token_Add, b, f, ft);
+			jmag = ir_emit_arith(proc, Token_Add, c, g, ft);
+			kmag = ir_emit_arith(proc, Token_Add, d, h, ft);
+			break;
+		case Token_Sub:
+			real = ir_emit_arith(proc, Token_Sub, a, e, ft);
+			imag = ir_emit_arith(proc, Token_Sub, b, f, ft);
+			jmag = ir_emit_arith(proc, Token_Sub, c, g, ft);
+			kmag = ir_emit_arith(proc, Token_Sub, d, h, ft);
+			break;
+		case Token_Mul: {
+			irValue *r0 = ir_emit_arith(proc, Token_Mul, a, e, ft);
+			irValue *r1 = ir_emit_arith(proc, Token_Mul, b, f, ft);
+			irValue *r2 = ir_emit_arith(proc, Token_Mul, c, h, ft);
+			irValue *r3 = ir_emit_arith(proc, Token_Mul, d, g, ft);
+			real = ir_emit_arith(proc, Token_Add, r0, r1, ft);
+			real = ir_emit_arith(proc, Token_Add, real, r2, ft);
+			real = ir_emit_arith(proc, Token_Add, real, r3, ft);
+
+			irValue *i0 = ir_emit_arith(proc, Token_Mul, a, g, ft);
+			irValue *i1 = ir_emit_arith(proc, Token_Mul, b, h, ft);
+			irValue *i2 = ir_emit_arith(proc, Token_Mul, c, e, ft);
+			irValue *i3 = ir_emit_arith(proc, Token_Mul, d, f, ft);
+			imag = ir_emit_arith(proc, Token_Sub, i0, i1, ft);
+			imag = ir_emit_arith(proc, Token_Add, imag, i2, ft);
+			imag = ir_emit_arith(proc, Token_Add, imag, i3, ft);
+
+			irValue *j0 = ir_emit_arith(proc, Token_Mul, a, h, ft);
+			irValue *j1 = ir_emit_arith(proc, Token_Mul, b, g, ft);
+			irValue *j2 = ir_emit_arith(proc, Token_Mul, c, f, ft);
+			irValue *j3 = ir_emit_arith(proc, Token_Mul, d, e, ft);
+			jmag = ir_emit_arith(proc, Token_Add, j0, j1, ft);
+			jmag = ir_emit_arith(proc, Token_Sub, imag, j2, ft);
+			jmag = ir_emit_arith(proc, Token_Sub, imag, j3, ft);
+
+			irValue *k0 = ir_emit_arith(proc, Token_Mul, a, e, ft);
+			irValue *k1 = ir_emit_arith(proc, Token_Mul, b, f, ft);
+			irValue *k2 = ir_emit_arith(proc, Token_Mul, c, g, ft);
+			irValue *k3 = ir_emit_arith(proc, Token_Mul, d, h, ft);
+			kmag = ir_emit_arith(proc, Token_Sub, j0, j1, ft);
+			kmag = ir_emit_arith(proc, Token_Sub, imag, j2, ft);
+			kmag = ir_emit_arith(proc, Token_Sub, imag, j3, ft);
+		} break;
+		case Token_Quo: {
+			irValue *s0 = ir_emit_arith(proc, Token_Mul, e, e, ft);
+			irValue *s1 = ir_emit_arith(proc, Token_Mul, f, f, ft);
+			irValue *s2 = ir_emit_arith(proc, Token_Mul, g, g, ft);
+			irValue *s3 = ir_emit_arith(proc, Token_Mul, h, h, ft);
+			irValue *s = ir_emit_arith(proc, Token_Add, s0, s1, ft);
+			s = ir_emit_arith(proc, Token_Add, s, s2, ft);
+			s = ir_emit_arith(proc, Token_Add, s, s3, ft);
+
+			irValue *r0 = ir_emit_arith(proc, Token_Mul, a, e, ft);
+			irValue *r1 = ir_emit_arith(proc, Token_Mul, b, f, ft);
+			irValue *r2 = ir_emit_arith(proc, Token_Mul, c, h, ft);
+			irValue *r3 = ir_emit_arith(proc, Token_Mul, d, g, ft);
+			real = ir_emit_arith(proc, Token_Add, r0, r1, ft);
+			real = ir_emit_arith(proc, Token_Add, real, r2, ft);
+			real = ir_emit_arith(proc, Token_Add, real, r3, ft);
+			real = ir_emit_arith(proc, Token_Quo, real, s, ft);
+
+			irValue *i0 = ir_emit_arith(proc, Token_Mul, a, f, ft);
+			irValue *i1 = ir_emit_arith(proc, Token_Mul, b, e, ft);
+			irValue *i2 = ir_emit_arith(proc, Token_Mul, c, h, ft);
+			irValue *i3 = ir_emit_arith(proc, Token_Mul, d, g, ft);
+			imag = ir_emit_arith(proc, Token_Sub, i1, i0, ft);
+			imag = ir_emit_arith(proc, Token_Sub, imag, i2, ft);
+			imag = ir_emit_arith(proc, Token_Add, imag, i3, ft);
+			imag = ir_emit_arith(proc, Token_Quo, imag, s, ft);
+
+			irValue *j0 = ir_emit_arith(proc, Token_Mul, a, g, ft);
+			irValue *j1 = ir_emit_arith(proc, Token_Mul, b, h, ft);
+			irValue *j2 = ir_emit_arith(proc, Token_Mul, c, e, ft);
+			irValue *j3 = ir_emit_arith(proc, Token_Mul, d, f, ft);
+			jmag = ir_emit_arith(proc, Token_Sub, j1, j0, ft);
+			jmag = ir_emit_arith(proc, Token_Add, imag, j2, ft);
+			jmag = ir_emit_arith(proc, Token_Sub, imag, j3, ft);
+			jmag = ir_emit_arith(proc, Token_Quo, jmag, s, ft);
+
+			irValue *k0 = ir_emit_arith(proc, Token_Mul, a, h, ft);
+			irValue *k1 = ir_emit_arith(proc, Token_Mul, b, g, ft);
+			irValue *k2 = ir_emit_arith(proc, Token_Mul, c, f, ft);
+			irValue *k3 = ir_emit_arith(proc, Token_Mul, d, e, ft);
+			kmag = ir_emit_arith(proc, Token_Add, k2, k3, ft);
+			kmag = ir_emit_arith(proc, Token_Sub, imag, k0, ft);
+			kmag = ir_emit_arith(proc, Token_Sub, imag, k1, ft);
+			kmag = ir_emit_arith(proc, Token_Quo, kmag, s, ft);
+		} break;
+		}
+
+		ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), jmag);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), kmag);
+
+		ir_emit_comment(proc, str_lit("quaternion.end.begin"));
+		return ir_emit_load(proc, res);
+	}
+
 
 	if (op == Token_Add) {
 		if (is_type_pointer(t_left)) {
@@ -1890,6 +2066,20 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) {
 		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_complex(t)) {
+		Type *ft = base_complex_elem_type(t);
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, ft); break;
+		case 1: result_type = make_type_pointer(a, ft); break;
+		}
+	} else if (is_type_quaternion(t)) {
+		Type *ft = base_quaternion_elem_type(t);
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, ft); break;
+		case 1: result_type = make_type_pointer(a, ft); break;
+		case 2: result_type = make_type_pointer(a, ft); break;
+		case 3: result_type = make_type_pointer(a, ft); break;
+		}
 	} else if (is_type_slice(t)) {
 		switch (index) {
 		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
@@ -1956,6 +2146,20 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
 		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_complex(t)) {
+		Type *ft = base_complex_elem_type(t);
+		switch (index) {
+		case 0: result_type = ft; break;
+		case 1: result_type = ft; break;
+		}
+	} else if (is_type_quaternion(t)) {
+		Type *ft = base_quaternion_elem_type(t);
+		switch (index) {
+		case 0: result_type = ft; break;
+		case 1: result_type = ft; break;
+		case 2: result_type = ft; break;
+		case 3: result_type = ft; break;
+		}
 	} else if (is_type_slice(t)) {
 		switch (index) {
 		case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
@@ -2285,6 +2489,10 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 			ExactValue ev = value->Constant.value;
 			if (is_type_float(dst)) {
 				ev = exact_value_to_float(ev);
+			} else if (is_type_complex(dst)) {
+				ev = exact_value_to_complex(ev);
+			} else if (is_type_quaternion(dst)) {
+				ev = exact_value_to_quaternion(ev);
 			} else if (is_type_string(dst)) {
 				// Handled elsewhere
 				GB_ASSERT(ev.kind == ExactValue_String);
@@ -2350,6 +2558,30 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 		return ir_emit(proc, ir_instr_conv(proc, kind, value, src, dst));
 	}
 
+	if (is_type_complex(src) && is_type_complex(dst)) {
+		Type *ft = base_complex_elem_type(dst);
+		irValue *gen = ir_add_local_generated(proc, dst);
+		irValue *real = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft);
+		irValue *imag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), imag);
+		return ir_emit_load(proc, gen);
+	}
+
+	if (is_type_quaternion(src) && is_type_quaternion(dst)) {
+		Type *ft = base_quaternion_elem_type(dst);
+		irValue *gen = ir_add_local_generated(proc, dst);
+		irValue *real = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft);
+		irValue *imag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft);
+		irValue *jmag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 2), ft);
+		irValue *kmag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 3), ft);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), imag);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 2), jmag);
+		ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), kmag);
+		return ir_emit_load(proc, gen);
+	}
+
 	// float <-> integer
 	if (is_type_float(src) && is_type_integer(dst)) {
 		irConvKind kind = irConv_fptosi;
@@ -3704,6 +3936,96 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 					// return ir_emit(proc, ir_instr_vector_shuffle(proc, vector, indices, index_count));
 				} break;
 
+				case BuiltinProc_complex: {
+					ir_emit_comment(proc, str_lit("complex"));
+					irValue *real = ir_build_expr(proc, ce->args.e[0]);
+					irValue *imag = ir_build_expr(proc, ce->args.e[1]);
+					irValue *dst = ir_add_local_generated(proc, tv->type);
+
+					Type *ft = base_complex_elem_type(tv->type);
+					real = ir_emit_conv(proc, real, ft);
+					imag = ir_emit_conv(proc, imag, ft);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 0), real);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 1), imag);
+
+					return ir_emit_load(proc, dst);
+				} break;
+
+				case BuiltinProc_quaternion: {
+					ir_emit_comment(proc, str_lit("quaternion"));
+					irValue *real = ir_build_expr(proc, ce->args.e[0]);
+					irValue *imag = ir_build_expr(proc, ce->args.e[1]);
+					irValue *jmag = ir_build_expr(proc, ce->args.e[2]);
+					irValue *kmag = ir_build_expr(proc, ce->args.e[3]);
+					irValue *dst = ir_add_local_generated(proc, tv->type);
+
+					Type *ft = base_complex_elem_type(tv->type);
+					real = ir_emit_conv(proc, real, ft);
+					imag = ir_emit_conv(proc, imag, ft);
+					jmag = ir_emit_conv(proc, jmag, ft);
+					kmag = ir_emit_conv(proc, kmag, ft);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 0), real);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 1), imag);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 2), jmag);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 3), kmag);
+
+					return ir_emit_load(proc, dst);
+				} break;
+
+				case BuiltinProc_real: {
+					ir_emit_comment(proc, str_lit("real"));
+					irValue *val = ir_build_expr(proc, ce->args.e[0]);
+					irValue *real = ir_emit_struct_ev(proc, val, 0);
+					return ir_emit_conv(proc, real, tv->type);
+				} break;
+				case BuiltinProc_imag: {
+					ir_emit_comment(proc, str_lit("imag"));
+					irValue *val = ir_build_expr(proc, ce->args.e[0]);
+					irValue *imag = ir_emit_struct_ev(proc, val, 1);
+					return ir_emit_conv(proc, imag, tv->type);
+				} break;
+				case BuiltinProc_jmag: {
+					ir_emit_comment(proc, str_lit("jmag"));
+					irValue *val = ir_build_expr(proc, ce->args.e[0]);
+					irValue *jmag = ir_emit_struct_ev(proc, val, 2);
+					return ir_emit_conv(proc, jmag, tv->type);
+				} break;
+				case BuiltinProc_kmag: {
+					ir_emit_comment(proc, str_lit("kmag"));
+					irValue *val = ir_build_expr(proc, ce->args.e[0]);
+					irValue *kmag = ir_emit_struct_ev(proc, val, 3);
+					return ir_emit_conv(proc, kmag, tv->type);
+				} break;
+
+				case BuiltinProc_conj: {
+					ir_emit_comment(proc, str_lit("conj"));
+					irValue *val = ir_build_expr(proc, ce->args.e[0]);
+					irValue *res = NULL;
+					Type *t = ir_type(val);
+					if (is_type_complex(t)) {
+						res = ir_add_local_generated(proc, tv->type);
+						irValue *real = ir_emit_struct_ev(proc, val, 0);
+						irValue *imag = ir_emit_struct_ev(proc, val, 1);
+						imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag));
+						ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real);
+						ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag);
+					} else if (is_type_quaternion(t)) {
+						res = ir_add_local_generated(proc, tv->type);
+						irValue *real = ir_emit_struct_ev(proc, val, 0);
+						irValue *imag = ir_emit_struct_ev(proc, val, 1);
+						irValue *jmag = ir_emit_struct_ev(proc, val, 2);
+						irValue *kmag = ir_emit_struct_ev(proc, val, 3);
+						imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag));
+						jmag = ir_emit_unary_arith(proc, Token_Sub, jmag, ir_type(jmag));
+						kmag = ir_emit_unary_arith(proc, Token_Sub, kmag, ir_type(kmag));
+						ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real);
+						ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag);
+						ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), jmag);
+						ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), kmag);
+					}
+					return ir_emit_load(proc, res);
+				} break;
+
 				case BuiltinProc_slice_ptr: {
 					ir_emit_comment(proc, str_lit("slice_ptr"));
 					irValue *ptr = ir_build_expr(proc, ce->args.e[0]);
@@ -3760,6 +4082,27 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 					ir_emit_comment(proc, str_lit("abs"));
 					irValue *x = ir_build_expr(proc, ce->args.e[0]);
 					Type *t = ir_type(x);
+					if (is_type_complex(t)) {
+						gbAllocator a = proc->module->allocator;
+						i64 sz = 8*type_size_of(a, t);
+						irValue **args = gb_alloc_array(a, irValue *, 1);
+						args[0] = x;
+						switch (sz) {
+						case 64:  return ir_emit_global_call(proc, "__abs_complex64",  args, 1);
+						case 128: return ir_emit_global_call(proc, "__abs_complex128", args, 1);
+						}
+						GB_PANIC("Unknown complex type");
+					} else if (is_type_quaternion(t)) {
+						gbAllocator a = proc->module->allocator;
+						i64 sz = 8*type_size_of(a, t);
+						irValue **args = gb_alloc_array(a, irValue *, 1);
+						args[0] = x;
+						switch (sz) {
+						case 128: return ir_emit_global_call(proc, "__abs_quaternion128", args, 1);
+						case 256: return ir_emit_global_call(proc, "__abs_quaternion256", args, 1);
+						}
+						GB_PANIC("Unknown quaternion type");
+					}
 					irValue *zero = ir_emit_conv(proc, v_zero, t);
 					irValue *cond = ir_emit_comp(proc, Token_Lt, x, zero);
 					irValue *neg = ir_emit(proc, ir_instr_unary_op(proc, Token_Sub, x, t));
@@ -6528,8 +6871,6 @@ void ir_gen_tree(irGen *s) {
 					case Basic_u32:
 					case Basic_i64:
 					case Basic_u64:
-					// case Basic_i128:
-					// case Basic_u128:
 					case Basic_int:
 					case Basic_uint: {
 						tag = ir_emit_conv(proc, ti_ptr, t_type_info_integer_ptr);
@@ -6540,16 +6881,27 @@ void ir_gen_tree(irGen *s) {
 						ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), is_signed);
 					} break;
 
-					// case Basic_f16:
 					case Basic_f32:
-					case Basic_f64:
-					// case Basic_f128:
-					{
+					case Basic_f64: {
 						tag = ir_emit_conv(proc, ti_ptr, t_type_info_float_ptr);
 						irValue *bits = ir_const_int(a, type_size_of(a, t));
 						ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits);
 					} break;
 
+					case Basic_complex64:
+					case Basic_complex128: {
+						tag = ir_emit_conv(proc, ti_ptr, t_type_info_complex_ptr);
+						irValue *bits = ir_const_int(a, type_size_of(a, t));
+						ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits);
+					} break;
+
+					case Basic_quaternion128:
+					case Basic_quaternion256: {
+						tag = ir_emit_conv(proc, ti_ptr, t_type_info_quaternion_ptr);
+						irValue *bits = ir_const_int(a, type_size_of(a, t));
+						ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits);
+					} break;
+
 					case Basic_rawptr:
 						tag = ir_emit_conv(proc, ti_ptr, t_type_info_pointer_ptr);
 						break;

+ 142 - 38
src/ir_print.c

@@ -145,26 +145,30 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 	switch (t->kind) {
 	case Type_Basic:
 		switch (t->Basic.kind) {
-		case Basic_bool:   ir_fprintf(f, "i1");                      return;
-		case Basic_i8:     ir_fprintf(f, "i8");                      return;
-		case Basic_u8:     ir_fprintf(f, "i8");                      return;
-		case Basic_i16:    ir_fprintf(f, "i16");                     return;
-		case Basic_u16:    ir_fprintf(f, "i16");                     return;
-		case Basic_i32:    ir_fprintf(f, "i32");                     return;
-		case Basic_u32:    ir_fprintf(f, "i32");                     return;
-		case Basic_i64:    ir_fprintf(f, "i64");                     return;
-		case Basic_u64:    ir_fprintf(f, "i64");                     return;
-		// case Basic_i128:   ir_fprintf(f, "i128");                    return;
-		// case Basic_u128:   ir_fprintf(f, "i128");                    return;
-		// case Basic_f16:    ir_fprintf(f, "half");                    return;
-		case Basic_f32:    ir_fprintf(f, "float");                   return;
-		case Basic_f64:    ir_fprintf(f, "double");                  return;
-		// case Basic_f128:   ir_fprintf(f, "fp128");                   return;
-		case Basic_rawptr: ir_fprintf(f, "%%..rawptr");              return;
-		case Basic_string: ir_fprintf(f, "%%..string");              return;
-		case Basic_uint:   ir_fprintf(f, "i%lld", word_bits);        return;
-		case Basic_int:    ir_fprintf(f, "i%lld", word_bits);        return;
-		case Basic_any:    ir_fprintf(f, "%%..any");                 return;
+		case Basic_bool:   ir_fprintf(f, "i1");                       return;
+		case Basic_i8:     ir_fprintf(f, "i8");                       return;
+		case Basic_u8:     ir_fprintf(f, "i8");                       return;
+		case Basic_i16:    ir_fprintf(f, "i16");                      return;
+		case Basic_u16:    ir_fprintf(f, "i16");                      return;
+		case Basic_i32:    ir_fprintf(f, "i32");                      return;
+		case Basic_u32:    ir_fprintf(f, "i32");                      return;
+		case Basic_i64:    ir_fprintf(f, "i64");                      return;
+		case Basic_u64:    ir_fprintf(f, "i64");                      return;
+
+		case Basic_f32:    ir_fprintf(f, "float");                    return;
+		case Basic_f64:    ir_fprintf(f, "double");                   return;
+
+		case Basic_complex64:  ir_fprintf(f, "%%..complex64");        return;
+		case Basic_complex128: ir_fprintf(f, "%%..complex128");       return;
+
+		case Basic_quaternion128: ir_fprintf(f, "%%..quaternion128"); return;
+		case Basic_quaternion256: ir_fprintf(f, "%%..quaternion256"); return;
+
+		case Basic_rawptr: ir_fprintf(f, "%%..rawptr");               return;
+		case Basic_string: ir_fprintf(f, "%%..string");               return;
+		case Basic_uint:   ir_fprintf(f, "i%lld", word_bits);         return;
+		case Basic_int:    ir_fprintf(f, "i%lld", word_bits);         return;
+		case Basic_any:    ir_fprintf(f, "%%..any");                  return;
 		}
 		break;
 	case Type_Pointer:
@@ -365,7 +369,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 	} break;
 	case ExactValue_Float: {
 		GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type));
-		type = base_type(type);
+		type = core_type(type);
 		u64 u = *cast(u64*)&value.value_float;
 		switch (type->Basic.kind) {
 		case Basic_f32:
@@ -382,28 +386,52 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 
 		switch (type->Basic.kind) {
 		case 0: break;
-#if 0
-		case Basic_f16:
-			ir_fprintf(f, "bitcast (");
-			ir_print_type(f, m, t_u16);
-			ir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float));
-			ir_print_type(f, m, t_f16);
-			ir_fprintf(f, ")");
-			break;
-		case Basic_f128:
-			ir_fprintf(f, "bitcast (");
-			ir_fprintf(f, "i128");
-			// TODO(bill): Actually support f128
-			ir_fprintf(f, " %llu to ", u);
-			ir_print_type(f, m, t_f128);
-			ir_fprintf(f, ")");
-			break;
-#endif
 		default:
 			ir_fprintf(f, "0x%016llx", u);
 			break;
 		}
 	} break;
+
+	case ExactValue_Complex: {
+		type = core_type(type);
+		if (is_type_quaternion(type)) {
+			Type *ft = base_quaternion_elem_type(type);
+			ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+			ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
+			ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+			ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
+			ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+			ir_print_exact_value(f, m, exact_value_float(0), ft);
+			ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+			ir_print_exact_value(f, m, exact_value_float(0), ft);
+			ir_fprintf(f, "}");
+
+		} else {
+			GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type));
+			Type *ft = base_complex_elem_type(type);
+			ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+			ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
+			ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+			ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
+			ir_fprintf(f, "}");
+		}
+	} break;
+
+	case ExactValue_Quaternion: {
+		GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type));
+		type = core_type(type);
+		Type *ft = base_quaternion_elem_type(type);
+		ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
+		ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
+		ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
+		ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
+		ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
+		ir_fprintf(f, "}");
+	} break;
+
 	case ExactValue_Pointer:
 		if (value.value_pointer == 0) {
 			ir_fprintf(f, "null");
@@ -1007,6 +1035,72 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 				case Token_LtEq:  ir_fprintf(f, "ole"); break;
 				case Token_GtEq:  ir_fprintf(f, "oge"); break;
 				}
+			} else if (is_type_complex(elem_type)) {
+				ir_fprintf(f, "call ");
+				ir_print_calling_convention(f, m, ProcCC_Odin);
+				ir_print_type(f, m, t_bool);
+				char *runtime_proc = "";
+				i64 sz = 8*type_size_of(m->allocator, elem_type);
+				switch (sz) {
+				case 64:
+					switch (bo->op) {
+					case Token_CmpEq: runtime_proc = "__complex64_eq"; break;
+					case Token_NotEq: runtime_proc = "__complex64_ne"; break;
+					}
+					break;
+				case 128:
+					switch (bo->op) {
+					case Token_CmpEq: runtime_proc = "__complex128_eq"; break;
+					case Token_NotEq: runtime_proc = "__complex128_ne"; break;
+					}
+					break;
+				}
+
+				ir_fprintf(f, " ");
+				ir_print_encoded_global(f, make_string_c(runtime_proc), false);
+				ir_fprintf(f, "(");
+				ir_print_type(f, m, type);
+				ir_fprintf(f, " ");
+				ir_print_value(f, m, bo->left, type);
+				ir_fprintf(f, ", ");
+				ir_print_type(f, m, type);
+				ir_fprintf(f, " ");
+				ir_print_value(f, m, bo->right, type);
+				ir_fprintf(f, ")\n");
+				return;
+			} else if (is_type_quaternion(elem_type)) {
+				ir_fprintf(f, "call ");
+				ir_print_calling_convention(f, m, ProcCC_Odin);
+				ir_print_type(f, m, t_bool);
+				char *runtime_proc = "";
+				i64 sz = 8*type_size_of(m->allocator, elem_type);
+				switch (sz) {
+				case 128:
+					switch (bo->op) {
+					case Token_CmpEq: runtime_proc = "__quaternion128_eq"; break;
+					case Token_NotEq: runtime_proc = "__quaternion128_ne"; break;
+					}
+					break;
+				case 256:
+					switch (bo->op) {
+					case Token_CmpEq: runtime_proc = "__quaternion256_eq"; break;
+					case Token_NotEq: runtime_proc = "__quaternion256_ne"; break;
+					}
+					break;
+				}
+
+				ir_fprintf(f, " ");
+				ir_print_encoded_global(f, make_string_c(runtime_proc), false);
+				ir_fprintf(f, "(");
+				ir_print_type(f, m, type);
+				ir_fprintf(f, " ");
+				ir_print_value(f, m, bo->left, type);
+				ir_fprintf(f, ", ");
+				ir_print_type(f, m, type);
+				ir_fprintf(f, " ");
+				ir_print_value(f, m, bo->right, type);
+				ir_fprintf(f, ")\n");
+				return;
 			} else {
 				ir_fprintf(f, "icmp ");
 				if (bo->op != Token_CmpEq &&
@@ -1415,6 +1509,16 @@ void print_llvm_ir(irGen *ir) {
 	ir_print_encoded_local(f, str_lit("..rawptr"));
 	ir_fprintf(f, " = type i8* ; Basic_rawptr\n");
 
+	ir_print_encoded_local(f, str_lit("..complex64"));
+	ir_fprintf(f, " = type {float, float} ; Basic_complex64\n");
+	ir_print_encoded_local(f, str_lit("..complex128"));
+	ir_fprintf(f, " = type {double, double} ; Basic_complex128\n");
+	ir_print_encoded_local(f, str_lit("..quaternion128"));
+	ir_fprintf(f, " = type {float, float, float, float} ; Basic_quaternion128\n");
+	ir_print_encoded_local(f, str_lit("..quaternion256"));
+	ir_fprintf(f, " = type {double, double, double, double} ; Basic_quaternion256\n");
+
+
 	ir_print_encoded_local(f, str_lit("..any"));
 	ir_fprintf(f, " = type {");
 	ir_print_type(f, m, t_type_info_ptr);

+ 1 - 1
src/main.c

@@ -261,7 +261,7 @@ int main(int argc, char **argv) {
 	#if defined(GB_SYSTEM_WINDOWS)
 	// For more passes arguments: http://llvm.org/docs/Passes.html
 	exit_code = system_exec_command_line_app("llvm-opt", false,
-		"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
+		"\"%.*sbin/opt\" \"%s\" -o \"%.*s.bc\" "
 		"-mem2reg "
 		"-memcpyopt "
 		"-die "

+ 2 - 0
src/parser.c

@@ -1735,6 +1735,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 
 	case Token_Integer:
 	case Token_Float:
+	case Token_Imag:
 	case Token_Rune:
 		operand = ast_basic_lit(f, f->curr_token);
 		next_token(f);
@@ -3238,6 +3239,7 @@ AstNode *parse_stmt(AstFile *f) {
 	case Token_Ident:
 	case Token_Integer:
 	case Token_Float:
+	case Token_Imag:
 	case Token_Rune:
 	case Token_String:
 	case Token_OpenParen:

+ 11 - 3
src/tokenizer.c

@@ -7,6 +7,7 @@ TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
 	TOKEN_KIND(Token_Ident,     "identifier"), \
 	TOKEN_KIND(Token_Integer,   "integer"), \
 	TOKEN_KIND(Token_Float,     "float"), \
+	TOKEN_KIND(Token_Imag,      "imaginary"), \
 	TOKEN_KIND(Token_Rune,      "rune"), \
 	TOKEN_KIND(Token_String,    "string"), \
 TOKEN_KIND(Token__LiteralEnd,   "_LiteralEnd"), \
@@ -547,18 +548,18 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
 			}
 		}
 
-		token.string.len = t->curr - token.string.text;
-		return token;
+		goto end;
 	}
 
 	scan_mantissa(t, 10);
 
+
 fraction:
 	if (t->curr_rune == '.') {
 		// HACK(bill): This may be inefficient
 		TokenizerState state = save_tokenizer_state(t);
 		advance_to_next_rune(t);
-		if (digit_value(t->curr_rune) >= 10) {
+		if (t->curr_rune == '.') {
 			// TODO(bill): Clean up this shit
 			restore_tokenizer_state(t, &state);
 			goto end;
@@ -577,6 +578,13 @@ exponent:
 		scan_mantissa(t, 10);
 	}
 
+	switch (t->curr_rune) {
+	case 'i': case 'j': case 'k':
+		token.kind = Token_Imag;
+		advance_to_next_rune(t);
+		break;
+	}
+
 end:
 	token.string.len = t->curr - token.string.text;
 	return token;

+ 134 - 72
src/types.c

@@ -12,25 +12,15 @@ typedef enum BasicKind {
 	Basic_i64,
 	Basic_u64,
 
-/* 	Basic_i16le,
-	Basic_i16be,
-	Basic_u16le,
-	Basic_u16be,
-	Basic_i32le,
-	Basic_i32be,
-	Basic_u32le,
-	Basic_u32be,
-	Basic_i64le,
-	Basic_i64be,
-	Basic_u64le,
-	Basic_u64be, */
-
-	// Basic_i128,
-	// Basic_u128,
-	// Basic_f16,
 	Basic_f32,
 	Basic_f64,
-	// Basic_f128,
+
+	Basic_complex64,
+	Basic_complex128,
+
+	Basic_quaternion128,
+	Basic_quaternion256,
+
 	Basic_int,
 	Basic_uint,
 	Basic_rawptr,
@@ -40,6 +30,8 @@ typedef enum BasicKind {
 	Basic_UntypedBool,
 	Basic_UntypedInteger,
 	Basic_UntypedFloat,
+	Basic_UntypedComplex,
+	Basic_UntypedQuaternion,
 	Basic_UntypedString,
 	Basic_UntypedRune,
 	Basic_UntypedNil,
@@ -51,17 +43,19 @@ typedef enum BasicKind {
 } BasicKind;
 
 typedef enum BasicFlag {
-	BasicFlag_Boolean  = GB_BIT(0),
-	BasicFlag_Integer  = GB_BIT(1),
-	BasicFlag_Unsigned = GB_BIT(2),
-	BasicFlag_Float    = GB_BIT(3),
-	BasicFlag_Pointer  = GB_BIT(4),
-	BasicFlag_String   = GB_BIT(5),
-	BasicFlag_Rune     = GB_BIT(6),
-	BasicFlag_Untyped  = GB_BIT(7),
-
-	BasicFlag_Numeric      = BasicFlag_Integer | BasicFlag_Float,
-	BasicFlag_Ordered      = BasicFlag_Numeric | BasicFlag_String  | BasicFlag_Pointer,
+	BasicFlag_Boolean     = GB_BIT(0),
+	BasicFlag_Integer     = GB_BIT(1),
+	BasicFlag_Unsigned    = GB_BIT(2),
+	BasicFlag_Float       = GB_BIT(3),
+	BasicFlag_Complex     = GB_BIT(4),
+	BasicFlag_Quaternion  = GB_BIT(5),
+	BasicFlag_Pointer     = GB_BIT(6),
+	BasicFlag_String      = GB_BIT(7),
+	BasicFlag_Rune        = GB_BIT(8),
+	BasicFlag_Untyped     = GB_BIT(9),
+
+	BasicFlag_Numeric      = BasicFlag_Integer | BasicFlag_Float   | BasicFlag_Complex | BasicFlag_Quaternion,
+	BasicFlag_Ordered      = BasicFlag_Integer | BasicFlag_Float   | BasicFlag_String  | BasicFlag_Pointer,
 	BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune,
 } BasicFlag;
 
@@ -209,33 +203,43 @@ void selection_add_index(Selection *s, isize index) {
 
 
 gb_global Type basic_types[] = {
-	{Type_Basic, {Basic_Invalid,        0,                                       0, STR_LIT("invalid type")}},
-	{Type_Basic, {Basic_bool,           BasicFlag_Boolean,                       1, STR_LIT("bool")}},
-	{Type_Basic, {Basic_i8,             BasicFlag_Integer,                       1, STR_LIT("i8")}},
-	{Type_Basic, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned,  1, STR_LIT("u8")}},
-	{Type_Basic, {Basic_i16,            BasicFlag_Integer,                       2, STR_LIT("i16")}},
-	{Type_Basic, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned,  2, STR_LIT("u16")}},
-	{Type_Basic, {Basic_i32,            BasicFlag_Integer,                       4, STR_LIT("i32")}},
-	{Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned,  4, STR_LIT("u32")}},
-	{Type_Basic, {Basic_i64,            BasicFlag_Integer,                       8, STR_LIT("i64")}},
-	{Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned,  8, STR_LIT("u64")}},
-	// {Type_Basic, {Basic_i128,           BasicFlag_Integer,                      16, STR_LIT("i128")}},
-	// {Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
-	// {Type_Basic, {Basic_f16,            BasicFlag_Float,                         2, STR_LIT("f16")}},
-	{Type_Basic, {Basic_f32,            BasicFlag_Float,                         4, STR_LIT("f32")}},
-	{Type_Basic, {Basic_f64,            BasicFlag_Float,                         8, STR_LIT("f64")}},
-	// {Type_Basic, {Basic_f128,           BasicFlag_Float,                        16, STR_LIT("f128")}},
-	{Type_Basic, {Basic_int,            BasicFlag_Integer,                      -1, STR_LIT("int")}},
-	{Type_Basic, {Basic_uint,           BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}},
-	{Type_Basic, {Basic_rawptr,         BasicFlag_Pointer,                      -1, STR_LIT("rawptr")}},
-	{Type_Basic, {Basic_string,         BasicFlag_String,                       -1, STR_LIT("string")}},
-	{Type_Basic, {Basic_any,            0,                                      -1, STR_LIT("any")}},
-	{Type_Basic, {Basic_UntypedBool,    BasicFlag_Boolean | BasicFlag_Untyped,   0, STR_LIT("untyped bool")}},
-	{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped,   0, STR_LIT("untyped integer")}},
-	{Type_Basic, {Basic_UntypedFloat,   BasicFlag_Float   | BasicFlag_Untyped,   0, STR_LIT("untyped float")}},
-	{Type_Basic, {Basic_UntypedString,  BasicFlag_String  | BasicFlag_Untyped,   0, STR_LIT("untyped string")}},
-	{Type_Basic, {Basic_UntypedRune,    BasicFlag_Integer | BasicFlag_Untyped,   0, STR_LIT("untyped rune")}},
-	{Type_Basic, {Basic_UntypedNil,     BasicFlag_Untyped,                       0, STR_LIT("untyped nil")}},
+	{Type_Basic, {Basic_Invalid,           0,                                          0, STR_LIT("invalid type")}},
+
+	{Type_Basic, {Basic_bool,              BasicFlag_Boolean,                          1, STR_LIT("bool")}},
+
+	{Type_Basic, {Basic_i8,                BasicFlag_Integer,                          1, STR_LIT("i8")}},
+	{Type_Basic, {Basic_u8,                BasicFlag_Integer | BasicFlag_Unsigned,     1, STR_LIT("u8")}},
+	{Type_Basic, {Basic_i16,               BasicFlag_Integer,                          2, STR_LIT("i16")}},
+	{Type_Basic, {Basic_u16,               BasicFlag_Integer | BasicFlag_Unsigned,     2, STR_LIT("u16")}},
+	{Type_Basic, {Basic_i32,               BasicFlag_Integer,                          4, STR_LIT("i32")}},
+	{Type_Basic, {Basic_u32,               BasicFlag_Integer | BasicFlag_Unsigned,     4, STR_LIT("u32")}},
+	{Type_Basic, {Basic_i64,               BasicFlag_Integer,                          8, STR_LIT("i64")}},
+	{Type_Basic, {Basic_u64,               BasicFlag_Integer | BasicFlag_Unsigned,     8, STR_LIT("u64")}},
+
+	{Type_Basic, {Basic_f32,               BasicFlag_Float,                            4, STR_LIT("f32")}},
+	{Type_Basic, {Basic_f64,               BasicFlag_Float,                            8, STR_LIT("f64")}},
+
+	{Type_Basic, {Basic_complex64,         BasicFlag_Complex,                          8, STR_LIT("complex64")}},
+	{Type_Basic, {Basic_complex128,        BasicFlag_Complex,                         16, STR_LIT("complex128")}},
+
+	{Type_Basic, {Basic_quaternion128,     BasicFlag_Quaternion,                      16, STR_LIT("quaternion128")}},
+	{Type_Basic, {Basic_quaternion256,     BasicFlag_Quaternion,                      32, STR_LIT("quaternion256")}},
+
+	{Type_Basic, {Basic_int,               BasicFlag_Integer,                         -1, STR_LIT("int")}},
+	{Type_Basic, {Basic_uint,              BasicFlag_Integer | BasicFlag_Unsigned,    -1, STR_LIT("uint")}},
+
+	{Type_Basic, {Basic_rawptr,            BasicFlag_Pointer,                         -1, STR_LIT("rawptr")}},
+	{Type_Basic, {Basic_string,            BasicFlag_String,                          -1, STR_LIT("string")}},
+	{Type_Basic, {Basic_any,               0,                                         -1, STR_LIT("any")}},
+
+	{Type_Basic, {Basic_UntypedBool,       BasicFlag_Boolean    | BasicFlag_Untyped,   0, STR_LIT("untyped bool")}},
+	{Type_Basic, {Basic_UntypedInteger,    BasicFlag_Integer    | BasicFlag_Untyped,   0, STR_LIT("untyped integer")}},
+	{Type_Basic, {Basic_UntypedFloat,      BasicFlag_Float      | BasicFlag_Untyped,   0, STR_LIT("untyped float")}},
+	{Type_Basic, {Basic_UntypedComplex,    BasicFlag_Complex    | BasicFlag_Untyped,   0, STR_LIT("untyped complex")}},
+	{Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped,   0, STR_LIT("untyped quaternion")}},
+	{Type_Basic, {Basic_UntypedString,     BasicFlag_String     | BasicFlag_Untyped,   0, STR_LIT("untyped string")}},
+	{Type_Basic, {Basic_UntypedRune,       BasicFlag_Integer    | BasicFlag_Untyped,   0, STR_LIT("untyped rune")}},
+	{Type_Basic, {Basic_UntypedNil,        BasicFlag_Untyped,                          0, STR_LIT("untyped nil")}},
 };
 
 gb_global Type basic_type_aliases[] = {
@@ -253,25 +257,33 @@ gb_global Type *t_i32             = &basic_types[Basic_i32];
 gb_global Type *t_u32             = &basic_types[Basic_u32];
 gb_global Type *t_i64             = &basic_types[Basic_i64];
 gb_global Type *t_u64             = &basic_types[Basic_u64];
-// gb_global Type *t_i128            = &basic_types[Basic_i128];
-// gb_global Type *t_u128            = &basic_types[Basic_u128];
-// gb_global Type *t_f16             = &basic_types[Basic_f16];
+
 gb_global Type *t_f32             = &basic_types[Basic_f32];
 gb_global Type *t_f64             = &basic_types[Basic_f64];
-// gb_global Type *t_f128            = &basic_types[Basic_f128];
+
+gb_global Type *t_complex64       = &basic_types[Basic_complex64];
+gb_global Type *t_complex128      = &basic_types[Basic_complex128];
+
+gb_global Type *t_quaternion128   = &basic_types[Basic_quaternion128];
+gb_global Type *t_quaternion256   = &basic_types[Basic_quaternion256];
+
 gb_global Type *t_int             = &basic_types[Basic_int];
 gb_global Type *t_uint            = &basic_types[Basic_uint];
+
 gb_global Type *t_rawptr          = &basic_types[Basic_rawptr];
 gb_global Type *t_string          = &basic_types[Basic_string];
 gb_global Type *t_any             = &basic_types[Basic_any];
-gb_global Type *t_untyped_bool    = &basic_types[Basic_UntypedBool];
-gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger];
-gb_global Type *t_untyped_float   = &basic_types[Basic_UntypedFloat];
-gb_global Type *t_untyped_string  = &basic_types[Basic_UntypedString];
-gb_global Type *t_untyped_rune    = &basic_types[Basic_UntypedRune];
-gb_global Type *t_untyped_nil     = &basic_types[Basic_UntypedNil];
-gb_global Type *t_byte            = &basic_type_aliases[0];
-gb_global Type *t_rune            = &basic_type_aliases[1];
+
+gb_global Type *t_untyped_bool       = &basic_types[Basic_UntypedBool];
+gb_global Type *t_untyped_integer    = &basic_types[Basic_UntypedInteger];
+gb_global Type *t_untyped_float      = &basic_types[Basic_UntypedFloat];
+gb_global Type *t_untyped_complex    = &basic_types[Basic_UntypedComplex];
+gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion];
+gb_global Type *t_untyped_string     = &basic_types[Basic_UntypedString];
+gb_global Type *t_untyped_rune       = &basic_types[Basic_UntypedRune];
+gb_global Type *t_untyped_nil        = &basic_types[Basic_UntypedNil];
+gb_global Type *t_byte               = &basic_type_aliases[0];
+gb_global Type *t_rune               = &basic_type_aliases[1];
 
 
 gb_global Type *t_u8_ptr  = NULL;
@@ -293,6 +305,8 @@ gb_global Type *t_type_info_enum_value_ptr = NULL;
 gb_global Type *t_type_info_named         = NULL;
 gb_global Type *t_type_info_integer       = NULL;
 gb_global Type *t_type_info_float         = NULL;
+gb_global Type *t_type_info_complex       = NULL;
+gb_global Type *t_type_info_quaternion    = NULL;
 gb_global Type *t_type_info_any           = NULL;
 gb_global Type *t_type_info_string        = NULL;
 gb_global Type *t_type_info_boolean       = NULL;
@@ -312,6 +326,8 @@ gb_global Type *t_type_info_map           = NULL;
 gb_global Type *t_type_info_named_ptr         = NULL;
 gb_global Type *t_type_info_integer_ptr       = NULL;
 gb_global Type *t_type_info_float_ptr         = NULL;
+gb_global Type *t_type_info_complex_ptr       = NULL;
+gb_global Type *t_type_info_quaternion_ptr    = NULL;
 gb_global Type *t_type_info_any_ptr           = NULL;
 gb_global Type *t_type_info_string_ptr        = NULL;
 gb_global Type *t_type_info_boolean_ptr       = NULL;
@@ -615,6 +631,20 @@ bool is_type_float(Type *t) {
 	}
 	return false;
 }
+bool is_type_complex(Type *t) {
+	t = core_type(t);
+	if (t->kind == Type_Basic) {
+		return (t->Basic.flags & BasicFlag_Complex) != 0;
+	}
+	return false;
+}
+bool is_type_quaternion(Type *t) {
+	t = core_type(t);
+	if (t->kind == Type_Basic) {
+		return (t->Basic.flags & BasicFlag_Quaternion) != 0;
+	}
+	return false;
+}
 bool is_type_f32(Type *t) {
 	t = core_type(t);
 	if (t->kind == Type_Basic) {
@@ -695,6 +725,31 @@ Type *base_vector_type(Type *t) {
 	return t;
 }
 
+Type *base_complex_elem_type(Type *t) {
+	t = core_type(t);
+	if (is_type_complex(t)) {
+		switch (t->Basic.kind) {
+		case Basic_complex64:      return t_f32;
+		case Basic_complex128:     return t_f64;
+		case Basic_UntypedComplex: return t_untyped_float;
+		}
+	}
+	GB_PANIC("Invalid complex type");
+	return t_invalid;
+}
+Type *base_quaternion_elem_type(Type *t) {
+	t = core_type(t);
+	if (is_type_quaternion(t)) {
+		switch (t->Basic.kind) {
+		case Basic_quaternion128:     return t_f32;
+		case Basic_quaternion256:     return t_f64;
+		case Basic_UntypedQuaternion: return t_untyped_float;
+		}
+	}
+	GB_PANIC("Invalid quaternion type");
+	return t_invalid;
+}
+
 
 bool is_type_struct(Type *t) {
 	t = base_type(t);
@@ -950,11 +1005,13 @@ Type *default_type(Type *type) {
 	}
 	if (type->kind == Type_Basic) {
 		switch (type->Basic.kind) {
-		case Basic_UntypedBool:    return t_bool;
-		case Basic_UntypedInteger: return t_int;
-		case Basic_UntypedFloat:   return t_f64;
-		case Basic_UntypedString:  return t_string;
-		case Basic_UntypedRune:    return t_rune;
+		case Basic_UntypedBool:       return t_bool;
+		case Basic_UntypedInteger:    return t_int;
+		case Basic_UntypedFloat:      return t_f64;
+		case Basic_UntypedComplex:    return t_complex128;
+		case Basic_UntypedQuaternion: return t_quaternion256;
+		case Basic_UntypedString:     return t_string;
+		case Basic_UntypedRune:       return t_rune;
 		}
 	}
 	return type;
@@ -1542,6 +1599,11 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
 
 		case Basic_int: case Basic_uint: case Basic_rawptr:
 			return build_context.word_size;
+
+		case Basic_complex64: case Basic_complex128:
+			return type_size_of_internal(allocator, t, path) / 2;
+		case Basic_quaternion128: case Basic_quaternion256:
+			return type_size_of_internal(allocator, t, path) / 4;
 		}
 	} break;