}
```
-[ ] Polymorphic structs do not recognize default values for members.
+[X] Polymorphic structs do not recognize default values for members.
[ ] `use` on struct members does not work correctly if the type is a union.
```
--- /dev/null
+Currently, there are many janky things about structs in Onyx. I want
+to revisit all of the code related to them and fix all the issues I
+am having. To recap, here are all of the features that are required
+for structs to work:
+ * #union
+ * #size
+ * #align
+ * `use` members
+ * struct literals
+ * polymorphic structs
+
+All of these features work well individually, but when used together
+(such as struct literals over polymoprhic structs), they have many
+issues.
+
+Here is a brief explanation of the current data model:
+ * AstStruct contains:
+ - an array of AstStructMembers
+ - the minimum size
+ - the minimum alignment
+
+ * AstStructMembers contain:
+ - A symbol (token) for the name
+ - the initial value
+
+ * AstStructLiterals contain:
+ - an array of values (unnamed)
+ - an array of AstStructMembers representing named values
+
+ * AstPolyStructs contain:
+ - the base AstStruct
+ - an array of tokens representing the parameters
+ - the scope the struct was defined in
+
+In principle, this is the simple way each of the features should work:
+ * #union
+ - use 'max' instead of '+' when computing struct size
+ - all members have offset 0
+ * #size
+ - the size is the minimum of the size given and the computed size
+ * #align
+ - the alignment is the minimum of the alignment given and the computed alignment
+ * `use` members
+ - adds members of used type to member list, with proper offsets
+ * struct literals
+ - ensures all values needed for the struct are present (no
+ automatic zero values)
+ * polymorphic structs
+ - copy the base struct, introduce the type symbols, resolve symbols, win
// be many struct members, and iterating through an array would be
// easier and less costly. - brendanfh 2020/09/17
char *name;
+
+ struct AstTyped* initial_value;
+
+ b32 member_was_used : 1;
} StructMember;
#define TYPE_KINDS \
printf("%p\n", a.int);
printf("%f\n", a.float);
- e := Entity.{ pos = Vec2.{ 1, 2 } };
+ e := Entity.{ Vec2.{ 1, 2 } };
{
foo : [5] [2] u32;
}
if (range->step == NULL) {
- // HACK: This relies on the third member of the 'range' struct to exist, be the step,
- // and have an initial_value.
- AstStructMember* step_member = ((AstStructType *) builtin_range_type)->members[2];
- if (check_expression(&step_member->initial_value)) return 1;
+ type_lookup_member(expected_range_type, "step", &smem);
+ assert(smem.initial_value != NULL);
+ if (check_expression(&smem.initial_value)) return 1;
- range->step = step_member->initial_value;
+ range->step = smem.initial_value;
}
return 0;
return;
}
+ if (s.member_was_used) {
+ (*smem)->flags |= Ast_Flag_Struct_Mem_Used;
+ }
+
sl->values[s.idx] = (*smem)->initial_value;
}
if (sl->type->kind == Type_Kind_Struct) {
- AstStructType* st = (AstStructType *) sl->type_node;
bh_arr_each(StructMember*, smem, sl->type->Struct.memarr) {
u32 idx = (*smem)->idx;
if (sl->values[idx] == NULL) {
- if (st->kind != Ast_Kind_Struct_Type || st->members[idx]->initial_value == NULL) {
+ if ((*smem)->initial_value == NULL) {
onyx_report_error(sl->token->pos, "No value was given for the field '%s'.", (*smem)->name);
return;
}
- sl->values[idx] = st->members[idx]->initial_value;
+ sl->values[idx] = (*smem)->initial_value;
}
}
}
.type = (*member)->type,
.idx = idx,
.name = bh_strdup(alloc, (*member)->token->text),
+ .member_was_used = ((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0,
+ .initial_value = (*member)->initial_value,
};
bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+ smem->member_was_used = 0;
+ smem->initial_value = NULL;
+
switch (type->kind) {
case Type_Kind_Struct: {
TypeStruct* stype = &type->Struct;
--- /dev/null
+Testing a simple structure.
+SimpleStruct<16, 4>(41, 67, Steve)
+
+
+Testing a simple union.
+0x3F000000 == 0x3F000000
+
+
+Testing a struct with default values.
+DefaultedStruct(0, 1.0, 2, 3.0)
+DefaultedStruct(3, 1.0, 2, 0.0)
+
+
+Testing a struct with `use`.
+StructWithUse(1234, (1.0, 2.0), 5678)
+StructWithUse(1234, (1.0, 2.0), 5678)
+
+
+Testing a polymorphic struct.
+PolyStruct<i32, f32>(1234, 5678.0)
+PolyStruct<f32, i32>(1234.0, 5678)
+
+
+Testing a polymorphic struct with default values.
+PolyStruct<i32, f32>(1234, 5678.0)
--- /dev/null
+use package core
+
+main :: proc (args: [] cstr) {
+
+ test_simple_struct();
+ test_simple_union();
+ test_default_values();
+ test_simple_use();
+ test_polymorphic();
+ test_polymorphic_with_defaults();
+
+ test_simple_struct :: proc () {
+ println("Testing a simple structure.");
+
+ SimpleStruct :: struct {
+ age : u16;
+ height : u32;
+
+ name : str;
+ }
+
+ ss := SimpleStruct.{ 41, 67, "Steve" };
+
+ printf("SimpleStruct<%i, %i>(%i, %i, %s)\n",
+ sizeof SimpleStruct,
+ alignof SimpleStruct,
+ cast(u32) ss.age, ss.height, ss.name);
+ }
+
+ test_simple_union :: proc () {
+ println("\n\nTesting a simple union.");
+
+ SimpleUnion :: struct #union {
+ int_val : i32;
+ float_val : f32;
+ }
+
+ u : SimpleUnion;
+ u.float_val = 0.5;
+
+ printf("%p == 0x3F000000\n", u.int_val);
+ }
+
+ test_default_values :: proc () {
+ println("\n\nTesting a struct with default values.");
+
+ DefaultedStruct :: struct {
+ i : i32 = 0;
+ f : f32 = 1;
+ l : i64 = 2;
+ d : f64 = 3;
+ }
+
+ ds1 := DefaultedStruct.{};
+ print_defaulted(ds1);
+
+ ds2 := DefaultedStruct.{ i = 3, d = 0 };
+ print_defaulted(ds2);
+
+ print_defaulted :: proc (use ds: DefaultedStruct) {
+ printf("DefaultedStruct(%i, %f, %l, %d)\n", i, f, l, d);
+ }
+ }
+
+ test_simple_use :: proc () {
+ println("\n\nTesting a struct with `use`.");
+
+ StructWithUse :: struct {
+ first_member : i32;
+
+ use used_member : UsedMember;
+
+ last_member : i32;
+ }
+
+ UsedMember :: struct {
+ x : f32;
+ y : f32;
+ }
+
+ // This does not work, but it should.
+ // swu := StructWithUse.{ 1234, UsedMember.{ 1, 2 }, 5678 };
+
+ // Neither does this, but it also should.
+ // swu := StructWithUse.{
+ // first_member = 1234,
+ // used_member = UsedMember.{ 1, 2 },
+ // last_member = 5678,
+ // };
+
+ // This does, when all non-used members are listed out.
+ swu := StructWithUse.{ 1234, 1, 2, 5678 };
+
+ // This also does.
+ swu2 := StructWithUse.{
+ first_member = 1234,
+ x = 1,
+ y = 2,
+ last_member = 5678,
+ };
+
+ print_swu :: proc (use swu: StructWithUse) {
+ printf("StructWithUse(%i, (%f, %f), %i)\n",
+ first_member, x, y, last_member);
+ }
+
+ print_swu(swu);
+ print_swu(swu2);
+ }
+
+ test_polymorphic :: proc () {
+ println("\n\nTesting a polymorphic struct.");
+
+ PolyStruct :: struct ($T, $R) {
+ t_data : T;
+ r_data : R;
+ }
+
+ ps1 : PolyStruct(i32, f32);
+ ps1.t_data = 1234;
+ ps1.r_data = 5678;
+
+ printf("PolyStruct<i32, f32>(%i, %f)\n", ps1.t_data, ps1.r_data);
+
+ // Currently, this is how you have to do this.
+ ps2 := (#type PolyStruct(f32, i32)).{ 1234, 5678 };
+ printf("PolyStruct<f32, i32>(%f, %i)\n", ps2.t_data, ps2.r_data);
+ }
+
+ test_polymorphic_with_defaults :: proc () {
+ println("\n\nTesting a polymorphic struct with default values.");
+
+ PolyStruct :: struct ($T, $R) {
+ t_data : T = 1234;
+ r_data : R = 5678;
+ }
+
+ PolyStructTyped :: #type PolyStruct(i32, f32);
+
+ ps := PolyStructTyped.{};
+ printf("PolyStruct<i32, f32>(%i, %f)\n", ps.t_data, ps.r_data);
+ }
+}
+
+#include_file "core/std/js"
\ No newline at end of file