Ver código fonte

Dynamic arrays

Ginger Bill 8 anos atrás
pai
commit
984e36a151
13 arquivos alterados com 539 adições e 120 exclusões
  1. 1 1
      build.bat
  2. 13 2
      code/demo.odin
  3. 77 44
      core/_preload.odin
  4. 3 0
      core/mem.odin
  5. 11 0
      core/os_windows.odin
  6. 103 6
      src/check_expr.c
  7. 7 0
      src/check_stmt.c
  8. 6 0
      src/checker.c
  9. 190 33
      src/ir.c
  10. 7 0
      src/ir_print.c
  11. 26 8
      src/parser.c
  12. 24 25
      src/tokenizer.c
  13. 71 1
      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

+ 13 - 2
code/demo.odin

@@ -10,8 +10,19 @@
 // #import win32 "sys/windows.odin";
 
 main :: proc() {
-	// syntax();
-	procedure_overloading();
+	array: [dynamic]int;
+	defer free(array);
+	reserve(^array, 10);
+
+	append(^array, 2);
+	append(^array, 3);
+	append(^array, 5);
+	append(^array, 7);
+	append(^array, 11);
+	append(^array, 13);
+	for val, idx in array {
+		fmt.println(val, idx);
+	}
 }
 
 syntax :: proc() {

+ 77 - 44
core/_preload.odin

@@ -116,6 +116,7 @@ __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,
 	FREE,
@@ -164,12 +165,16 @@ alloc_align :: proc(size, alignment: int) -> rawptr #inline {
 	return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0);
 }
 
+free_ptr_with_allocator :: proc(a: Allocator, ptr: rawptr) #inline {
+	if ptr == nil {
+		return;
+	}
+	a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
+}
+
 free_ptr :: proc(ptr: rawptr) #inline {
 	__check_context();
-	a := context.allocator;
-	if ptr != nil {
-		a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
-	}
+	free_ptr_with_allocator(context.allocator, ptr);
 }
 free_all :: proc() #inline {
 	__check_context();
@@ -217,46 +222,21 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
 	using Allocator_Mode;
 
-	when false {
-		match mode {
-		case ALLOC:
-			total_size := size + alignment + size_of(mem.AllocationHeader);
-			ptr := os.heap_alloc(total_size);
-			header := (^mem.AllocationHeader)(ptr);
-			ptr = mem.align_forward(header+1, alignment);
-			mem.allocation_header_fill(header, ptr, size);
-			return mem.zero(ptr, size);
-
-		case FREE:
-			os.heap_free(mem.allocation_header(old_memory));
-			return nil;
-
-		case FREE_ALL:
-			// NOTE(bill): Does nothing
-
-		case RESIZE:
-			total_size := size + alignment + size_of(mem.AllocationHeader);
-			ptr := os.heap_resize(mem.allocation_header(old_memory), total_size);
-			header := (^mem.AllocationHeader)(ptr);
-			ptr = mem.align_forward(header+1, alignment);
-			mem.allocation_header_fill(header, ptr, size);
-			return mem.zero(ptr, size);
-		}
-	} else {
-		match mode {
-		case ALLOC:
-			return os.heap_alloc(size);
-
-		case FREE:
-			os.heap_free(old_memory);
-			return nil;
-
-		case FREE_ALL:
-			// NOTE(bill): Does nothing
-
-		case RESIZE:
-			return os.heap_resize(old_memory, size);
-		}
+	match mode {
+	case ALLOC:
+		return os.heap_alloc(size);
+
+	case FREE:
+		os.heap_free(old_memory);
+		return nil;
+
+	case FREE_ALL:
+		// NOTE(bill): Does nothing
+
+	case RESIZE:
+		ptr := os.heap_resize(old_memory, size);
+		assert(ptr != nil);
+		return ptr;
 	}
 
 	return nil;
@@ -336,3 +316,56 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline {
 	return utf8.decode_rune(s);
 }
 
+
+Raw_Dynamic_Array :: struct #ordered {
+	data:      rawptr,
+	count:     int,
+	capacity:  int,
+	allocator: Allocator,
+};
+
+__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool {
+	array := cast(^Raw_Dynamic_Array)array_;
+
+	if capacity <= array.capacity {
+		return true;
+	}
+
+	__check_context();
+	if array.allocator.procedure == nil {
+		array.allocator = context.allocator;
+	}
+	assert(array.allocator.procedure != nil);
+
+	old_size  := array.capacity * elem_size;
+	new_size  := capacity * elem_size;
+	allocator := array.allocator;
+
+	new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
+	if new_data == nil {
+		return false;
+	}
+
+	array.data = new_data;
+	array.capacity = capacity;
+	return true;
+}
+
+
+__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, item_ptr: rawptr) {
+	array := cast(^Raw_Dynamic_Array)array_;
+
+	ok := true;
+	if array.data == nil || array.capacity <= array.count {
+		capacity := 2 * array.capacity + 8;
+		ok := __dynamic_array_reserve(array, elem_size, elem_align, capacity);
+	}
+	if !ok {
+		// TODO(bill): Better error handling for failed reservation
+		return;
+	}
+	data := cast(^byte)array.data;
+	assert(data != nil);
+	mem.copy(data + (elem_size*array.count), item_ptr, elem_size);
+	array.count += 1;
+}

+ 3 - 0
core/mem.odin

@@ -80,6 +80,9 @@ allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: i
 	}
 }
 allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
