Browse Source

Add branch labels for loops; using list

Ginger Bill 8 years ago
parent
commit
5562364a98
17 changed files with 1606 additions and 776 deletions
  1. 3 4
      build.bat
  2. 206 70
      code/demo.odin
  3. 3 3
      core/fmt.odin
  4. 1 3
      core/strconv.odin
  5. 2 8
      src/array.c
  6. 1 6
      src/check_decl.c
  7. 58 144
      src/check_expr.c
  8. 99 37
      src/check_stmt.c
  9. 19 7
      src/checker.c
  10. 15 1
      src/entity.c
  11. 21 15
      src/exact_value.c
  12. 4 0
      src/gb/gb.h
  13. 184 76
      src/ir.c
  14. 95 55
      src/parser.c
  15. 600 329
      src/ssa.c
  16. 277 0
      src/ssa_op.c
  17. 18 18
      src/tokenizer.c

+ 3 - 4
build.bat

@@ -44,10 +44,9 @@ del *.ilk > NUL 2> NUL
 
 cl %compiler_settings% "src\main.c" ^
 	/link %linker_settings% -OUT:%exe_name% ^
-	&& odin build code/metagen.odin ^
-	&& call "code\metagen.exe" "src\ast_nodes.metagen"
-	rem && odin build code/markdown.odin ^
-	rem && call "code\markdown.exe" "misc\example.md"
+	&& odin run code/demo.odin
+	rem && odin build code/metagen.odin ^
+	rem && call "code\metagen.exe" "src\ast_nodes.metagen"
 	rem && odin run code/Jaze/src/main.odin
 
 del *.obj > NUL 2> NUL

+ 206 - 70
code/demo.odin

@@ -10,40 +10,18 @@
 #import win32 "sys/windows.odin";
 
 main :: proc() {
-	fmt.println("Here");
-when false {
 /*
-	Version 0.1.1
-
 	Added:
-	 * Dynamic Arrays `[dynamic]Type`
-	 * Dynamic Maps   `map[Key]Value`
-	 * Dynamic array and map literals
-	 * Custom struct alignemnt `struct #align 8 { bar: i8 }`
-	 * Allow `_` in numbers
-	 * Variadic `append`
-	 * fmt.sprint*
-	 * Entities prefixes with an underscore do not get exported on imports
-	 * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
-	 * enum types have an implict `names` field, a []string of all the names in that enum
-	 * immutable variables are "completely immutable" - rules need a full explanation
-	 * `slice_to_bytes` - convert any slice to a slice of bytes
-	 * `union_cast` allows for optional ok check
-	 * Record type field `names` (struct/raw_union/enum)
-	 * ?: ternary operator
-	 * Unions with variants and common fields
-	 * New built-in procedures
-	     - `delete` to delete map entries `delete(m, key)`
-	     - `clear` to clear dynamic maps and arrays `clear(map_or_array)`
-	     - `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
-	 * Unexported entities and fields using an underscore prefix
+		* Unexported entities and fields using an underscore prefix
+			- See `sync.odin` and explain
 
 	Removed:
 	 * Maybe/option types
 	 * Remove `type` keyword and other "reserved" keywords
-	 * `compile_assert` and `assert`return the value of the condition for semantic reasons
+	 * ..< and ... removed and replace with .. (half-closed range)
 
 	Changed:
+	 * `compile_assert` and `assert`return the value of the condition for semantic reasons
 	 * thread_local -> #thread_local
 	 * #include -> #load
 	 * Files only get checked if they are actually used
@@ -51,20 +29,7 @@ when false {
 	 * Version numbering now starts from 0.1.0 and uses the convention:
 	 	- major.minor.patch
 	 * Core library additions to Windows specific stuff
-
-	Fixes:
-	 * Many fmt.* fixes
-	 * Overloading bug due to comparison of named types
-	 * Overloading bug due to `#import .` collision
-	 * disallow a `cast` from pointers of unions
-	 * Minor bugs in generated IR code for slices
-
-	To come very Soon™:
-	 * Linux and OS X builds (unofficial ones do exist already)
-*/
-	{
-
-	}
+ */
 
 	{
 		Fruit :: enum {
@@ -75,6 +40,147 @@ when false {
 		fmt.println(Fruit.names);
 	}
 
+	{
+		A :: struct           {x, y: f32};
+		B :: struct #align 16 {x, y: f32};
+		fmt.println("align_of(A) =", align_of(A));
+		fmt.println("align_of(B) =", align_of(B));
+	}
+
+	{
+		// Removal of ..< and ...
+		for i in 0..16 {
+		}
+		// Is similar to
+		for _i := 0; _i < 16; _i++ { immutable i := _i;
+		}
+	}
+
+	{
+	#label thing
+		for i in 0..10 {
+			for j := i+1; j < 10; j++ {
+				if j == 2 {
+					fmt.println(i, j);
+					break thing;
+				}
+			}
+		}
+		return;
+	}
+
+	{
+		cond := true;
+		x: int;
+		if cond {
+			x = 3;
+		} else {
+			x = 4;
+		}
+
+
+		// Ternary operator
+		y := cond ? 3 : 4;
+
+		FOO :: true ? 123 : 432; // Constant ternary operation
+		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);
@@ -109,49 +215,79 @@ when false {
 		c := m["c"];
 		_, ok := m["c"];
 		assert(ok && c == 7654);
+		fmt.println(m);
+
+		delete(m, "c"); // deletes entry with key "c"
+		_, found := m["c"];
+		assert(!found);
 
 		fmt.println(m);
+		clear(m);
+		fmt.println(m);
+
+		// NOTE: Fixed size maps are planned but we have not yet implemented
+		// them as we have had no need for them as of yet
 	}
 
 	{
-		x: [dynamic]f64;
-		reserve(x, 16);
-		defer free(x);
-		append(x, 2_000_000.500_000, 3, 5, 7);
+		Vector3 :: struct{x, y, z: f32};
+		Quaternion :: struct{x, y, z, w: f32};
 
-		for p, i in x {
-			if i > 0 { fmt.print(", "); }
-			fmt.print(p);
-		}
-		fmt.println();
-	}
+		Entity :: union {
+			// Common Fields
+			id:             u64,
+			name:           string,
+			using position: Vector3,
+			orientation:    Quaternion,
+			flags:          u32,
 
-	{
-		x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
-		defer free(x);
-		fmt.println(x);
-	}
+			// 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 */
+		};
 
-	{
-		Vec3 :: [vector 3]f32;
+		entity.name = "Frank";
+		entity.position = Vector3{1, 4, 9};
 
-		x := Vec3{1, 2, 3};
-		y := Vec3{4, 5, 6};
-		fmt.println(x < y);
-		fmt.println(x + y);
-		fmt.println(x - y);
-		fmt.println(x * y);
-		fmt.println(x / y);
+		using Entity;
+		match e in entity {
+		case Frog:
+			fmt.println("Ribbit");
+		case Door:
+			fmt.println("Creak");
+		case Map:
+			fmt.println("Rustle");
+		default:
+			fmt.println("Just a normal entity");
+		}
 
-		for i in x {
-			fmt.println(i);
+		if frog, ok := union_cast(Frog)entity; ok {
+			fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
 		}
 
-		compile_assert(size_of([vector 7]bool) == size_of([7]bool));
-		compile_assert(size_of([vector 7]i32) == size_of([7]i32));
-		// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
+		// Panics if not the correct type
+		frog: Frog;
+		frog = union_cast(Frog)entity;
+		frog, _ = union_cast(Frog)entity; // ignore error and force cast
 	}
 }
-}
 

+ 3 - 3
core/fmt.odin

@@ -295,15 +295,15 @@ bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
 
 sprint :: proc(buf: []byte, args: ..any) -> string {
 	count := bprint(^buf, ..args);
-	return cast(string)buf;
+	return cast(string)buf[..count];
 }
 sprintln :: proc(buf: []byte, args: ..any) -> string {
 	count := bprintln(^buf, ..args);
-	return cast(string)buf;
+	return cast(string)buf[..count];
 }
 sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
 	count := bprintf(^buf, fmt, ..args);
-	return cast(string)buf;
+	return cast(string)buf[..count];
 }
 
 

+ 1 - 3
core/strconv.odin

@@ -30,9 +30,7 @@ append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
 append_int :: proc(buf: []byte, i: i64, base: int) -> string {
 	return append_bits(buf, cast(u64)i, base, true, 8*size_of(int), digits, 0);
 }
-itoa :: proc(buf: []byte, i: int) -> string {
-	return append_int(buf, cast(i64)i, 10);
-}
+itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, cast(i64)i, 10); }
 
 append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
 	return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size);

