Browse Source

#soa[dynamic]Type (Experimental)

gingerBill 5 years ago
parent
commit
9b58781122
5 changed files with 498 additions and 102 deletions
  1. 196 0
      core/runtime/core.odin
  2. 10 1
      examples/demo/demo.odin
  3. 26 0
      src/check_expr.cpp
  4. 253 99
      src/check_type.cpp
  5. 13 2
      src/ir.cpp

+ 196 - 0
core/runtime/core.odin

@@ -559,7 +559,203 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
 		a.len += arg_len;
 	}
 }
+
+@builtin
+reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+	if array == nil do return false;
+
+	old_cap := cap(array);
+	if capacity <= old_cap do return true;
+
+	if array.allocator.procedure == nil {
+		array.allocator = context.allocator;
+	}
+	assert(array.allocator.procedure != nil);
+
+
+	ti := type_info_of(typeid_of(T));
+	ti = type_info_base(ti);
+	si := &ti.variant.(Type_Info_Struct);
+
+	field_count := uintptr(len(si.offsets) - 3);
+
+	if field_count == 0 {
+		return true;
+	}
+
+	cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
+	assert(cap_ptr^ == old_cap);
+
+
+	old_size := 0;
+	new_size := 0;
+
+	max_align := 0;
+	for i in 0..<field_count {
+		type := si.types[i].variant.(Type_Info_Pointer).elem;
+		max_align = max(max_align, type.align);
+
+		old_size = mem.align_forward_int(old_size, type.align);
+		new_size = mem.align_forward_int(new_size, type.align);
+
+		old_size += type.size * old_cap;
+		new_size += type.size * capacity;
+	}
+
+	old_size = mem.align_forward_int(old_size, max_align);
+	new_size = mem.align_forward_int(new_size, max_align);
+
+	old_data := (^rawptr)(array)^;
+
+	new_data := array.allocator.procedure(
+		array.allocator.data, mem.Allocator_Mode.Alloc, new_size, max_align,
+		nil, old_size, 0, loc,
+	);
+	if new_data == nil do return false;
+
+
+	cap_ptr^ = capacity;
+
+	old_offset := 0;
+	new_offset := 0;
+	for i in 0..<field_count {
+		type := si.types[i].variant.(Type_Info_Pointer).elem;
+		max_align = max(max_align, type.align);
+
+		old_offset = mem.align_forward_int(old_offset, type.align);
+		new_offset = mem.align_forward_int(new_offset, type.align);
+
+		new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
+		old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
+
+		mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
+
+		(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
+
+		old_offset += type.size * old_cap;
+		new_offset += type.size * capacity;
+	}
+
+	array.allocator.procedure(
+		array.allocator.data, mem.Allocator_Mode.Free, 0, max_align,
+		old_data, old_size, 0, loc,
+	);
+
+	return true;
+}
+
+@builtin
+append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
+	if array == nil do return;
+
+	arg_len := 1;
+
+	if cap(array) <= len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve_soa(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		ti := type_info_of(typeid_of(T));
+		ti = type_info_base(ti);
+		si := &ti.variant.(Type_Info_Struct);
+		field_count := uintptr(len(si.offsets) - 3);
+
+		if field_count == 0 {
+			return;
+		}
+
+		data := (^rawptr)(array)^;
+
+		len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
+
+
+		soa_offset := 0;
+		item_offset := 0;
+
+		arg_copy := arg;
+		arg_ptr := &arg_copy;
+
+		max_align := 0;
+		for i in 0..<field_count {
+			type := si.types[i].variant.(Type_Info_Pointer).elem;
+			max_align = max(max_align, type.align);
+
+			soa_offset  = mem.align_forward_int(soa_offset, type.align);
+			item_offset = mem.align_forward_int(item_offset, type.align);
+
+			dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
+			src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
+			mem_copy(dst, src, type.size);
+
+			soa_offset  += type.size * cap(array);
+			item_offset += type.size;
+		}
+
+		len_ptr^ += arg_len;
+	}
+}
+
+@builtin
+append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
+	if array == nil do return;
+
+	arg_len := len(args);
+	if arg_len == 0 {
+		return;
+	}
+
+	if cap(array) <= len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve_soa(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		ti := type_info_of(typeid_of(T));
+		ti = type_info_base(ti);
+		si := &ti.variant.(Type_Info_Struct);
+		field_count := uintptr(len(si.offsets) - 3);
+
+		if field_count == 0 {
+			return;
+		}
+
+		data := (^rawptr)(array)^;
+
+		len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
+
+
+		soa_offset := 0;
+		item_offset := 0;
+
+		args_ptr := &args[0];
+
+		max_align := 0;
+		for i in 0..<field_count {
+			type := si.types[i].variant.(Type_Info_Pointer).elem;
+			max_align = max(max_align, type.align);
+
+			soa_offset  = mem.align_forward_int(soa_offset, type.align);
+			item_offset = mem.align_forward_int(item_offset, type.align);
+
+			dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
+			src := uintptr(args_ptr) + uintptr(item_offset);
+			for j in 0..<arg_len {
+				d := rawptr(dst + uintptr(j*type.size));
+				s := rawptr(src + uintptr(j*size_of(E)));
+				mem_copy(d, s, type.size);
+			}
+
+			soa_offset  += type.size * cap(array);
+			item_offset += type.size;
+		}
+
+		len_ptr^ += arg_len;
+	}
+}
+
 @builtin append :: proc{append_elem, append_elems};