+	if data == nil {
+		return nil;
+	}
 	p := cast(^int)data;
 	for (p-1)^ == -1 {
 		p = (p-1);

+ 11 - 0
core/os_windows.odin

@@ -248,12 +248,23 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 
 
 heap_alloc :: proc(size: int) -> rawptr {
+	assert(size > 0);
 	return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	if new_size == 0 {
+		heap_free(ptr);
+		return nil;
+	}
+	if ptr == nil {
+		return heap_alloc(new_size);
+	}
 	return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
 }
 heap_free :: proc(ptr: rawptr) {
+	if ptr == nil {
+		return;
+	}
 	win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
 }
 

+ 103 - 6
src/check_expr.c

@@ -1140,6 +1140,13 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
 		goto end;
 	case_end;
 
+	case_ast_node(dat, DynamicArrayType, e);
+		Type *elem = check_type_extra(c, dat->elem, NULL);
+		type = make_type_dynamic_array(c->allocator, elem);
+		goto end;
+	case_end;
+
+
 
 	case_ast_node(vt, VectorType, e);
 		Type *elem = check_type(c, vt->elem);
@@ -1984,7 +1991,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		return;
 	}
 
-	if (token_is_shift(op)) {
+	if (token_is_shift(op.kind)) {
 		check_shift(c, x, y, node);
 		return;
 	}
@@ -2019,7 +2026,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		return;
 	}
 
-	if (token_is_comparison(op)) {
+	if (token_is_comparison(op.kind)) {
 		check_comparison(c, x, y, op.kind);
 		return;
 	}
@@ -2138,9 +2145,9 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
 			// See above note in UnaryExpr case
 			break;
 		}
-		if (token_is_comparison(be->op)) {
-		}
-		else if (token_is_shift(be->op)) {
+		if (token_is_comparison(be->op.kind)) {
+			// NOTE(bill): Do nothing as the types are fine
+		} else if (token_is_shift(be->op.kind)) {
 			update_expr_type(c, be->left,  type, final);
 		} else {
 			update_expr_type(c, be->left,  type, final);
@@ -2658,11 +2665,13 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			ok = true;
 		} else if (is_type_string(type)) {
 			ok = true;
+		} else if (is_type_dynamic_array(type)) {
+			ok = true;
 		}
 
 		if (!ok) {
 			gbString type_str = type_to_string(type);
-			error_node(operand->expr, "You can only free pointers, slices, and strings, got `%s`", type_str);
+			error_node(operand->expr, "Invalid type for `free`, got `%s`", type_str);
 			gb_string_free(type_str);
 			return false;
 		}
@@ -2671,6 +2680,80 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		operand->mode = Addressing_NoValue;
 	} break;
 
+
+	case BuiltinProc_reserve: {
+		// reserve :: proc(^[dynamic]Type, count: int) {
+		Type *type = operand->type;
+		if (!is_type_pointer(type)) {
+			gbString str = type_to_string(type);
+			error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str);
+			gb_string_free(str);
+			return false;
+		}
+		type = type_deref(type);
+		if (!is_type_dynamic_array(type)) {
+			gbString str = type_to_string(type);
+			error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str);
+			gb_string_free(str);
+			return false;
+		}
+
+		AstNode *capacity = ce->args.e[1];
+		Operand op = {0};
+		check_expr(c, &op, capacity);
+		if (op.mode == Addressing_Invalid) {
+			return false;
+		}
+		Type *arg_type = base_type(op.type);
+		if (!is_type_integer(arg_type)) {
+			error_node(operand->expr, "`reserve` capacities must be an integer");
+			return false;
+		}
+
+		operand->type = NULL;
+		operand->mode = Addressing_NoValue;
+	} break;
+
+	case BuiltinProc_append: {
+		// append :: proc(^[dynamic]Type, item: Type) {
+		Type *type = operand->type;
+		if (!is_type_pointer(type)) {
+			gbString str = type_to_string(type);
+			error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str);
+			gb_string_free(str);
+			return false;
+		}
+		type = base_type(type_deref(type));
+		if (!is_type_dynamic_array(type)) {
+			gbString str = type_to_string(type);
+			error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str);
+			gb_string_free(str);
+			return false;
+		}
+		Type *elem = type->DynamicArray.elem;
+
+
+		AstNode *item = ce->args.e[1];
+		Operand op = {0};
+		check_expr(c, &op, item);
+		if (op.mode == Addressing_Invalid) {
+			return false;
+		}
+		Type *arg_type = op.type;
+		if (!check_is_assignable_to(c, &op, elem)) {
+			gbString elem_str = type_to_string(elem);
+			gbString str = type_to_string(arg_type);
+			error_node(operand->expr, "Expected `%s` for `append` item, got `%s`", elem_str, str);
+			gb_string_free(str);
+			gb_string_free(elem_str);
+			return false;
+		}
+
+		operand->type = NULL;
+		operand->mode = Addressing_NoValue;
+	} break;
+
+
 	case BuiltinProc_size_of: {
 		// size_of :: proc(Type) -> untyped int
 		Type *type = check_type(c, ce->args.e[0]);
@@ -3839,6 +3922,11 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
 		o->type = t->Slice.elem;
 		o->mode = Addressing_Variable;
 		return true;
+
+	case Type_DynamicArray:
+		o->type = t->DynamicArray.elem;
+		o->mode = Addressing_Variable;
+		return true;
 	}
 
 	return false;
@@ -4750,6 +4838,10 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		case Type_Slice:
 			valid = true;
 			break;
