working on making structs better as a whole
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 30 Dec 2020 03:34:32 +0000 (21:34 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 30 Dec 2020 03:34:32 +0000 (21:34 -0600)
docs/bugs
docs/struct_fixes [new file with mode: 0644]
include/onyxtypes.h
onyx
progs/odin_example.onyx
src/onyxchecker.c
src/onyxsymres.c
src/onyxtypes.c
tests/struct_robustness [new file with mode: 0644]
tests/struct_robustness.onyx [new file with mode: 0644]

index 10e43eaa64aff58a7387f63eba9539ca5c1402e5..b5c6a35d0906b364c8f79d0633e69bb382a6d34c 100644 (file)
--- a/docs/bugs
+++ b/docs/bugs
@@ -53,7 +53,7 @@ List of known bugs:
     }
     ```
 
-[ ] 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.
     ```
diff --git a/docs/struct_fixes b/docs/struct_fixes
new file mode 100644 (file)
index 0000000..e9310b7
--- /dev/null
@@ -0,0 +1,49 @@
+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
index 15bf08566e50368257754c9545fd6e26d67ca3f1..542516395d725e8c32ece055d1e24e0e1a7c0d1b 100644 (file)
@@ -67,6 +67,10 @@ typedef struct StructMember {
     // 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 \
diff --git a/onyx b/onyx
index 217ea22c9838e25bdcd2712d5c3df742d11c5f87..2722db43ed545bbccaeeb50a9f94c0cc7419695f 100755 (executable)
Binary files a/onyx and b/onyx differ
index 9de1b6d246d2edd9e5ab9ad5843c653a65225473..090eadd6355798bee511956642291780832c00a6 100644 (file)
@@ -93,7 +93,7 @@ main :: proc (args: [] cstr) {
     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;
index 8a2610ed08d81c0964a2a912ed57ab14f8341e2b..40be178029beabce8939658508d345ba231a4982 100644 (file)
@@ -1008,12 +1008,11 @@ b32 check_range_literal(AstRangeLiteral** prange) {
     }
 
     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;
index 5f7a668ad222b4ecbeae0633a96154595898213a..2d0921c76632ea3e1d99844ae00aefb589454ad3 100644 (file)
@@ -356,21 +356,24 @@ static void symres_struct_literal(AstStructLiteral* sl) {
                 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;
                 }
             }
         }
index 0145f72ed6acb9caaa0a49b569d98bb89ce776e0..31ffcc044364bf58194d23608c19bd1e51ff03b3 100644 (file)
@@ -367,6 +367,8 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
                     .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);
@@ -647,6 +649,9 @@ u32 type_get_alignment_log2(Type* type) {
 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;
diff --git a/tests/struct_robustness b/tests/struct_robustness
new file mode 100644 (file)
index 0000000..e78fd20
--- /dev/null
@@ -0,0 +1,25 @@
+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)
diff --git a/tests/struct_robustness.onyx b/tests/struct_robustness.onyx
new file mode 100644 (file)
index 0000000..1391139
--- /dev/null
@@ -0,0 +1,145 @@
+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