+@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
 
 
 

+ 10 - 1
examples/demo/demo.odin

@@ -1727,7 +1727,8 @@ soa_struct_layout :: proc() {
 	}
 	{
 		// SOA Slices
-		Vector3 :: struct {x, y, z: f32};
+		// Vector3 :: struct {x, y, z: f32};
+		Vector3 :: struct {x: i8, y: i16, z: f32};
 
 		N :: 3;
 		v: #soa[N]Vector3;
@@ -1744,6 +1745,14 @@ soa_struct_layout :: proc() {
 		a := s[1:2];
 		assert(len(a) == 1);
 		fmt.println(a);
+
+		d: #soa[dynamic]Vector3;
+
+		append_soa(&d, Vector3{1, 2, 3}, Vector3{4, 5, 9}, Vector3{-4, -4, 3});
+		fmt.println(d);
+		fmt.println(len(d));
+		fmt.println(cap(d));
+		fmt.println(d[:]);
 	}
 }
 

+ 26 - 0
src/check_expr.cpp

@@ -94,6 +94,8 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
 
 
 Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
+Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
+
 
 
 Entity *entity_from_expr(Ast *expr) {
@@ -924,6 +926,30 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
 
 	case Type_Struct:
 		if (source->kind == Type_Struct) {
+			if (poly->Struct.soa_kind == source->Struct.soa_kind) {
+				bool ok = is_polymorphic_type_assignable(c, poly->Struct.soa_elem, source->Struct.soa_elem, true, modify_type);
+				if (ok) switch (source->Struct.soa_kind) {
+				case StructSoa_Fixed:
+				default:
+					GB_PANIC("Unhandled SOA Kind");
+					break;
+
+				case StructSoa_Slice:
+					if (modify_type) {
+						Type *type = make_soa_struct_slice(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
+						gb_memmove(poly, type, gb_size_of(*type));
+					}
+					break;
+				case StructSoa_Dynamic:
+					if (modify_type) {
+						Type *type = make_soa_struct_dynamic_array(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
+						gb_memmove(poly, type, gb_size_of(*type));
+					}
+					break;
+				}
+				return ok;
+
+			}
 			// return check_is_assignable_to(c, &o, poly);
 		}
 		return false;

+ 253 - 99
src/check_type.cpp

@@ -2702,9 +2702,98 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
 	// error(node, "'map' types are not yet implemented");
 }
 
-Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem) {
+Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type) {
 	Type *bt_elem = base_type(elem);
+
 	if (!is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
+		gbString str = type_to_string(elem);
+		error(elem_expr, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str);
+		gb_string_free(str);
+		return alloc_type_array(elem, count, generic_type);
+	}
+
+	Type *soa_struct = nullptr;
+	Scope *scope = nullptr;
+
+	if (is_type_array(elem)) {
+		Type *old_array = base_type(elem);
+		soa_struct = alloc_type_struct();
+		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_array->Array.count);
+		soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_array->Array.count);
+		soa_struct->Struct.node = array_typ_expr;
+		soa_struct->Struct.soa_kind = StructSoa_Fixed;
+		soa_struct->Struct.soa_elem = elem;
+		soa_struct->Struct.soa_count = count;
+
+		scope = create_scope(ctx->scope, ctx->allocator);
+		soa_struct->Struct.scope = scope;
+
+		String params_xyzw[4] = {
+			str_lit("x"),
+			str_lit("y"),
+			str_lit("z"),
+			str_lit("w")
+		};
+
+		for (i64 i = 0; i < old_array->Array.count; i++) {
+			Type *array_type = alloc_type_array(old_array->Array.elem, count);
+			Token token = {};
+			token.string = params_xyzw[i];
+
+			Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
+			soa_struct->Struct.fields[i] = new_field;
+			add_entity(ctx->checker, scope, nullptr, new_field);
+			add_entity_use(ctx, nullptr, new_field);
+		}
+
+	} else {
+		GB_ASSERT(is_type_struct(elem));
+
+		Type *old_struct = base_type(elem);
+		soa_struct = alloc_type_struct();
+		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count);
+		soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_struct->Struct.tags.count);
+		soa_struct->Struct.node = array_typ_expr;
+		soa_struct->Struct.soa_kind = StructSoa_Fixed;
+		soa_struct->Struct.soa_elem = elem;
+		soa_struct->Struct.soa_count = count;
+
+		scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+		soa_struct->Struct.scope = scope;
+
+		for_array(i, old_struct->Struct.fields) {
+			Entity *old_field = old_struct->Struct.fields[i];
+			if (old_field->kind == Entity_Variable) {
+				Type *array_type = alloc_type_array(old_field->type, count);
+				Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index);
+				soa_struct->Struct.fields[i] = new_field;
+				add_entity(ctx->checker, scope, nullptr, new_field);
+				add_entity_use(ctx, nullptr, new_field);
+			} else {
+				soa_struct->Struct.fields[i] = old_field;
+			}
+
+			soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
+		}
+	}
+
+	Token token = {};
+	token.string = str_lit("Base_Type");
+	Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
+	add_entity(ctx->checker, scope, nullptr, base_type_entity);
+
+	add_type_info_type(ctx, soa_struct);
+
+	return soa_struct;
+}
+
+Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem) {
+	Type *bt_elem = base_type(elem);
+
+
+	bool is_polymorphic = is_type_polymorphic(elem);
+
+	if (!is_polymorphic && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
 		GB_ASSERT(elem_expr != nullptr);
 
 		gbString str = type_to_string(elem);
@@ -2716,9 +2805,25 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 	Type *soa_struct = nullptr;
 	Scope *scope = nullptr;
 
-	if (is_type_array(elem)) {
+	isize field_count = 0;
+
+	if (is_polymorphic) {
+		field_count = 0;
+
+		soa_struct = alloc_type_struct();
+		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+1);
+		soa_struct->Struct.tags = array_make<String>(heap_allocator(), field_count+1);
+		soa_struct->Struct.node = array_typ_expr;
+		soa_struct->Struct.soa_kind = StructSoa_Slice;
+		soa_struct->Struct.soa_elem = elem;
+		soa_struct->Struct.soa_count = 0;
+		soa_struct->Struct.is_polymorphic = true;
+
+		scope = create_scope(ctx->scope, ctx->allocator);
+		soa_struct->Struct.scope = scope;
+	} else if (is_type_array(elem)) {
 		Type *old_array = base_type(elem);
-		isize field_count = old_array->Array.count;
+		field_count = old_array->Array.count;
 
 		soa_struct = alloc_type_struct();
 		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+1);
@@ -2750,17 +2855,11 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 			add_entity_use(ctx, nullptr, new_field);
 		}
 
-		Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
-		soa_struct->Struct.fields[field_count] = len_field;
-		add_entity(ctx->checker, scope, nullptr, len_field);
-		add_entity_use(ctx, nullptr, len_field);
-
-
 	} else {
 		GB_ASSERT(is_type_struct(elem));
 
 		Type *old_struct = base_type(elem);
-		isize field_count = old_struct->Struct.fields.count;
+		field_count = old_struct->Struct.fields.count;
 
 		soa_struct = alloc_type_struct();
 		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+1);