+
+		case Type_DynamicArray:
+			valid = true;
+			break;
 		}
 
 		if (!valid) {
@@ -5101,6 +5193,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = write_expr_to_string(str, at->elem);
 	case_end;
 
+	case_ast_node(at, DynamicArrayType, node);
+		str = gb_string_appendc(str, "[dynamic]");
+		str = write_expr_to_string(str, at->elem);
+	case_end;
+
 	case_ast_node(vt, VectorType, node);
 		str = gb_string_appendc(str, "[vector ");
 		str = write_expr_to_string(str, vt->count);

+ 7 - 0
src/check_stmt.c

@@ -697,6 +697,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 					val = t->Array.elem;
 					idx = t_int;
 					break;
+
+				case Type_DynamicArray:
+					// val = make_type_pointer(c->allocator, t->DynamicArray.elem);
+					val = t->DynamicArray.elem;
+					idx = t_int;
+					break;
+
 				case Type_Slice:
 					// val = make_type_pointer(c->allocator, t->Slice.elem);
 					val = t->Slice.elem;

+ 6 - 0
src/checker.c

@@ -125,6 +125,9 @@ typedef enum BuiltinProcId {
 	BuiltinProc_new_slice,
 	BuiltinProc_free,
 
+	BuiltinProc_reserve,
+	BuiltinProc_append,
+
 	BuiltinProc_size_of,
 	BuiltinProc_size_of_val,
 	BuiltinProc_align_of,
@@ -169,6 +172,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("new_slice"),        2, false, Expr_Expr},
 	{STR_LIT("free"),             1, false, Expr_Stmt},
 
+	{STR_LIT("reserve"),          2, false, Expr_Stmt},
+	{STR_LIT("append"),           2, false, Expr_Stmt},
+
 	{STR_LIT("size_of"),          1, false, Expr_Expr},
 	{STR_LIT("size_of_val"),      1, false, Expr_Expr},
 	{STR_LIT("align_of"),         1, false, Expr_Expr},

+ 190 - 33
src/ir.c

@@ -1620,6 +1620,13 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) {
 		case 0: result_type = make_type_pointer(a, t->Maybe.elem); break;
 		case 1: result_type = make_type_pointer(a, t_bool);        break;
 		}
+	} else if (is_type_dynamic_array(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->DynamicArray.elem)); break;
+		case 1: result_type = t_int_ptr;                                      break;
+		case 2: result_type = t_int_ptr;                                      break;
+		case 3: result_type = t_allocator_ptr;                                break;
+		}
 	} else {
 		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ir_type(s)), index);
 	}
@@ -1667,6 +1674,13 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
 		case 0: result_type = t->Maybe.elem; break;
 		case 1: result_type = t_bool;        break;
 		}
+	} else if (is_type_dynamic_array(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t->DynamicArray.elem); break;
+		case 1: result_type = t_int;                                      break;
+		case 2: result_type = t_int;                                      break;
+		case 3: result_type = t_allocator;                                break;
+		}
 	} else {
 		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ir_type(s)), index);
 	}
@@ -1765,29 +1779,52 @@ irValue *ir_array_elem(irProcedure *proc, irValue *array) {
 	return ir_emit_array_ep(proc, array, v_zero32);
 }
 irValue *ir_array_len(irProcedure *proc, irValue *array) {
-	Type *t = ir_type(array);
+	Type *t = base_type(ir_type(array));
 	GB_ASSERT(t->kind == Type_Array);
 	return ir_make_const_int(proc->module->allocator, t->Array.count);
 }
 
 irValue *ir_slice_elem(irProcedure *proc, irValue *slice) {
-	Type *t = ir_type(slice);
+	Type *t = base_type(ir_type(slice));
 	GB_ASSERT(t->kind == Type_Slice);
 	return ir_emit_struct_ev(proc, slice, 0);
 }
-irValue *ir_slice_len(irProcedure *proc, irValue *slice) {
-	Type *t = ir_type(slice);
+irValue *ir_slice_count(irProcedure *proc, irValue *slice) {
+	Type *t = base_type(ir_type(slice));
 	GB_ASSERT(t->kind == Type_Slice);
 	return ir_emit_struct_ev(proc, slice, 1);
 }
 
+irValue *ir_dynamic_array_elem(irProcedure *proc, irValue *da) {
+	Type *t = ir_type(da);
+	GB_ASSERT(t->kind == Type_DynamicArray);
+	return ir_emit_struct_ev(proc, da, 0);
+}
+irValue *ir_dynamic_array_count(irProcedure *proc, irValue *da) {
+	Type *t = base_type(ir_type(da));
+	GB_ASSERT_MSG(t->kind == Type_DynamicArray, "%s", type_to_string(t));
+	return ir_emit_struct_ev(proc, da, 1);
+}
+irValue *ir_dynamic_array_capacity(irProcedure *proc, irValue *da) {
+	Type *t = base_type(ir_type(da));
+	GB_ASSERT(t->kind == Type_DynamicArray);
+	return ir_emit_struct_ev(proc, da, 2);
+}
+irValue *ir_dynamic_array_allocator(irProcedure *proc, irValue *da) {
+	Type *t = base_type(ir_type(da));
+	GB_ASSERT(t->kind == Type_DynamicArray);
+	return ir_emit_struct_ev(proc, da, 3);
+}
+
+
+
 irValue *ir_string_elem(irProcedure *proc, irValue *string) {
-	Type *t = ir_type(string);
+	Type *t = base_type(ir_type(string));
 	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
 	return ir_emit_struct_ev(proc, string, 0);
 }
 irValue *ir_string_len(irProcedure *proc, irValue *string) {
-	Type *t = ir_type(string);
+	Type *t = base_type(ir_type(string));
 	GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t));
 	return ir_emit_struct_ev(proc, string, 1);
 }