+ 2 - 8
src/array.c

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

+ 1 - 6
src/check_decl.c

@@ -12,6 +12,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 			gbString expr_str = expr_to_string(operand->expr);
 
 			// TODO(bill): is this a good enough error message?
+			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error_node(operand->expr,
 			      "Cannot assign builtin procedure `%s` in %.*s",
 			      expr_str,
@@ -276,12 +277,6 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
 			error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
 		}
 
-		// TODO(bill): Is this the best option? What about passing to external shit?!
-		// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
-		// 	error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
-		// 	proc_type->Proc.calling_convention = ProcCC_Odin;
-		// }
-
 		d->scope = c->context.scope;
 
 		GB_ASSERT(pd->body->kind == AstNode_BlockStmt);

+ 58 - 144
src/check_expr.c

@@ -272,6 +272,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 
 		if (operand->mode == Addressing_Builtin) {
 			// TODO(bill): is this a good enough error message?
+			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error_node(operand->expr,
 			           "Cannot assign builtin procedure `%s` in %.*s",
 			           expr_str,
@@ -379,7 +380,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 				Entity **found = map_entity_get(&entity_map, key);
 				if (found != NULL) {
 					Entity *e = *found;
-					// TODO(bill): Scope checking already checks the declaration
+					// NOTE(bill): Scope checking already checks the declaration but in many cases, this can happen so why not?
+					// This may be a little janky but it's not really that much of a problem
 					error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
 					error(e->token,   "\tpreviously declared");
 				} else {
@@ -604,16 +606,11 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
 		{
 			ast_node(fl, FieldList, f->list);
 
-			// TODO(bill): Just do a gb_memcopy here
 			// NOTE(bill): Copy the contents for the common fields for now
 			AstNodeArray list = {0};
 			array_init_count(&list, c->allocator, ut->fields.count+fl->list.count);
-			for (isize j = 0; j < ut->fields.count; j++) {
-				list.e[j] = ut->fields.e[j];
-			}
-			for (isize j = 0; j < fl->list.count; j++) {
-				list.e[j+ut->fields.count] = fl->list.e[j];
-			}
+			gb_memmove_array(list.e, ut->fields.e, ut->fields.count);
+			gb_memmove_array(list.e+ut->fields.count, fl->list.e, fl->list.count);
 
 			isize list_count = 0;
 			for_array(j, list) {
@@ -654,7 +651,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
 
 		HashKey key = hash_string(name_token.string);
 		if (map_entity_get(&entity_map, key) != NULL) {
-			// TODO(bill): Scope checking already checks the declaration
+			// NOTE(bill): Scope checking already checks the declaration
 			error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
 		} else {
 			map_entity_set(&entity_map, key, e);
@@ -1142,10 +1139,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
 		}
 		break;
 
-	case Entity_TypeName: {
+	case Entity_TypeName:
+		// NOTE(bill): Cyclical dependency checking is handled in the "type system" not here
 		o->mode = Addressing_Type;
-		// TODO(bill): Fix cyclical dependancy checker
-	} break;
+		break;
 
 	case Entity_Procedure:
 		o->mode = Addressing_Value;
@@ -1165,6 +1162,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
 		error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name));
 		return e;
 
+	case Entity_Label:
+		o->mode = Addressing_NoValue;
+		break;
+
 	case Entity_Nil:
 		o->mode = Addressing_Value;
 		break;
@@ -1691,7 +1692,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
 		if (s < 64) {
 			umax = (1ull << s) - 1ull;
 		} else {
-			// TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
+			// IMPORTANT TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
 			s = 64;
 		}
 		i64 imax = (1ll << (s-1ll));
@@ -2884,7 +2885,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 		operand->value = entity->Constant.value;
 		break;
 	case Entity_Variable:
-		// TODO(bill): This is the rule I need?
+		// TODO(bill): Is this the rule I need?
 		if (operand->mode == Addressing_Immutable) {
 			// Okay
 		} else if (sel.indirect || operand->mode != Addressing_Value) {
@@ -3073,9 +3074,11 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 	case BuiltinProc_clear: {
 		Type *type = operand->type;
-		if (!is_type_dynamic_array(type) && !is_type_map(type)) {
+		bool is_pointer = is_type_pointer(type);
+		type = base_type(type_deref(type));
+		if (!is_type_dynamic_array(type) && !is_type_map(type) && !is_type_slice(type)) {
 			gbString str = type_to_string(type);
-			error_node(operand->expr, "Expected a map or dynamic array, got `%s`", str);
+			error_node(operand->expr, "Invalid type for `clear`, got `%s`", str);
 			gb_string_free(str);
 			return false;
 		}
@@ -3107,14 +3110,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 
 		Type *elem = NULL;
-		Type *slice_elem = NULL;
 		if (is_type_dynamic_array(type)) {
-			// TODO(bill): Semi-memory leaks
 			elem = type->DynamicArray.elem;
 		} else {
 			elem = type->Slice.elem;
 		}
-		slice_elem = make_type_slice(c->allocator, elem);
+		Type *slice_elem = make_type_slice(c->allocator, elem);
 
 		Type *proc_type_params = make_type_tuple(c->allocator);
 		proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2);
@@ -3501,113 +3502,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		operand->mode = Addressing_Value;
 	} break;
 
-#if 0
-	case BuiltinProc_ptr_offset: {
-		// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error_node(call,
-			      "Expected a pointer to `ptr_offset`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		AstNode *offset = ce->args.e[1];
-		Operand op = {0};
-		check_expr(c, &op, offset);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		Type *offset_type = base_type(op.type);
-		if (!is_type_integer(offset_type)) {
-			error_node(op.expr, "Pointer offsets for `ptr_offset` must be an integer");
-			return false;
-		}
-
-		if (operand->mode == Addressing_Constant &&
-		    op.mode == Addressing_Constant) {
-			i64 ptr = operand->value.value_pointer;
-			i64 elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
-			ptr += elem_size * op.value.value_integer;
-			operand->value.value_pointer = ptr;
-		} else {
-			operand->mode = Addressing_Value;
-		}
-
-	} break;
-
-	case BuiltinProc_ptr_sub: {
-		// ptr_sub :: proc(a, b: ^T) -> int
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error_node(call,
-			      "Expected a pointer to `ptr_add`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-		AstNode *offset = ce->args[1];
-		Operand op = {0};
-		check_expr(c, &op, offset);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		if (!is_type_pointer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error_node(call,
-			      "Expected a pointer to `ptr_add`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (base_type(op.type) == t_rawptr) {
-			error_node(call,
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		if (!are_types_identical(operand->type, op.type)) {
-			gbString a = type_to_string(operand->type);
-			gbString b = type_to_string(op.type);
-			defer (gb_string_free(a));
-			defer (gb_string_free(b));
-			error_node(op.expr,
-			      "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
-			return false;
-		}
-
-		operand->type = t_int;
-
-		if (operand->mode == Addressing_Constant &&
-		    op.mode == Addressing_Constant) {
-			u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
-			u8 *ptr_b = cast(u8 *)op.value.value_pointer;
-			isize elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem);
-			operand->value = exact_value_integer((ptr_a - ptr_b) / elem_size);
-		} else {
-			operand->mode = Addressing_Value;
-		}
-	} break;
-#endif
-
 	case BuiltinProc_slice_ptr: {
 		// slice_ptr :: proc(a: ^T, len: int) -> []T
+		// slice_ptr :: proc(a: ^T, len, cap: int) -> []T
 		// ^T cannot be rawptr
 		Type *ptr_type = base_type(operand->type);
 		if (!is_type_pointer(ptr_type)) {
@@ -3625,21 +3522,28 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			return false;
 		}
 
-		AstNode *len = ce->args.e[1];
+		isize arg_count = ce->args.count;
+		if (arg_count < 2 || 3 < arg_count) {
+			error_node(ce->args.e[0], "`slice_ptr` expects 2 or 3 arguments, found %td", arg_count);
+			// NOTE(bill): Return the correct type to reduce errors
+		} else {
+			// If any are constant
+			i64 sizes[2] = {0};
+			isize size_count = 0;
+			for (isize i = 1; i < arg_count; i++) {
+				i64 val = 0;
+				bool ok = check_index_value(c, ce->args.e[i], -1, &val);
+				if (ok && val >= 0) {
+					GB_ASSERT(size_count < gb_count_of(sizes));
+					sizes[size_count++] = val;
+				}
+			}
 
-		Operand op = {0};
-		check_expr(c, &op, len);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		if (!is_type_integer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			error_node(call,
-			      "Length for `slice_ptr` must be an integer, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
+			if (size_count == 2 && sizes[0] > sizes[1]) {
+				error_node(ce->args.e[1], "`slice_ptr` count and capacity are swapped");
+				// No need quit
+			}
 		}
-
 		operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
 		operand->mode = Addressing_Value;
 	} break;
@@ -4491,13 +4395,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case_end;
 
 	case_ast_node(te, TernaryExpr, node);
-		if (c->proc_stack.count == 0) {
-			error_node(node, "A ternary expression is only allowed within a procedure");
-			goto error;
-		}
-		Operand operand = {Addressing_Invalid};
-		check_expr(c, &operand, te->cond);
-		if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
+		Operand cond = {Addressing_Invalid};
+		check_expr(c, &cond, te->cond);
+		if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
 			error_node(te->cond, "Non-boolean condition in if expression");
 		}
 
@@ -4539,6 +4439,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		o->type = x.type;
 		o->mode = Addressing_Value;
+
+		if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) &&
+		    x.mode == Addressing_Constant &&
+		    y.mode == Addressing_Constant) {
+
+			o->mode = Addressing_Constant;
+
+			if (cond.value.value_bool) {
+				o->value = x.value;
+			} else {
+				o->value = y.value;
+			}
+		}
+
 	case_end;
 
 	case_ast_node(cl, CompoundLit, node);

+ 99 - 37
src/check_stmt.c

@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
 	return rhs->type;
 }
 
-bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
-	if (is_type_pointer(type)) {
-		*is_union_ptr = is_type_union(type_deref(type));
-		return *is_union_ptr;
+typedef enum MatchTypeKind {
+	MatchType_Invalid,
+	MatchType_Union,
+	MatchType_Any,
+} MatchTypeKind;
+
+MatchTypeKind check_valid_type_match_type(Type *type) {
+	type = type_deref(type);
+	if (is_type_union(type)) {
+		return MatchType_Union;
 	}
 	if (is_type_any(type)) {
-		*is_any = true;
-		return *is_any;
+		return MatchType_Any;
 	}
-	return false;
+	return MatchType_Invalid;
 }
 
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
@@ -385,6 +390,47 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
 	}
 }
 
+void check_label(Checker *c, AstNode *label) {
+	if (label == NULL) {
+		return;
+	}
+	ast_node(l, Label, label);
+	if (l->name->kind != AstNode_Ident) {
+		error_node(l->name, "A label's name must be an identifier");
+		return;
+	}
+	String name = l->name->Ident.string;
+	if (str_eq(name, str_lit("_"))) {
+		error_node(l->name, "A label's name cannot be a blank identifier");
+		return;
+	}
+
+
+	if (c->proc_stack.count == 0) {
+		error_node(l->name, "A label is only allowed within a procedure");
+		return;
+	}
+	GB_ASSERT(c->context.decl != NULL);
+
+	bool ok = true;
+	for_array(i, c->context.decl->labels) {
+		BlockLabel bl = c->context.decl->labels.e[i];
+		if (str_eq(bl.name, name)) {
+			error_node(label, "Duplicate label with the name `%.*s`", LIT(name));
+			ok = false;
+			break;
+		}
+	}
+
+	Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label);
+	add_entity(c, c->context.scope, l->name, e);
+
+	if (ok) {
+		BlockLabel bl = {name, label};
+		array_add(&c->context.decl->labels, bl);
+	}
+}
+
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
@@ -475,9 +521,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				return;
 			}
 
-			// TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just
-			// leave it?
-
 			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
 
 			// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
@@ -515,7 +558,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
 				return;
 			}
-			// TODO(bill): Check if valid assignment operator
 			Operand operand = {Addressing_Invalid};
 			AstNode binary_expr = {AstNode_BinaryExpr};
 			ast_node(be, BinaryExpr, &binary_expr);
@@ -580,7 +622,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 		if (c->context.in_defer) {
 			error(rs->token, "You cannot `return` within a defer statement");
-			// TODO(bill): Should I break here?
 			break;
 		}
 
@@ -619,7 +660,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 	case_ast_node(fs, ForStmt, node);
 		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
+
 		check_open_scope(c, node);
+		check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
 
 		if (fs->init != NULL) {
 			check_stmt(c, fs->init, 0);
@@ -648,6 +691,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
 		check_open_scope(c, node);
 
+		check_label(c, rs->label);
+
 		Type *val = NULL;
 		Type *idx = NULL;
 		Entity *entities[2] = {0};
@@ -718,7 +763,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				case Token_Ellipsis: op = Token_Lt; break;
 				default: error(ie->op, "Invalid range operator"); break;
 				}
-				bool ok = compare_exact_values(Token_Lt, a, b);
+				bool ok = compare_exact_values(op, a, b);
 				if (!ok) {
 					// TODO(bill): Better error message
 					error(ie->op, "Invalid interval range");
@@ -982,8 +1027,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		mod_flags |= Stmt_BreakAllowed;
 		check_open_scope(c, node);
 
-		bool is_union_ptr = false;
-		bool is_any = false;
+		MatchTypeKind match_type_kind = MatchType_Invalid;
 
 		if (ms->tag->kind != AstNode_AssignStmt) {
 			error_node(ms->tag, "Expected an `in` assignment for this type match statement");
@@ -1005,7 +1049,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 		check_expr(c, &x, rhs);
 		check_assignment(c, &x, NULL, str_lit("type match expression"));
-		if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
+		match_type_kind = check_valid_type_match_type(x.type);
+		if (check_valid_type_match_type(x.type) == MatchType_Invalid) {
 			gbString str = type_to_string(x.type);
 			error_node(x.expr,
 			           "Invalid type for this type match expression, got `%s`", str);
@@ -1062,14 +1107,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			// TODO(bill): Make robust
 			Type *bt = base_type(type_deref(x.type));
 
-
 			AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
 			Type *case_type = NULL;
 			if (type_expr != NULL) { // Otherwise it's a default expression
 				Operand y = {0};
 				check_expr_or_type(c, &y, type_expr);
 
-				if (is_union_ptr) {
+				if (match_type_kind == MatchType_Union) {
 					GB_ASSERT(is_type_union(bt));
 					bool tag_type_found = false;
 					for (isize i = 0; i < bt->Record.variant_count; i++) {
@@ -1086,7 +1130,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 						continue;
 					}
 					case_type = y.type;
-				} else if (is_any) {
+				} else if (match_type_kind == MatchType_Any) {
 					case_type = y.type;
 				} else {
 					GB_PANIC("Unknown type to type match statement");
@@ -1110,11 +1154,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 			check_open_scope(c, stmt);
 			if (case_type == NULL) {
-				if (is_union_ptr) {
-					case_type = type_deref(x.type);
-				} else {
-					case_type = x.type;
-				}
+				case_type = type_deref(x.type);
 			}
 
 			add_type_info_type(c, case_type);
@@ -1122,7 +1162,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			{
 				// NOTE(bill): Dummy type
 				Type *tt = case_type;
-				if (is_union_ptr) {
+				if (is_type_pointer(x.type)) {
 					tt = make_type_pointer(c->allocator, case_type);
 					add_type_info_type(c, tt);
 				}
@@ -1173,20 +1213,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
 			break;
 		}
+
+		if (bs->label != NULL) {
+			if (bs->label->kind != AstNode_Ident) {
+				error_node(bs->label, "A branch statement's label name must be an identifier");
+				return;
+			}
+			AstNode *ident = bs->label;
+			String name = ident->Ident.string;
+			Entity *e = scope_lookup_entity(c->context.scope, name);
+			if (e == NULL) {
+				error_node(ident, "Undeclared label name: %.*s", LIT(name));
+				return;
+			}
+			add_entity_use(c, ident, e);
+			if (e->kind != Entity_Label) {
+				error_node(ident, "`%.*s` is not a label", LIT(name));
+				return;
+			}
+		}
+
 	case_end;
 
 	case_ast_node(us, UsingStmt, node);
-		switch (us->node->kind) {
-		default:
-			// TODO(bill): Better error message for invalid using statement
-			error(us->token, "Invalid `using` statement");
-			break;
-		case_ast_node(es, ExprStmt, us->node);
-			// TODO(bill): Allow for just a LHS expression list rather than this silly code
+		if (us->list.count == 0) {
+			error(us->token, "Empty `using` list");
+			return;
+		}
+		for_array(i, us->list) {
+			AstNode *expr = unparen_expr(us->list.e[0]);
 			Entity *e = NULL;
 
 			bool is_selector = false;
-			AstNode *expr = unparen_expr(es->expr);
 			if (expr->kind == AstNode_Ident) {
 				String name = expr->Ident.string;
 				e = scope_lookup_entity(c->context.scope, name);
@@ -1194,11 +1252,14 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				Operand o = {0};
 				e = check_selector(c, &o, expr, NULL);
 				is_selector = true;
+			} else if (expr->kind == AstNode_Implicit) {
+				error(us->token, "`using` applied to an implicit value");
+				continue;
 			}
 
 			if (e == NULL) {
 				error(us->token, "`using` applied to an unknown entity");
-				return;
+				continue;
 			}
 
 			switch (e->kind) {
@@ -1296,6 +1357,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				error(us->token, "`using` cannot be applied to `nil`");
 				break;
 
+			case Entity_Label:
+				error(us->token, "`using` cannot be applied to a label");
+				break;
+
 			case Entity_Invalid:
 				error(us->token, "`using` cannot be applied to an invalid entity");
 				break;
@@ -1303,13 +1368,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			default:
 				GB_PANIC("TODO(bill): `using` other expressions?");
 			}
-		case_end;
-
 		}
 	case_end;
 
 
-
 	case_ast_node(pa, PushAllocator, node);
 		Operand op = {0};
 		check_expr(c, &op, pa->expr);

+ 19 - 7
src/checker.c

@@ -98,7 +98,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 
 	// {STR_LIT("ptr_offset"),       2, false, Expr_Expr},
 	// {STR_LIT("ptr_sub"),          2, false, Expr_Expr},
-	{STR_LIT("slice_ptr"),        2, false,  Expr_Expr},
+	{STR_LIT("slice_ptr"),        2, true,   Expr_Expr},
 	{STR_LIT("slice_to_bytes"),   1, false,  Expr_Stmt},
 
 	{STR_LIT("min"),              2, false, Expr_Expr},
@@ -163,6 +163,11 @@ bool is_operand_nil(Operand o) {
 }
 
 
+typedef struct BlockLabel {
+	String   name;
+	AstNode *label; // AstNode_Label
+} BlockLabel;
+
 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
 typedef struct DeclInfo {
 	Scope *scope;
@@ -175,16 +180,19 @@ typedef struct DeclInfo {
 	AstNode *proc_lit; // AstNode_ProcLit
 
 	MapBool deps; // Key: Entity *
+	Array(BlockLabel) labels;
 } DeclInfo;
 
 // ProcedureInfo stores the information needed for checking a procedure
+
+
 typedef struct ProcedureInfo {
-	AstFile * file;
-	Token     token;
-	DeclInfo *decl;
-	Type *    type; // Type_Procedure
-	AstNode * body; // AstNode_BlockStmt
-	u32       tags;
+	AstFile *             file;
+	Token                 token;
+	DeclInfo *            decl;
+	Type *                type; // Type_Procedure
+	AstNode *             body; // AstNode_BlockStmt
+	u32                   tags;
 } ProcedureInfo;
 
 // ExprInfo stores information used for "untyped" expressions
@@ -320,6 +328,7 @@ typedef Array(DelayedEntity) DelayedEntities;
 void init_declaration_info(DeclInfo *d, Scope *scope) {
 	d->scope = scope;
 	map_bool_init(&d->deps, heap_allocator());
+	array_init(&d->labels,  heap_allocator());
 }
 
 DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
@@ -455,6 +464,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
 		if (found) {
 			Entity *e = *found;
 			if (gone_thru_proc) {
+				if (e->kind == Entity_Label) {
+					continue;
+				}
 				if (e->kind == Entity_Variable &&
 				    !e->scope->is_file &&
 				    !e->scope->is_global) {

+ 15 - 1
src/entity.c

@@ -15,12 +15,13 @@ typedef struct Type Type;
 	ENTITY_KIND(LibraryName) \
 	ENTITY_KIND(Alias) \
 	ENTITY_KIND(Nil) \
-	ENTITY_KIND(Count)
+	ENTITY_KIND(Label)
 
 typedef enum EntityKind {
 #define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
 	ENTITY_KINDS
 #undef ENTITY_KIND
+	Entity_Count,
 } EntityKind;
 
 String const entity_strings[] = {
@@ -100,6 +101,10 @@ struct Entity {
 			Entity *original;
 		} Alias;
 		i32 Nil;
+		struct {
+			String name;
+			AstNode *node;
+		} Label;
 	};
 };
 
@@ -235,6 +240,15 @@ Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
 	return entity;
 }
 
+Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
+                          AstNode *node) {
+	Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
+	entity->Label.node = node;
+	return entity;
+}
+
+
+
 Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
 	token.string = str_lit("_");
 	return make_entity_variable(a, scope, token, NULL, false);

+ 21 - 15
src/exact_value.c

@@ -82,6 +82,7 @@ ExactValue exact_value_integer_from_string(String string) {
 		case 'b': base = 2;  has_prefix = true; break;
 		case 'o': base = 8;  has_prefix = true; break;
 		case 'd': base = 10; has_prefix = true; break;
+		case 'z': base = 12; has_prefix = true; break;
 		case 'x': base = 16; has_prefix = true; break;
 		}
 	}
@@ -100,14 +101,10 @@ ExactValue exact_value_integer_from_string(String string) {
 			continue;
 		}
 		i64 v = 0;
-		if (gb_char_is_digit(r)) {
-			v = r - '0';
-		} else if (gb_char_is_hex_digit(r)) {
-			v = gb_hex_digit_to_int(r);
-		} else {
+		v = digit_value(r);
+		if (v >= base) {
 			break;
 		}
-
 		result *= base;
 		result += v;
 	}
@@ -137,10 +134,10 @@ ExactValue exact_value_float_from_string(String string) {
 		if (r == '_') {
 			continue;
 		}
-		if (!gb_char_is_digit(r)) {
+		i64 v = digit_value(r);
+		if (v >= 10) {
 			break;
 		}
-		i64 v = r - '0';
 		value *= 10.0;
 		value += v;
 	}
@@ -153,29 +150,38 @@ ExactValue exact_value_float_from_string(String string) {
 			if (r == '_') {
 				continue;
 			}
-			if (!gb_char_is_digit(r)) {
+			i64 v = digit_value(r);
+			if (v >= 10) {
 				break;
 			}
-			value += (r-'0')/pow10;
+			value += v/pow10;
 			pow10 *= 10.0;
 		}
 	}
 
-	f64 frac = 0;
+	bool frac = false;
 	f64 scale = 1.0;
 	if ((str[i] == 'e') || (str[i] == 'E')) {
 		i++;
 
 		if (str[i] == '-') {
-			frac = 1;
+			frac = true;
 			i++;
 		} else if (str[i] == '+') {
 			i++;
 		}
 
-		u32 exp;
-		for (exp = 0; gb_char_is_digit(str[i]); i++) {
-			exp = exp * 10 + (str[i]-'0');
+		u32 exp = 0;
+		for (; i < len; i++) {
+			Rune r = cast(Rune)str[i];
+			if (r == '_') {
+				continue;
+			}
+			u32 d = cast(u32)digit_value(r);
+			if (d >= 10) {
+				break;
+			}
+			exp = exp * 10 + d;
 		}
 		if (exp > 308) exp = 308;
 

+ 4 - 0
src/gb/gb.h

@@ -811,6 +811,10 @@ GB_DEF void const *gb_memrchr   (void const *data, u8 byte_value, isize size);
 #define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src), gb_size_of(*(dst))*(count))
 #endif
 
+#ifndef gb_memmove_array
+#define gb_memmove_array(dst, src, count) gb_memmove((dst), (src), gb_size_of(*(dst))*(count))
+#endif
+
 // NOTE(bill): Very similar to doing `*cast(T *)(&u)`
 #ifndef GB_BIT_CAST
 #define GB_BIT_CAST(dest, source) do { \

+ 184 - 76
src/ir.c

@@ -91,7 +91,7 @@ typedef enum irDeferKind {
 
 typedef struct irDefer {
 	irDeferKind kind;
-	isize         scope_index;
+	isize       scope_index;
 	irBlock *   block;
 	union {
 		AstNode *stmt;
@@ -100,32 +100,41 @@ typedef struct irDefer {
 	};
 } irDefer;
 
+
+typedef struct irBranchBlocks {
+	AstNode *label;
+	irBlock *break_;
+	irBlock *continue_;
+} irBranchBlocks;
+
+
 struct irProcedure {
-	irProcedure *        parent;
-	Array(irProcedure *) children;
-
-	Entity *             entity;
-	irModule *           module;
-	String               name;
-	Type *               type;
-	AstNode *            type_expr;
-	AstNode *            body;
-	u64                  tags;
-
-	irValueArray         params;
-	Array(irDefer)       defer_stmts;
-	Array(irBlock *)     blocks;
-	i32                  scope_index;
-	irBlock *            decl_block;
-	irBlock *            entry_block;
-	irBlock *            curr_block;
-	irTargetList *       target_list;
-	irValueArray         referrers;
-
-
-	i32                  local_count;
-	i32                  instr_count;
-	i32                  block_count;
+	irProcedure *         parent;
+	Array(irProcedure *)  children;
+
+	Entity *              entity;
+	irModule *            module;
+	String                name;
+	Type *                type;
+	AstNode *             type_expr;
+	AstNode *             body;
+	u64                   tags;
+
+	irValueArray          params;
+	Array(irDefer)        defer_stmts;
+	Array(irBlock *)      blocks;
+	i32                   scope_index;
+	irBlock *             decl_block;
+	irBlock *             entry_block;
+	irBlock *             curr_block;
+	irTargetList *        target_list;
+	irValueArray          referrers;
+
+	Array(irBranchBlocks) branch_blocks;
+
+	i32                   local_count;
+	i32                   instr_count;
+	i32                   block_count;
 };
 
 #define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
@@ -1328,10 +1337,10 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na
 irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) {
 #if 1
 	// NOTE(bill): Sanity check
-	Type *a = core_type(type_deref(ir_type(address)));
-	Type *b = core_type(ir_type(value));
+	Type *a = type_deref(ir_type(address));
+	Type *b = ir_type(value);
 	if (!is_type_untyped(b)) {
-		GB_ASSERT_MSG(are_types_identical(a, b), "%s %s", type_to_string(a), type_to_string(b));
+		GB_ASSERT_MSG(are_types_identical(core_type(a), core_type(b)), "%s %s", type_to_string(a), type_to_string(b));
 	}
 #endif
 	return ir_emit(p, ir_instr_store(p, address, value));
@@ -1801,8 +1810,8 @@ irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index) {
 
 irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) {
 	Type *t = ir_type(u);
-	GB_ASSERT(is_type_pointer(t) &&
-	          is_type_union(type_deref(t)));
+	GB_ASSERT_MSG(is_type_pointer(t) &&
+	              is_type_union(type_deref(t)), "%s", type_to_string(t));
 	irValue *tag_ptr = ir_emit(proc, ir_instr_union_tag_ptr(proc, u));
 	Type *tpt = ir_type(tag_ptr);
 	GB_ASSERT(is_type_pointer(tpt));
@@ -1948,8 +1957,9 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
 }
 
 
-irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selection sel) {
+irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) {
 	GB_ASSERT(sel.index.count > 0);
+	Type *type = type_deref(ir_type(e));
 
 	for_array(i, sel.index) {
 		i32 index = cast(i32)sel.index.e[i];
@@ -1958,7 +1968,7 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec
 			e = ir_emit_load(proc, e);
 			e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
 		}
-		type = base_type(type);
+		type = core_type(type);
 
 
 		if (is_type_raw_union(type)) {
@@ -2005,7 +2015,7 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec
 			case 2: e = ir_emit_struct_ep(proc, e, 3); break; // allocator
 			}
 		} else {
-			GB_PANIC("un-gep-able type");
+			GB_PANIC("un-gep-able type %s", type_to_string(type));
 		}
 	}
 
@@ -2013,8 +2023,9 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec
 }
 
 
-irValue *ir_emit_deep_field_ev(irProcedure *proc, Type *type, irValue *e, Selection sel) {
+irValue *ir_emit_deep_field_ev(irProcedure *proc, irValue *e, Selection sel) {
 	GB_ASSERT(sel.index.count > 0);
+	Type *type = ir_type(e);
 
 	for_array(i, sel.index) {
 		i32 index = cast(i32)sel.index.e[i];
@@ -2359,7 +2370,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 					if (src_is_ptr) {
 						value = ir_emit_load(proc, value);
 					}
-					return ir_emit_deep_field_ev(proc, sb, value, sel);
+					return ir_emit_deep_field_ev(proc, value, sel);
 				}
 			}
 		}
@@ -3368,8 +3379,13 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 
 				case BuiltinProc_clear: {
 					ir_emit_comment(proc, str_lit("clear"));
-					irValue *ptr = ir_build_addr(proc, ce->args.e[0]).addr;
-					Type *t = base_type(type_deref(ir_type(ptr)));
+					Type *original_type = type_of_expr(proc->module->info, ce->args.e[0]);
+					irAddr addr = ir_build_addr(proc, ce->args.e[0]);
+					irValue *ptr = addr.addr;
+					if (is_type_pointer(original_type)) {
+						ptr = ir_addr_load(proc, addr);
+					}
+					Type *t = base_type(type_deref(original_type));
 					if (is_type_dynamic_array(t)) {
 						irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1);
 						ir_emit_store(proc, count_ptr, v_zero);
@@ -3378,6 +3394,9 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 						irValue *ea = ir_emit_struct_ep(proc, ptr, 1);
 						ir_emit_store(proc, ir_emit_struct_ep(proc, ha, 1), v_zero);
 						ir_emit_store(proc, ir_emit_struct_ep(proc, ea, 1), v_zero);
+					} else if (is_type_slice(t)) {
+						irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1);
+						ir_emit_store(proc, count_ptr, v_zero);
 					} else {
 						GB_PANIC("TODO(bill): ir clear for `%s`", type_to_string(t));
 					}
@@ -3627,10 +3646,15 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 					irValue *ptr = ir_build_expr(proc, ce->args.e[0]);
 					irValue *count = ir_build_expr(proc, ce->args.e[1]);
 					count = ir_emit_conv(proc, count, t_int);
+					irValue *capacity = count;
+					if (ce->args.count > 2) {
+						capacity = ir_build_expr(proc, ce->args.e[2]);
+						capacity = ir_emit_conv(proc, capacity, t_int);
+					}
 
 					Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ir_type(ptr)));
 					irValue *slice = ir_add_local_generated(proc, slice_type);
-					ir_fill_slice(proc, slice, ptr, count, count);
+					ir_fill_slice(proc, slice, ptr, count, capacity);
 					return ir_emit_load(proc, slice);
 				} break;
 
@@ -3809,7 +3833,8 @@ irValue *ir_get_using_variable(irProcedure *proc, Entity *e) {
 		v = ir_build_addr(proc, e->using_expr).addr;
 	}
 	GB_ASSERT(v != NULL);
-	return ir_emit_deep_field_gep(proc, parent->type, v, sel);
+	GB_ASSERT(parent->type == type_deref(ir_type(v)));
+	return ir_emit_deep_field_gep(proc, v, sel);
 }
 
 // irValue *ir_add_using_variable(irProcedure *proc, Entity *e) {
@@ -3925,7 +3950,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			GB_ASSERT(sel.entity != NULL);
 
 			irValue *a = ir_build_addr(proc, se->expr).addr;
-			a = ir_emit_deep_field_gep(proc, type, a, sel);
+			a = ir_emit_deep_field_gep(proc, a, sel);
 			return ir_addr(a);
 		} else {
 			Type *type = base_type(type_of_expr(proc->module->info, se->expr));
@@ -3937,7 +3962,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			GB_ASSERT(sel.entity != NULL);
 
 			irValue *a = ir_build_addr(proc, se->expr).addr;
-			a = ir_emit_deep_field_gep(proc, type, a, sel);
+			a = ir_emit_deep_field_gep(proc, a, sel);
 			return ir_addr(a);
 		}
 	case_end;
@@ -4032,7 +4057,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			if (using_field != NULL) {
 				Selection sel = lookup_field(a, t, using_field->token.string, false);
 				irValue *e = ir_build_addr(proc, ie->expr).addr;
-				using_addr = ir_emit_deep_field_gep(proc, t, e, sel);
+				using_addr = ir_emit_deep_field_gep(proc, e, sel);
 
 				t = using_field->type;
 			}
@@ -4817,6 +4842,44 @@ void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type
 	if (done_) *done_ = done;
 }
 
+void ir_set_label_blocks(irProcedure *proc, AstNode *label, irBlock *break_, irBlock *continue_) {
+	if (label == NULL) {
+		return;
+	}
+	GB_ASSERT(label->kind == AstNode_Label);
+
+
+	for_array(i, proc->branch_blocks) {
+		irBranchBlocks *b = &proc->branch_blocks.e[i];
+		GB_ASSERT(b->label != NULL && label != NULL);
+		GB_ASSERT(b->label->kind == AstNode_Label);
+		if (b->label == label) {
+			b->break_    = break_;
+			b->continue_ = continue_;
+			return;
+		}
+	}
+
+	GB_PANIC("ir_set_label_blocks: Unreachable");
+}
+
+irBranchBlocks ir_lookup_branch_blocks(irProcedure *proc, AstNode *ident) {
+	GB_ASSERT(ident->kind == AstNode_Ident);
+	Entity **found = map_entity_get(&proc->module->info->uses, hash_pointer(ident));
+	GB_ASSERT(found != NULL);
+	Entity *e = *found;
+	GB_ASSERT(e->kind == Entity_Label);
+	for_array(i, proc->branch_blocks) {
+		irBranchBlocks *b = &proc->branch_blocks.e[i];
+		if (b->label == e->Label.node) {
+			return *b;
+		}
+	}
+
+	GB_PANIC("Unreachable");
+	return (irBranchBlocks){0};
+}
+
 
 void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 	switch (node->kind) {
@@ -4824,9 +4887,11 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 	case_end;
 
 	case_ast_node(us, UsingStmt, node);
-		AstNode *decl = unparen_expr(us->node);
-		if (decl->kind == AstNode_ValueDecl) {
-			ir_build_stmt(proc, decl);
+		for_array(i, us->list) {
+			AstNode *decl = unparen_expr(us->list.e[i]);
+			if (decl->kind == AstNode_ValueDecl) {
+				ir_build_stmt(proc, decl);
+			}
 		}
 	case_end;
 
@@ -4944,7 +5009,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 
 
 						irValue *value = ir_value_procedure(proc->module->allocator,
-						                                           proc->module, e, e->type, pd->type, pd->body, name);
+						                                    proc->module, e, e->type, pd->type, pd->body, name);
 
 						value->Proc.tags = pd->tags;
 						value->Proc.parent = proc;
@@ -5172,6 +5237,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 
 	case_ast_node(fs, ForStmt, node);
 		ir_emit_comment(proc, str_lit("ForStmt"));
+
 		if (fs->init != NULL) {
 			irBlock *init = ir_new_block(proc, node, "for.init");
 			ir_emit_jump(proc, init);
@@ -5188,6 +5254,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		if (fs->post != NULL) {
 			post = ir_new_block(proc, node, "for.post");
 		}
+
+
 		ir_emit_jump(proc, loop);
 		ir_start_block(proc, loop);
 
@@ -5196,6 +5264,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 			ir_start_block(proc, body);
 		}
 
+		ir_set_label_blocks(proc, fs->label, done, post);
 		ir_push_target_list(proc, done, post, NULL);
 
 		ir_open_scope(proc);
@@ -5330,6 +5399,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 			ir_addr_store(proc, idx_addr, index);
 		}
 
+		ir_set_label_blocks(proc, rs->label, done, loop);
 		ir_push_target_list(proc, done, loop, NULL);
 
 		ir_open_scope(proc);
@@ -5442,16 +5512,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		AstNode *rhs = as->rhs.e[0];
 
 		irValue *parent = ir_build_expr(proc, rhs);
-		bool is_union_ptr = false;
-		bool is_any = false;
-		GB_ASSERT(check_valid_type_match_type(ir_type(parent), &is_union_ptr, &is_any));
+		bool is_parent_ptr = is_type_pointer(ir_type(parent));
+		MatchTypeKind match_type_kind = check_valid_type_match_type(ir_type(parent));
+		GB_ASSERT(match_type_kind != MatchType_Invalid);
 
 		irValue *tag_index = NULL;
 		irValue *union_data = NULL;
-		if (is_union_ptr) {
+		if (match_type_kind == MatchType_Union) {
+			if (!is_parent_ptr) {
+				parent = ir_address_from_load_or_generate_local(proc, parent);
+			}
 			ir_emit_comment(proc, str_lit("get union's tag"));
 			tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent));
 			union_data = ir_emit_conv(proc, parent, t_rawptr);
+		} else if (match_type_kind == MatchType_Any) {
+			if (!is_parent_ptr) {
+				parent = ir_address_from_load_or_generate_local(proc, parent);
+			}
 		}
 
 		irBlock *start_block = ir_new_block(proc, node, "type-match.case.first");
@@ -5478,7 +5555,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 			Type *tag_var_type = NULL;
 			if (str_eq(tag_var_name, str_lit("_"))) {
 				Type *t = type_of_expr(proc->module->info, cc->list.e[0]);
-				if (is_union_ptr) {
+				if (match_type_kind == MatchType_Union) {
 					t = make_type_pointer(proc->module->allocator, t);
 				}
 				tag_var_type = t;
@@ -5505,7 +5582,12 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 				} else {
 					tag_var = ir_add_local_generated(proc, tag_var_type);
 				}
-				ir_emit_store(proc, tag_var, parent);
+
+				if (!is_parent_ptr) {
+					ir_emit_store(proc, tag_var, ir_emit_load(proc, parent));
+				} else {
+					ir_emit_store(proc, tag_var, parent);
+				}
 				continue;
 			}
 			GB_ASSERT(cc->list.count == 1);
@@ -5513,7 +5595,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 			irBlock *body = ir_new_block(proc, clause, "type-match.case.body");
 
 
-			if (is_union_ptr) {
+			if (match_type_kind == MatchType_Union) {
 				Type *bt = type_deref(tag_var_type);
 				irValue *index = NULL;
 				Type *ut = base_type(type_deref(ir_type(parent)));
@@ -5534,20 +5616,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 					tag_var = ir_add_local_generated(proc, tag_var_type);
 				}
 
-
-				irValue *data_ptr = ir_emit_conv(proc, union_data, tag_var_type);
+				Type *bt_ptr = make_type_pointer(proc->module->allocator, bt);
+				irValue *data_ptr = ir_emit_conv(proc, union_data, bt_ptr);
+				if (!is_type_pointer(type_deref(ir_type(tag_var)))) {
+					data_ptr = ir_emit_load(proc, data_ptr);
+				}
 				ir_emit_store(proc, tag_var, data_ptr);
 
 				cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index);
-			} else if (is_any) {
+			} else if (match_type_kind == MatchType_Any) {
 				Type *type = tag_var_type;
-				irValue *any_data = ir_emit_struct_ev(proc, parent, 1);
+				irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 1));
 				irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type));
 				if (tag_var_entity != NULL) {
 					ir_module_add_value(proc->module, tag_var_entity, data);
 				}
 