@@ -2789,13 +2888,140 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
 			soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
 		}
 
-		Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
-		soa_struct->Struct.fields[field_count] = len_field;
-		add_entity(ctx->checker, scope, nullptr, len_field);
-		add_entity_use(ctx, nullptr, len_field);
+	}
+	Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
+	soa_struct->Struct.fields[field_count] = len_field;
+	add_entity(ctx->checker, scope, nullptr, len_field);
+	add_entity_use(ctx, nullptr, len_field);
+
+	Token token = {};
+	token.string = str_lit("Base_Type");
+	Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
+	add_entity(ctx->checker, scope, nullptr, base_type_entity);
+
+	add_type_info_type(ctx, soa_struct);
+
+	return soa_struct;
+}
+
+
+Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem) {
+	Type *bt_elem = base_type(elem);
+
+	bool is_polymorphic = is_type_polymorphic(elem);
+
+	if (!is_polymorphic && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
+		GB_ASSERT(elem_expr != nullptr);
+
+		gbString str = type_to_string(elem);
+		error(elem_expr, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str);
+		gb_string_free(str);
+		return alloc_type_dynamic_array(elem);
+	}
+
+	Type *soa_struct = nullptr;
+	Scope *scope = nullptr;
+
+	isize field_count = 0;
+
+	if (is_polymorphic) {
+		field_count = 0;
+
+		soa_struct = alloc_type_struct();
+		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+3);
+		soa_struct->Struct.tags = array_make<String>(heap_allocator(), field_count+3);
+		soa_struct->Struct.node = array_typ_expr;
+		soa_struct->Struct.soa_kind = StructSoa_Dynamic;
+		soa_struct->Struct.soa_elem = elem;
+		soa_struct->Struct.soa_count = 0;
+		soa_struct->Struct.is_polymorphic = true;
+
+		scope = create_scope(ctx->scope, ctx->allocator);
+		soa_struct->Struct.scope = scope;
+	} else if (is_type_array(elem)) {
+		Type *old_array = base_type(elem);
+		field_count = old_array->Array.count;
+
+		soa_struct = alloc_type_struct();
+		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+3);
+		soa_struct->Struct.tags = array_make<String>(heap_allocator(), field_count+3);
+		soa_struct->Struct.node = array_typ_expr;
+		soa_struct->Struct.soa_kind = StructSoa_Dynamic;
+		soa_struct->Struct.soa_elem = elem;
+		soa_struct->Struct.soa_count = 0;
+
+		scope = create_scope(ctx->scope, ctx->allocator);
+		soa_struct->Struct.scope = scope;
+
+		String params_xyzw[4] = {
+			str_lit("x"),
+			str_lit("y"),
+			str_lit("z"),
+			str_lit("w")
+		};
+
+		for (i64 i = 0; i < field_count; i++) {
+			Type *array_type = alloc_type_pointer(old_array->Array.elem);
+			Token token = {};
+			token.string = params_xyzw[i];
+
+			Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
+			new_field->flags |= EntityFlag_SoaPtrField;
+			soa_struct->Struct.fields[i] = new_field;
+			add_entity(ctx->checker, scope, nullptr, new_field);
+			add_entity_use(ctx, nullptr, new_field);
+		}
+	} else {
+		GB_ASSERT(is_type_struct(elem));
+
+		Type *old_struct = base_type(elem);
+		field_count = old_struct->Struct.fields.count;
+
+		soa_struct = alloc_type_struct();
+		soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+3);
+		soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_struct->Struct.tags.count+3);
+		soa_struct->Struct.node = array_typ_expr;
+		soa_struct->Struct.soa_kind = StructSoa_Dynamic;
+		soa_struct->Struct.soa_elem = elem;
+		soa_struct->Struct.soa_count = 0;
+
+		scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
+		soa_struct->Struct.scope = scope;
+
+		for_array(i, old_struct->Struct.fields) {
+			Entity *old_field = old_struct->Struct.fields[i];
+			if (old_field->kind == Entity_Variable) {
+				Type *array_type = alloc_type_pointer(old_field->type);
+				Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index);
+				new_field->flags |= EntityFlag_SoaPtrField;
+				soa_struct->Struct.fields[i] = new_field;
+				add_entity(ctx->checker, scope, nullptr, new_field);
+				add_entity_use(ctx, nullptr, new_field);
+			} else {
+				soa_struct->Struct.fields[i] = old_field;
+			}
+
+			soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
+		}
 	}
 