@@ -1806,7 +1843,7 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base,
 	if (high == NULL) {
 		switch (bt->kind) {
 		case Type_Array:   high = ir_array_len(proc, base); break;
-		case Type_Slice:   high = ir_slice_len(proc, base); break;
+		case Type_Slice:   high = ir_slice_count(proc, base); break;
 		case Type_Pointer: high = v_one;                     break;
 		}
 	}
@@ -2069,7 +2106,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 	// []byte/[]u8 <-> string
 	if (is_type_u8_slice(src) && is_type_string(dst)) {
 		irValue *elem = ir_slice_elem(proc, value);
-		irValue *len  = ir_slice_len(proc, value);
+		irValue *len  = ir_slice_count(proc, value);
 		return ir_emit_string(proc, elem, len);
 	}
 	if (is_type_string(src) && is_type_u8_slice(dst)) {
@@ -2912,12 +2949,25 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 				case BuiltinProc_free: {
 					ir_emit_comment(proc, str_lit("free"));
 
-					gbAllocator allocator = proc->module->allocator;
+					gbAllocator a = proc->module->allocator;
 
 					AstNode *node = ce->args.e[0];
 					TypeAndValue tav = *type_and_value_of_expression(proc->module->info, node);
 					Type *type = base_type(tav.type);
 					irValue *val = ir_build_expr(proc, node);
+
+					if (is_type_dynamic_array(type)) {
+						irValue *da_allocator = ir_emit_struct_ev(proc, val, 3);
+
+						irValue *ptr = ir_emit_struct_ev(proc, val, 0);
+						ptr = ir_emit_conv(proc, ptr, t_rawptr);
+
+						irValue **args = gb_alloc_array(a, irValue *, 1);
+						args[0] = da_allocator;
+						args[1] = ptr;
+						return ir_emit_global_call(proc, "free_ptr_with_allocator", args, 2);
+					}
+
 					irValue *ptr = NULL;
 					if (is_type_pointer(type)) {
 						ptr = val;
@@ -2935,11 +2985,66 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 
 					ptr = ir_emit_conv(proc, ptr, t_rawptr);
 
-					irValue **args = gb_alloc_array(allocator, irValue *, 1);
+					irValue **args = gb_alloc_array(a, irValue *, 1);
 					args[0] = ptr;
 					return ir_emit_global_call(proc, "free_ptr", args, 1);
 				} break;
 
+				case BuiltinProc_reserve: {
+					ir_emit_comment(proc, str_lit("reserve"));
+					gbAllocator a = proc->module->allocator;
+
+					irValue *array_ptr = ir_build_expr(proc, ce->args.e[0]);
+					Type *type = ir_type(array_ptr);
+					GB_ASSERT(is_type_pointer(type));
+					type = base_type(type_deref(type));
+					GB_ASSERT(is_type_dynamic_array(type));
+					Type *elem = type->DynamicArray.elem;
+
+					irValue *elem_size  = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem));
+					irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem));
+
+					array_ptr = ir_emit_conv(proc, array_ptr, t_rawptr);
+
+					irValue *capacity = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t_int);
+
+					irValue **args = gb_alloc_array(a, irValue *, 4);
+					args[0] = array_ptr;
+					args[1] = elem_size;
+					args[2] = elem_align;
+					args[3] = capacity;
+					return ir_emit_global_call(proc, "__dynamic_array_reserve", args, 4);
+				} break;
+
+				case BuiltinProc_append: {
+					ir_emit_comment(proc, str_lit("append"));
+					gbAllocator a = proc->module->allocator;
+
+					irValue *array_ptr = ir_build_expr(proc, ce->args.e[0]);
+					Type *type = ir_type(array_ptr);
+					GB_ASSERT(is_type_pointer(type));
+					type = base_type(type_deref(type));
+					GB_ASSERT(is_type_dynamic_array(type));
+					Type *elem = type->DynamicArray.elem;
+
+					irValue *elem_size  = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem));
+					irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem));
+
+					array_ptr = ir_emit_conv(proc, array_ptr, t_rawptr);
+
+					irValue *item_ptr = ir_add_local_generated(proc, elem);
+					irValue *item = ir_build_expr(proc, ce->args.e[1]);
+					ir_emit_store(proc, item_ptr, item);
+
+					irValue **args = gb_alloc_array(a, irValue *, 4);
+					args[0] = array_ptr;
+					args[1] = elem_size;
+					args[2] = elem_align;
+					args[3] = ir_emit_conv(proc, item_ptr, t_rawptr);
+					return ir_emit_global_call(proc, "__dynamic_array_append", args, 4);
+				} break;
+
+
 				case BuiltinProc_assert: {
 					ir_emit_comment(proc, str_lit("assert"));
 					irValue *cond = ir_build_expr(proc, ce->args.e[0]);
@@ -3010,8 +3115,8 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					irValue *dst = ir_emit_conv(proc, ir_slice_elem(proc, dst_slice), t_rawptr);
 					irValue *src = ir_emit_conv(proc, ir_slice_elem(proc, src_slice), t_rawptr);
 
-					irValue *len_dst = ir_slice_len(proc, dst_slice);
-					irValue *len_src = ir_slice_len(proc, src_slice);
+					irValue *len_dst = ir_slice_count(proc, dst_slice);
+					irValue *len_src = ir_slice_count(proc, src_slice);
 
 					irValue *cond = ir_emit_comp(proc, Token_Lt, len_dst, len_src);
 					irValue *len = ir_emit_select(proc, cond, len_dst, len_src);
@@ -3038,7 +3143,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					irValue *slice = ir_emit_load(proc, slice_ptr);
 
 					irValue *elem = ir_slice_elem(proc, slice);
-					irValue *len  = ir_slice_len(proc,  slice);
+					irValue *len  = ir_slice_count(proc,  slice);
 					irValue *cap  = ir_slice_cap(proc,  slice);
 
 					Type *elem_type = type_deref(ir_type(elem));
@@ -3522,14 +3627,32 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 				}
 			}
 			irValue *elem = ir_slice_elem(proc, slice);