-				irValue *any_ti  = ir_emit_struct_ev(proc, parent, 0);
+				irValue *any_ti  = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 0));
 				irValue *case_ti = ir_type_info(proc, type);
 				cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti);
 			} else {
@@ -5588,22 +5673,34 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 
 	case_ast_node(bs, BranchStmt, node);
 		irBlock *block = NULL;
-		switch (bs->token.kind) {
-		case Token_break:
-			for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->break_;
-			}
-			break;
-		case Token_continue:
-			for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->continue_;
+
+		if (bs->label != NULL) {
+			irBranchBlocks bb = ir_lookup_branch_blocks(proc, bs->label);
+			switch (bs->token.kind) {
+			case Token_break:    block = bb.break_;    break;
+			case Token_continue: block = bb.continue_; break;
+			case Token_fallthrough:
+				GB_PANIC("fallthrough cannot have a label");
+				break;
 			}
-			break;
-		case Token_fallthrough:
-			for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->fallthrough_;
+		} else {
+			switch (bs->token.kind) {
+			case Token_break:
+				for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
+					block = t->break_;
+				}
+				break;
+			case Token_continue:
+				for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
+					block = t->continue_;
+				}
+				break;
+			case Token_fallthrough:
+				for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
+					block = t->fallthrough_;
+				}
+				break;
 			}
-			break;
 		}
 		if (block != NULL) {
 			ir_emit_defer_stmts(proc, irDeferExit_Branch, block);
@@ -5692,9 +5789,20 @@ void ir_number_proc_registers(irProcedure *proc) {
 void ir_begin_procedure_body(irProcedure *proc) {
 	array_add(&proc->module->procs, proc);
 
-	array_init(&proc->blocks,      heap_allocator());
-	array_init(&proc->defer_stmts, heap_allocator());
-	array_init(&proc->children,    heap_allocator());
+	array_init(&proc->blocks,        heap_allocator());
+	array_init(&proc->defer_stmts,   heap_allocator());
+	array_init(&proc->children,      heap_allocator());
+	array_init(&proc->branch_blocks, heap_allocator());
+
+	DeclInfo **found = map_decl_info_get(&proc->module->info->entities, hash_pointer(proc->entity));
+	if (found != NULL) {
+		DeclInfo *decl = *found;
+		for_array(i, decl->labels) {
+			BlockLabel bl = decl->labels.e[i];
+			irBranchBlocks bb = {bl.label, NULL, NULL};
+			array_add(&proc->branch_blocks, bb);
+		}
+	}
 
 	proc->decl_block  = ir_new_block(proc, proc->type_expr, "decls");
 	ir_start_block(proc, proc->decl_block);

+ 95 - 55
src/parser.c

@@ -219,6 +219,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 	}) \
 	AST_NODE_KIND(ForStmt, "for statement", struct { \
 		Token    token; \
+		AstNode *label; \
 		AstNode *init; \
 		AstNode *cond; \
 		AstNode *post; \
@@ -226,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 	}) \
 	AST_NODE_KIND(RangeStmt, "range statement", struct { \
 		Token    token; \
+		AstNode *label; \
 		AstNode *value; \
 		AstNode *index; \
 		Token    in_token; \
@@ -249,10 +251,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 		AstNode *body;  \
 	}) \
 	AST_NODE_KIND(DeferStmt,  "defer statement",  struct { Token token; AstNode *stmt; }) \
-	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
+	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
 	AST_NODE_KIND(UsingStmt,  "using statement",  struct { \
 		Token token;   \
-		AstNode *node; \
+		AstNodeArray list; \
 	}) \
 	AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
 		Token string;     \
@@ -305,6 +307,10 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 		AstNode *cond;         \
 		bool is_system;        \
 	}) \
+	AST_NODE_KIND(Label, "label", struct { 	\
+		Token token; \
+		AstNode *name; \
+	}) \
 AST_NODE_KIND(_DeclEnd,   "", i32) \
 	AST_NODE_KIND(Field, "field", struct { \
 		AstNodeArray names;    \
@@ -499,6 +505,7 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_ValueDecl:      return ast_node_token(node->ValueDecl.names.e[0]);
 	case AstNode_ImportDecl:     return node->ImportDecl.token;
 	case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
+	case AstNode_Label:      return node->Label.token;
 
 
 	case AstNode_Field:
@@ -869,9 +876,10 @@ AstNode *ast_return_stmt(AstFile *f, Token token, AstNodeArray results) {
 }
 
 
-AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
+AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *label, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
 	AstNode *result = make_ast_node(f, AstNode_ForStmt);
 	result->ForStmt.token = token;
+	result->ForStmt.label = label;
 	result->ForStmt.init  = init;
 	result->ForStmt.cond  = cond;
 	result->ForStmt.post  = post;
@@ -879,8 +887,9 @@ AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast
 	return result;
 }
 
-AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) {
+AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *label, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) {
 	AstNode *result = make_ast_node(f, AstNode_RangeStmt);
+	result->RangeStmt.label = label;
 	result->RangeStmt.token = token;
 	result->RangeStmt.value = value;
 	result->RangeStmt.index = index;
@@ -924,19 +933,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
 	return result;
 }
 
-AstNode *ast_branch_stmt(AstFile *f, Token token) {
+AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) {
 	AstNode *result = make_ast_node(f, AstNode_BranchStmt);
 	result->BranchStmt.token = token;
+	result->BranchStmt.label = label;
 	return result;
 }
 
-AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) {
+AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) {
 	AstNode *result = make_ast_node(f, AstNode_UsingStmt);
 	result->UsingStmt.token = token;
-	result->UsingStmt.node  = node;
+	result->UsingStmt.list  = list;
 	return result;
 }
 
+
 AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
 	AstNode *result = make_ast_node(f, AstNode_AsmOperand);
 	result->AsmOperand.string  = string;
@@ -1138,6 +1149,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
 	return result;
 }
 
+AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) {
+	AstNode *result = make_ast_node(f, AstNode_Label);
+	result->Label.token = token;
+	result->Label.name  = name;
+	return result;
+}
+
 
 bool next_token(AstFile *f) {
 	Token prev = f->curr_token;
@@ -2335,7 +2353,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
 	return parse_body(f);
 }
 
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow);
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow);
 
 
 AstNode *parse_results(AstFile *f) {
@@ -2354,7 +2372,7 @@ AstNode *parse_results(AstFile *f) {
 
 	AstNode *list = NULL;
 	expect_token(f, Token_OpenParen);
-	list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen);
+	list = parse_field_list(f, NULL, 0, Token_CloseParen);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	return list;
 }
@@ -2365,7 +2383,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
 
 	Token proc_token = expect_token(f, Token_proc);
 	expect_token(f, Token_OpenParen);
-	params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
+	params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	results = parse_results(f);
 
@@ -2384,17 +2402,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
 	return ast_proc_type(f, proc_token, params, results, tags, cc);
 }
 
-bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
-	if (separator == Token_Semicolon) {
-		expect_semicolon(f, param);
-	} else {
-		if (!allow_token(f, separator)) {
-			return true;
-		}
-	}
-	return false;
-}
-
 AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
 	if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
 		Token tok = f->curr_token;
@@ -2505,7 +2512,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
 	return idents;
 }
 
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) {
+
+bool parse_expect_field_separator(AstFile *f, AstNode *param) {
+	Token token = f->curr_token;
+	if (allow_token(f, Token_Comma)) {
+		return true;
+	}
+	if (token.kind == Token_Semicolon) {
+		next_token(f);
+		error(f->curr_token, "Expected a comma, got a semicolon");
+		return true;
+	}
+	return false;
+}
+
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) {
+	TokenKind separator = Token_Comma;
 	Token start_token = f->curr_token;
 
 	AstNodeArray params = make_ast_node_array(f);
@@ -2543,7 +2565,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 		AstNode *param = ast_field(f, names, type, set_flags);
 		array_add(&params, param);
 
-		parse_expect_separator(f, separator, type);
+		parse_expect_field_separator(f, type);
 
 		while (f->curr_token.kind != follow &&
 		       f->curr_token.kind != Token_EOF) {
@@ -2561,7 +2583,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 			AstNode *param = ast_field(f, names, type, set_flags);
 			array_add(&params, param);
 
-			if (parse_expect_separator(f, separator, param)) {
+			if (!parse_expect_field_separator(f, param)) {
 				break;
 			}
 		}
@@ -2590,7 +2612,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 
 
 AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
-	return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace);
+	return parse_field_list(f, field_count_, flags, Token_CloseBrace);
 }
 
 AstNode *parse_type_or_ident(AstFile *f) {
@@ -2987,7 +3009,7 @@ AstNode *parse_return_stmt(AstFile *f) {
 // 	return ast_expr_stmt(f, ge);
 // }
 
-AstNode *parse_for_stmt(AstFile *f) {
+AstNode *parse_for_stmt(AstFile *f, AstNode *label) {
 	if (f->curr_proc == NULL) {
 		syntax_error(f->curr_token, "You cannot use a for statement in the file scope");
 		return ast_bad_stmt(f, f->curr_token, f->curr_token);
@@ -3051,11 +3073,11 @@ AstNode *parse_for_stmt(AstFile *f) {
 		if (cond->AssignStmt.rhs.count > 0) {
 			rhs = cond->AssignStmt.rhs.e[0];
 		}
-		return ast_range_stmt(f, token, value, index, in_token, rhs, body);
+		return ast_range_stmt(f, token, label, value, index, in_token, rhs, body);
 	}
 
 	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
-	return ast_for_stmt(f, token, init, cond, post, body);
+	return ast_for_stmt(f, token, label, init, cond, post, body);
 
 #if 0
 	Token token = expect_token(f, Token_for);
@@ -3262,7 +3284,7 @@ AstNode *parse_stmt(AstFile *f) {
 
 	case Token_if:     return parse_if_stmt(f);
 	case Token_when:   return parse_when_stmt(f);
-	case Token_for:    return parse_for_stmt(f);
+	case Token_for:    return parse_for_stmt(f, NULL);
 	case Token_match:  return parse_match_stmt(f);
 	case Token_defer:  return parse_defer_stmt(f);
 	case Token_asm:    return parse_asm_stmt(f);
@@ -3271,43 +3293,47 @@ AstNode *parse_stmt(AstFile *f) {
 
 	case Token_break:
 	case Token_continue:
-	case Token_fallthrough:
+	case Token_fallthrough: {
+		AstNode *label = NULL;
 		next_token(f);
-		s = ast_branch_stmt(f, token);
+		if (token.kind != Token_fallthrough &&
+		    f->curr_token.kind == Token_Ident) {
+			label = parse_ident(f);
+		}
+		s = ast_branch_stmt(f, token, label);
 		expect_semicolon(f, s);
 		return s;
+	}
 
 	case Token_using: {
 		// TODO(bill): Make using statements better
 		Token token = expect_token(f, Token_using);
-		AstNode *node = parse_stmt(f);
+		AstNodeArray list = parse_lhs_expr_list(f);
+		if (list.count == 0) {
+			syntax_error(token, "Illegal use of `using` statement");
+			expect_semicolon(f, NULL);
+			return ast_bad_stmt(f, token, f->curr_token);
+		}
 
-		switch (node->kind) {
-		case AstNode_ValueDecl:
-			if (!node->ValueDecl.is_var) {
+		if (f->curr_token.kind != Token_Colon) {
+			expect_semicolon(f, list.e[list.count-1]);
+			return ast_using_stmt(f, token, list);
+		}
+
+		AstNode *decl = parse_simple_stmt(f, false);
+		expect_semicolon(f, decl);
+
+		if (decl->kind == AstNode_ValueDecl) {
+			if (!decl->ValueDecl.is_var) {
 				syntax_error(token, "`using` may not be applied to constant declarations");
-			} else {
-				if (f->curr_proc == NULL) {
-					syntax_error(token, "`using` is not allowed at the file scope");
-				} else {
-					node->ValueDecl.flags |= VarDeclFlag_using;
-				}
+				return decl;
 			}
-			return node;
-		case AstNode_ExprStmt: {
-			AstNode *e = unparen_expr(node->ExprStmt.expr);
-			while (e->kind == AstNode_SelectorExpr) {
-				e = unparen_expr(e->SelectorExpr.selector);
-			}
-			if (e->kind == AstNode_Ident) {
-				return ast_using_stmt(f, token, node);
-			} else if (e->kind == AstNode_Implicit) {
-				syntax_error(token, "Illegal use of `using` statement with implicit value `%.*s`", LIT(e->Implicit.string));
-				return ast_bad_stmt(f, token, f->curr_token);
+			if (f->curr_proc == NULL) {
+				syntax_error(token, "`using` is not allowed at the file scope");
+			} else {
+				decl->ValueDecl.flags |= VarDeclFlag_using;
 			}
-		} break;
-
-
+			return decl;
 		}
 
 		syntax_error(token, "Illegal use of `using` statement");
@@ -3518,6 +3544,20 @@ AstNode *parse_stmt(AstFile *f) {
 				syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
 			}
 			return s;
+		} else if (str_eq(tag, str_lit("label"))) {
+			AstNode *name = parse_ident(f);
+			AstNode *label = ast_label_decl(f, token, name);
+
+			Token tok = f->curr_token;
+			switch (tok.kind) {
+			case Token_for:
+				return parse_for_stmt(f, label);
+			default:
+				syntax_error(token, "#label may only be applied to a loop");
+				fix_advance_to_next_stmt(f);
+				s = ast_bad_stmt(f, token, f->curr_token);
+				return s;
+			}
 		}
 
 

File diff suppressed because it is too large
+ 600 - 329
src/ssa.c


+ 277 - 0
src/ssa_op.c

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

+ 18 - 18
src/tokenizer.c

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

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