+	Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
+	soa_struct->Struct.fields[field_count+0] = len_field;
+	add_entity(ctx->checker, scope, nullptr, len_field);
+	add_entity_use(ctx, nullptr, len_field);
+
+	Entity *cap_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
+	soa_struct->Struct.fields[field_count+1] = cap_field;
+	add_entity(ctx->checker, scope, nullptr, cap_field);
+	add_entity_use(ctx, nullptr, cap_field);
+
 	Token token = {};
+	token.string = str_lit("allocator");
+	Entity *allocator_field = alloc_entity_field(scope, token, t_allocator, false, cast(i32)field_count);
+	soa_struct->Struct.fields[field_count+2] = allocator_field;
+	add_entity(ctx->checker, scope, nullptr, allocator_field);
+	add_entity_use(ctx, nullptr, allocator_field);
+
 	token.string = str_lit("Base_Type");
 	Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
 	add_entity(ctx->checker, scope, nullptr, base_type_entity);
@@ -2992,90 +3218,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 				GB_ASSERT(at->tag->kind == Ast_BasicDirective);
 				String name = at->tag->BasicDirective.name;
 				if (name == "soa") {
-					Type *bt_elem = base_type(elem);
-					if (!is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
-						gbString str = type_to_string(elem);
-						error(at->elem, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str);
-						gb_string_free(str);
-						*type = alloc_type_array(elem, count, generic_type);
-						goto array_end;
-					}
-
-					Type *soa_struct = nullptr;
-					Scope *scope = nullptr;
-
-					if (is_type_array(elem)) {
-						Type *old_array = base_type(elem);
-						soa_struct = alloc_type_struct();
-						soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_array->Array.count);
-						soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_array->Array.count);
-						soa_struct->Struct.node = e;
-						soa_struct->Struct.soa_kind = StructSoa_Fixed;
-						soa_struct->Struct.soa_elem = elem;
-						soa_struct->Struct.soa_count = count;
-
-						scope = create_scope(ctx->scope, ctx->allocator);
-						soa_struct->Struct.scope = scope;
-
-						String params_xyzw[4] = {
-							str_lit("x"),
-							str_lit("y"),
-							str_lit("z"),
-							str_lit("w")
-						};
-
-						for (i64 i = 0; i < old_array->Array.count; i++) {
-							Type *array_type = alloc_type_array(old_array->Array.elem, count);
-							Token token = {};
-							token.string = params_xyzw[i];
-
-							Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
-							soa_struct->Struct.fields[i] = new_field;
-							add_entity(ctx->checker, scope, nullptr, new_field);
-							add_entity_use(ctx, nullptr, new_field);
-						}
-
-					} else {
-						GB_ASSERT(is_type_struct(elem));
-
-						Type *old_struct = base_type(elem);
-						soa_struct = alloc_type_struct();
-						soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count);
-						soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_struct->Struct.tags.count);
-						soa_struct->Struct.node = e;
-						soa_struct->Struct.soa_kind = StructSoa_Fixed;
-						soa_struct->Struct.soa_elem = elem;
-						soa_struct->Struct.soa_count = count;
-
-						scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
-						soa_struct->Struct.scope = scope;
-
-						for_array(i, old_struct->Struct.fields) {
-							Entity *old_field = old_struct->Struct.fields[i];
-							if (old_field->kind == Entity_Variable) {
-								Type *array_type = alloc_type_array(old_field->type, count);
-								Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index);
-								soa_struct->Struct.fields[i] = new_field;
-								add_entity(ctx->checker, scope, nullptr, new_field);
-								add_entity_use(ctx, nullptr, new_field);
-							} else {
-								soa_struct->Struct.fields[i] = old_field;
-							}
-
-							soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
-						}
-					}
-
-					Token token = {};
-					token.string = str_lit("Base_Type");
-					Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
-					add_entity(ctx->checker, scope, nullptr, base_type_entity);
-
-					add_type_info_type(ctx, soa_struct);
-
-					*type = soa_struct;
-
-
+					*type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
 				} else if (name == "vector") {
 					if (!is_type_valid_vector_elem(elem)) {
 						gbString str = type_to_string(elem);
@@ -3116,7 +3259,18 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 
 	case_ast_node(dat, DynamicArrayType, e);
 		Type *elem = check_type(ctx, dat->elem);
-		*type = alloc_type_dynamic_array(elem);
+		if (dat->tag != nullptr) {
+			GB_ASSERT(dat->tag->kind == Ast_BasicDirective);
+			String name = dat->tag->BasicDirective.name;
+			if (name == "soa") {
+				*type = make_soa_struct_dynamic_array(ctx, e, dat->elem, elem);
+			} else {
+				error(dat->tag, "Invalid tag applied to dynamic array, got #%.*s", LIT(name));
+				*type = alloc_type_dynamic_array(elem);
+			}
+		} else {
+			*type = alloc_type_dynamic_array(elem);
+		}
 		set_base_type(named_type, *type);
 		return true;
 	case_end;

+ 13 - 2
src/ir.cpp

@@ -8011,8 +8011,19 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
 						irValue *new_len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
 						ir_emit_store(proc, len_dst, new_len);
 					}
-				} else {
-					GB_PANIC("TODO #soa[dynamic]T");
+				} else if (type->Struct.soa_kind == StructSoa_Dynamic) {
+					i32 field_count = cast(i32)type->Struct.fields.count - 3;
+					for (i32 i = 0; i < field_count; i++) {
+						irValue *field_dst = ir_emit_struct_ep(proc, dst, i);
+						irValue *field_src = ir_emit_struct_ev(proc, base, i);
+						field_src = ir_emit_ptr_offset(proc, field_src, low);
+						ir_emit_store(proc, field_dst, field_src);
+					}
+
+
+					irValue *len_dst = ir_emit_struct_ep(proc, dst, field_count);
+					irValue *new_len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+					ir_emit_store(proc, len_dst, new_len);
 				}
 
 				return ir_addr(dst);