-			irValue *len = ir_slice_len(proc, slice);
+			irValue *len = ir_slice_count(proc, slice);
 			irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int);
 			ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
 			irValue *v = ir_emit_ptr_offset(proc, elem, index);
 			return ir_make_addr(v, expr);
+		} break;
 
+		case Type_DynamicArray: {
+			irValue *dynamic_array = NULL;
+			if (using_addr != NULL) {
+				dynamic_array = ir_emit_load(proc, using_addr);
+			} else {
+				dynamic_array = ir_build_expr(proc, ie->expr);
+				if (deref) {
+					dynamic_array = ir_emit_load(proc, dynamic_array);
+				}
+			}
+			irValue *elem = ir_dynamic_array_elem(proc, dynamic_array);
+			irValue *len = ir_dynamic_array_count(proc, dynamic_array);
+			irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int);
+			ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
+			irValue *v = ir_emit_ptr_offset(proc, elem, index);
+			return ir_make_addr(v, expr);
 		} break;
 
+
 		case Type_Basic: { // Basic_string
 			TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr));
 			irValue *str;
@@ -3580,7 +3703,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 		case Type_Slice: {
 			Type *slice_type = type;
 
-			if (high == NULL) high = ir_slice_len(proc, base);
+			if (high == NULL) high = ir_slice_count(proc, base);
 
 			ir_emit_slice_bounds_check(proc, se->open, low, high, false);
 
@@ -3588,14 +3711,30 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			irValue *len   = ir_emit_arith(proc, Token_Sub, high, low, t_int);
 			irValue *slice = ir_add_local_generated(proc, slice_type);
 
-			irValue *gep0 = ir_emit_struct_ep(proc, slice, 0);
-			irValue *gep1 = ir_emit_struct_ep(proc, slice, 1);
-			ir_emit_store(proc, gep0, elem);
-			ir_emit_store(proc, gep1, len);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
 
 			return ir_make_addr(slice, expr);
 		}
 
+		case Type_DynamicArray: {
+			Type *dynamic_array = type;
+
+			if (high == NULL) high = ir_dynamic_array_count(proc, base);
+
+			ir_emit_slice_bounds_check(proc, se->open, low, high, false);
+
+			irValue *elem  = ir_emit_ptr_offset(proc, ir_dynamic_array_elem(proc, base), low);
+			irValue *len   = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+			irValue *slice = ir_add_local_generated(proc, dynamic_array);
+
+			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
+
+			return ir_make_addr(slice, expr);
+		}
+
+
 		case Type_Array: {
 			Type *slice_type = make_type_slice(a, type->Array.elem);
 
@@ -3607,10 +3746,8 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			irValue *len  = ir_emit_arith(proc, Token_Sub, high, low, t_int);
 			irValue *slice = ir_add_local_generated(proc, slice_type);
 
-			irValue *gep0 = ir_emit_struct_ep(proc, slice, 0);
-			irValue *gep1 = ir_emit_struct_ep(proc, slice, 1);
-			ir_emit_store(proc, gep0, elem);
-			ir_emit_store(proc, gep1, len);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
 
 			return ir_make_addr(slice, expr);
 		}
@@ -3973,7 +4110,7 @@ void ir_emit_increment(irProcedure *proc, irValue *addr) {
 
 }
 
-void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type,
+void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, irValue *count_ptr,
                             irValue **val_, irValue **idx_, irBlock **loop_, irBlock **done_) {
 	irValue *count = NULL;
 	Type *expr_type = base_type(type_deref(ir_type(expr)));
@@ -3981,12 +4118,6 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type,
 	case Type_Array:
 		count = ir_make_const_int(proc->module->allocator, expr_type->Array.count);
 		break;
-	case Type_Slice:
-		count = ir_slice_len(proc, expr);
-		break;
-	default:
-		GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type));
-		break;
 	}
 
 	irValue *val = NULL;
@@ -4007,6 +4138,9 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type,
 
 	body = ir_add_block(proc, NULL, "for.index.body");
 	done = ir_add_block(proc, NULL, "for.index.done");
+	if (count == NULL) {
+		count = ir_emit_load(proc, count_ptr);
+	}
 	irValue *cond = ir_emit_comp(proc, Token_Lt, incr, count);
 	ir_emit_if(proc, cond, body, done);
 	proc->curr_block = body;
@@ -4020,7 +4154,10 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type,
 		} break;
 		case Type_Slice: {
 			irValue *elem = ir_slice_elem(proc, expr);
-			// val = ir_emit_ptr_offset(proc, elem, idx);
+			val = ir_emit_load(proc, ir_emit_ptr_offset(proc, elem, idx));
+		} break;
+		case Type_DynamicArray: {
+			irValue *elem = ir_dynamic_array_elem(proc, expr);
 			val = ir_emit_load(proc, ir_emit_ptr_offset(proc, elem, idx));
 		} break;
 		default:
@@ -4578,18 +4715,38 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 			Type *et = base_type(type_deref(expr_type));
 			switch (et->kind) {
 			case Type_Array: {
+				irValue *count_ptr = NULL;
 				irValue *array = ir_build_addr(proc, rs->expr).addr;
 				if (is_type_pointer(type_deref(ir_type(array)))) {
 					array = ir_emit_load(proc, array);
 				}
-				ir_build_range_indexed(proc, array, val_type, &val, &index, &loop, &done);
+				count_ptr = ir_add_local_generated(proc, t_int);
+				ir_emit_store(proc, count_ptr, ir_make_const_int(proc->module->allocator, et->Array.count));
+				ir_build_range_indexed(proc, array, val_type, count_ptr, &val, &index, &loop, &done);
+			} break;
+			case Type_DynamicArray: {
+				irValue *count_ptr = NULL;
+				irValue *array = ir_build_expr(proc, rs->expr);
+				if (is_type_pointer(type_deref(ir_type(array)))) {
+					count_ptr = ir_emit_struct_ep(proc, array, 1);
+					array = ir_emit_load(proc, array);
+				}  else {
+					count_ptr = ir_add_local_generated(proc, t_int);
+					ir_emit_store(proc, count_ptr, ir_dynamic_array_count(proc, array));
+				}
+				ir_build_range_indexed(proc, array, val_type, count_ptr, &val, &index, &loop, &done);
 			} break;
 			case Type_Slice: {
+				irValue *count_ptr = NULL;
 				irValue *slice = ir_build_expr(proc, rs->expr);
 				if (is_type_pointer(ir_type(slice))) {
+					count_ptr = ir_emit_struct_ep(proc, slice, 1);
 					slice = ir_emit_load(proc, slice);
+				} else {
+					count_ptr = ir_add_local_generated(proc, t_int);
+					ir_emit_store(proc, count_ptr, ir_slice_count(proc, slice));
 				}
-				ir_build_range_indexed(proc, slice, val_type, &val, &index, &loop, &done);
+				ir_build_range_indexed(proc, slice, val_type, count_ptr, &val, &index, &loop, &done);
 			} break;
 			case Type_Basic: {
 				irValue *string = ir_build_expr(proc, rs->expr);

+ 7 - 0
src/ir_print.c

@@ -194,6 +194,13 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 		ir_print_type(f, m, t->Slice.elem);
 		ir_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits);
 		return;
+	case Type_DynamicArray:
+		ir_fprintf(f, "{");
+		ir_print_type(f, m, t->Slice.elem);
+		ir_fprintf(f, "*, i%lld, i%lld,", word_bits, word_bits);
+		ir_print_type(f, m, t_allocator);
+		ir_fprintf(f, "}");
+		return;
 	case Type_Record: {
 		switch (t->Record.kind) {
 		case TypeRecord_Struct:

+ 26 - 8
src/parser.c

@@ -341,6 +341,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
 		AstNode *count; \
 		AstNode *elem; \
 	}) \
+	AST_NODE_KIND(DynamicArrayType, "dynamic array type", struct { \
+		Token token; \
+		AstNode *elem; \
+	}) \
 	AST_NODE_KIND(VectorType, "vector type", struct { \
 		Token token; \
 		AstNode *count; \
@@ -553,6 +557,8 @@ Token ast_node_token(AstNode *node) {
 		return node->MaybeType.token;
 	case AstNode_ArrayType:
 		return node->ArrayType.token;
+	case AstNode_DynamicArrayType:
+		return node->DynamicArrayType.token;
 	case AstNode_VectorType:
 		return node->VectorType.token;
 	case AstNode_StructType:
@@ -1080,6 +1086,13 @@ AstNode *make_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem)
 	return result;
 }
 
+AstNode *make_dynamic_array_type(AstFile *f, Token token, AstNode *elem) {
+	AstNode *result = make_node(f, AstNode_DynamicArrayType);
+	result->DynamicArrayType.token = token;
+	result->DynamicArrayType.elem  = elem;
+	return result;
+}
+
 AstNode *make_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
 	AstNode *result = make_node(f, AstNode_VectorType);
 	result->VectorType.token = token;
@@ -1267,7 +1280,6 @@ void fix_advance_to_next_stmt(AstFile *f) {
 		case Token_if:
 		case Token_when:
 		case Token_return:
-		case Token_range:
 		case Token_match:
 		case Token_defer:
 		case Token_asm:
@@ -1463,7 +1475,7 @@ AstNode *parse_value(AstFile *f) {
 	return value;
 }
 
-AstNode *parse_identifier_or_type(AstFile *f);
+AstNode *parse_type_or_ident(AstFile *f);
 
 
 void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) {
@@ -1839,7 +1851,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 		break;
 
 	default: {
-		AstNode *type = parse_identifier_or_type(f);
+		AstNode *type = parse_type_or_ident(f);
 		if (type != NULL) {
 			// TODO(bill): Is this correct???
 			// NOTE(bill): Sanity check as identifiers should be handled already
@@ -2204,7 +2216,7 @@ void parse_check_name_list_for_reserves(AstFile *f, AstNodeArray names) {
 }
 
 AstNode *parse_type_attempt(AstFile *f) {
-	AstNode *type = parse_identifier_or_type(f);
+	AstNode *type = parse_type_or_ident(f);
 	if (type != NULL) {
 		// TODO(bill): Handle?
 	}
@@ -2415,7 +2427,7 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
 	if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
 		Token tok = f->curr_token;
 		next_token(f);
-		AstNode *type = parse_identifier_or_type(f);
+		AstNode *type = parse_type_or_ident(f);
 		if (type == NULL) {
 			error(tok, "variadic field missing type after `...`");
 			type = make_bad_expr(f, tok, f->curr_token);
@@ -2561,7 +2573,7 @@ AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, Str
 	return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace);
 }
 
-AstNode *parse_identifier_or_type(AstFile *f) {
+AstNode *parse_type_or_ident(AstFile *f) {
 	switch (f->curr_token.kind) {
 	case Token_Ident: {
 		AstNode *e = parse_identifier(f);
@@ -2597,7 +2609,6 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 	}
 
 	case Token_OpenBracket: {
-		f->expr_level++;
 		Token token = expect_token(f, Token_OpenBracket);
 		AstNode *count_expr = NULL;
 		bool is_vector = false;
@@ -2607,16 +2618,23 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		} else if (f->curr_token.kind == Token_vector) {
 			next_token(f);
 			if (f->curr_token.kind != Token_CloseBracket) {
+				f->expr_level++;
 				count_expr = parse_expr(f, false);
+				f->expr_level--;
 			} else {
 				syntax_error(f->curr_token, "Vector type missing count");
 			}
 			is_vector = true;
+		} else if (f->curr_token.kind == Token_dynamic) {
+			next_token(f);
+			expect_token(f, Token_CloseBracket);
+			return make_dynamic_array_type(f, token, parse_type(f));
 		} else if (f->curr_token.kind != Token_CloseBracket) {
+			f->expr_level++;
 			count_expr = parse_expr(f, false);
+			f->expr_level--;
 		}
 		expect_token(f, Token_CloseBracket);
-		f->expr_level--;
 		if (is_vector) {
 			return make_vector_type(f, token, count_expr, parse_type(f));
 		}

+ 24 - 25
src/tokenizer.c

@@ -80,41 +80,40 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
 TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
 \
 TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
-	TOKEN_KIND(Token_type,           "type"), \
-	TOKEN_KIND(Token_proc,           "proc"), \
-	TOKEN_KIND(Token_macro,          "macro"), \
-	TOKEN_KIND(Token_match,          "match"), \
-	TOKEN_KIND(Token_break,          "break"), \
-	TOKEN_KIND(Token_continue,       "continue"), \
-	TOKEN_KIND(Token_fallthrough,    "fallthrough"), \
-	TOKEN_KIND(Token_case,           "case"), \
-	TOKEN_KIND(Token_default,        "default"), \
-	TOKEN_KIND(Token_then,           "then"), \
+	TOKEN_KIND(Token_when,           "when"), \
 	TOKEN_KIND(Token_if,             "if"), \
 	TOKEN_KIND(Token_else,           "else"), \
 	TOKEN_KIND(Token_for,            "for"), \
 	TOKEN_KIND(Token_in,             "in"), \
-	TOKEN_KIND(Token_when,           "when"), \
-	TOKEN_KIND(Token_range,          "range"), \
+	TOKEN_KIND(Token_break,          "break"), \
+	TOKEN_KIND(Token_continue,       "continue"), \
+	TOKEN_KIND(Token_fallthrough,    "fallthrough"), \
+	TOKEN_KIND(Token_match,          "match"), \
+	TOKEN_KIND(Token_type,           "type"), \
+	TOKEN_KIND(Token_default,        "default"), \
+	TOKEN_KIND(Token_case,           "case"), \
 	TOKEN_KIND(Token_defer,          "defer"), \
 	TOKEN_KIND(Token_return,         "return"), \
 	TOKEN_KIND(Token_give,           "give"), \
+	TOKEN_KIND(Token_proc,           "proc"), \
+	TOKEN_KIND(Token_macro,          "macro"), \
 	TOKEN_KIND(Token_struct,         "struct"), \
 	TOKEN_KIND(Token_union,          "union"), \
 	TOKEN_KIND(Token_raw_union,      "raw_union"), \
 	TOKEN_KIND(Token_enum,           "enum"), \
 	TOKEN_KIND(Token_vector,         "vector"), \
+	TOKEN_KIND(Token_dynamic,        "dynamic"), \
 	TOKEN_KIND(Token_using,          "using"), \
 	TOKEN_KIND(Token_no_alias,       "no_alias"), \
 	TOKEN_KIND(Token_immutable,      "immutable"), \
 	TOKEN_KIND(Token_thread_local,   "thread_local"), \
-	TOKEN_KIND(Token_asm,            "asm"), \
-	TOKEN_KIND(Token_push_allocator, "push_allocator"), \
-	TOKEN_KIND(Token_push_context,   "push_context"), \
 	TOKEN_KIND(Token_cast,           "cast"), \
 	TOKEN_KIND(Token_transmute,      "transmute"), \
 	TOKEN_KIND(Token_down_cast,      "down_cast"), \
 	TOKEN_KIND(Token_union_cast,     "union_cast"), \
+	TOKEN_KIND(Token_push_allocator, "push_allocator"), \
+	TOKEN_KIND(Token_push_context,   "push_context"), \
+	TOKEN_KIND(Token_asm,            "asm"), \
 TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
 	TOKEN_KIND(Token_Count, "")
 
@@ -264,20 +263,20 @@ void compiler_error(char *fmt, ...) {
 
 
 
-gb_inline bool token_is_literal(Token t) {
-	return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1);
+gb_inline bool token_is_literal(TokenKind t) {
+	return gb_is_between(t, Token__LiteralBegin+1, Token__LiteralEnd-1);
 }
-gb_inline bool token_is_operator(Token t) {
-	return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1);
+gb_inline bool token_is_operator(TokenKind t) {
+	return gb_is_between(t, Token__OperatorBegin+1, Token__OperatorEnd-1);
 }
-gb_inline bool token_is_keyword(Token t) {
-	return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1);
+gb_inline bool token_is_keyword(TokenKind t) {
+	return gb_is_between(t, Token__KeywordBegin+1, Token__KeywordEnd-1);
 }
-gb_inline bool token_is_comparison(Token t) {
-	return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
+gb_inline bool token_is_comparison(TokenKind t) {
+	return gb_is_between(t, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
 }
-gb_inline bool token_is_shift(Token t) {
-	return t.kind == Token_Shl || t.kind == Token_Shr;
+gb_inline bool token_is_shift(TokenKind t) {
+	return t == Token_Shl || t == Token_Shr;
 }
 
 gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }

+ 71 - 1
src/types.c

@@ -97,6 +97,7 @@ typedef struct TypeRecord {
 	TYPE_KIND(Basic,   BasicType) \
 	TYPE_KIND(Pointer, struct { Type *elem; }) \
 	TYPE_KIND(Array,   struct { Type *elem; i64 count; }) \
+	TYPE_KIND(DynamicArray, struct { Type *elem; }) \
 	TYPE_KIND(Vector,  struct { Type *elem; i64 count; }) \
 	TYPE_KIND(Slice,   struct { Type *elem; }) \
 	TYPE_KIND(Maybe,   struct { Type *elem; }) \
@@ -385,6 +386,12 @@ Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
 	return t;
 }
 
+Type *make_type_dynamic_array(gbAllocator a, Type *elem) {
+	Type *t = alloc_type(a, Type_DynamicArray);
+	t->DynamicArray.elem = elem;
+	return t;
+}
+
 Type *make_type_vector(gbAllocator a, Type *elem, i64 count) {
 	Type *t = alloc_type(a, Type_Vector);
 	t->Vector.elem = elem;
@@ -616,6 +623,10 @@ bool is_type_array(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_Array;
 }
+bool is_type_dynamic_array(Type *t) {
+	t = base_type(t);
+	return t->kind == Type_DynamicArray;
+}
 bool is_type_slice(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_Slice;
@@ -690,6 +701,7 @@ bool type_has_nil(Type *t) {
 		return false;
 	} break;
 	case Type_Slice:
+	case Type_DynamicArray:
 	case Type_Proc:
 	case Type_Pointer:
 	case Type_Maybe:
@@ -750,6 +762,12 @@ bool are_types_identical(Type *x, Type *y) {
 		}
 		break;
 
+	case Type_DynamicArray:
+		if (y->kind == Type_DynamicArray) {
+			return are_types_identical(x->DynamicArray.elem, y->DynamicArray.elem);
+		}
+		break;
+
 	case Type_Vector:
 		if (y->kind == Type_Vector) {
 			return (x->Vector.count == y->Vector.count) && are_types_identical(x->Vector.elem, y->Vector.elem);
@@ -973,7 +991,10 @@ gb_global Entity *entity__any_data       = NULL;
 gb_global Entity *entity__string_data    = NULL;
 gb_global Entity *entity__string_count   = NULL;
 gb_global Entity *entity__slice_count    = NULL;
-gb_global Entity *entity__slice_capacity = NULL;
+
+gb_global Entity *entity__dynamic_array_count     = NULL;
+gb_global Entity *entity__dynamic_array_capacity  = NULL;
+gb_global Entity *entity__dynamic_array_allocator = NULL;
 
 Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel);
 
@@ -1142,6 +1163,39 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 			sel.entity = entity__slice_count;
 			return sel;
 		}
+	}  else if (type->kind == Type_DynamicArray) {
+		String data_str      = str_lit("data");
+		String count_str     = str_lit("count");
+		String capacity_str  = str_lit("capacity");
+		String allocator_str = str_lit("allocator");
+
+		if (str_eq(field_name, data_str)) {
+			selection_add_index(&sel, 0);
+			// HACK(bill): Memory leak
+			sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->DynamicArray.elem), false, 0);
+			return sel;
+		} else if (str_eq(field_name, count_str)) {
+			selection_add_index(&sel, 1);
+			if (entity__dynamic_array_count == NULL) {
+				entity__dynamic_array_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
+			}
+			sel.entity = entity__dynamic_array_count;
+			return sel;
+		} else if (str_eq(field_name, capacity_str)) {
+			selection_add_index(&sel, 2);
+			if (entity__dynamic_array_capacity == NULL) {
+				entity__dynamic_array_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
+			}
+			sel.entity = entity__dynamic_array_capacity;
+			return sel;
+		} else if (str_eq(field_name, allocator_str)) {
+			selection_add_index(&sel, 3);
+			if (entity__dynamic_array_allocator == NULL) {
+				entity__dynamic_array_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 3);
+			}
+			sel.entity = entity__dynamic_array_allocator;
+			return sel;
+		}
 	}
 
 	if (type->kind != Type_Record) {
@@ -1351,6 +1405,14 @@ i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, Type
 		type_path_pop(path);
 		return align;
 	}
+
+	case Type_DynamicArray:
+		// data, count, capacity, allocator
+		return s.word_size;
+
+	case Type_Slice:
+		return s.word_size;
+
 	case Type_Vector: {
 		Type *elem = t->Vector.elem;
 		type_path_push(path, elem);
@@ -1537,6 +1599,9 @@ i64 type_size_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, TypeP
 		return alignment*(count-1) + size;
 	} break;
 
+	case Type_DynamicArray:
+		return 3*s.word_size + type_size_of(s, allocator, t_allocator);
+
 	case Type_Vector: {
 		i64 count, bit_size, total_size_in_bits, total_size;
 		count = t->Vector.count;
@@ -1736,6 +1801,11 @@ gbString write_type_to_string(gbString str, Type *type) {
 		str = write_type_to_string(str, type->Array.elem);
 		break;
 
+	case Type_DynamicArray:
+		str = gb_string_appendc(str, "[dynamic]");
+		str = write_type_to_string(str, type->DynamicArray.elem);
+		break;
+
 	case Type_Record: {
 		switch (type->Record.kind) {
 		case TypeRecord_Struct: