added polymorphic structs; must testing will be needed
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 14 Sep 2020 20:47:26 +0000 (15:47 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 14 Sep 2020 20:47:26 +0000 (15:47 -0500)
18 files changed:
.vimspector.json
CHANGELOG [new file with mode: 0644]
core/i32map.onyx [new file with mode: 0644]
core/ptrmap.onyx
core/std/js.onyx
core/std/wasi.onyx
include/onyxastnodes.h
include/onyxsempass.h
include/onyxtypes.h
onyx
progs/poly_struct.onyx [new file with mode: 0644]
src/onyx.c
src/onyxchecker.c
src/onyxclone.c
src/onyxparser.c
src/onyxsymres.c
src/onyxtypes.c
src/onyxutils.c

index 5cd4edea86e32015233103e6784fbc00556d0f7e..ea4cd749f4be96898328375528453ef648a5f544 100644 (file)
@@ -6,7 +6,7 @@
                 "type": "cppdbg",
                 "request": "launch",
                 "program": "${workspaceFolder}/onyx",
-                "args": ["-verbose", "progs/simd_test.onyx"],
+                "args": ["-verbose", "progs/poly_struct.onyx"],
                 "stopAtEntry": true,
                 "cwd": "${workspaceFolder}",
                 "environment": [],
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..a506ae9
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,13 @@
+Release v0.0.3
+--------------
+Additions:
+* Added polymorphic structs
+* Added polymorphic structs to polymorphic pattern matching
+
+Removals:
+
+Changes:
+
+Bug fixes:
+* Fixed error message index for struct literal member type mismatch.
+* Fixed freeing non-allocated memory when generating documentation.
diff --git a/core/i32map.onyx b/core/i32map.onyx
new file mode 100644 (file)
index 0000000..e0932fa
--- /dev/null
@@ -0,0 +1,109 @@
+package core
+
+I32Map :: struct ($T) {
+    hashes  : [..] i32;
+    entries : [..] I32MapEntry(T);
+}
+
+I32MapEntry :: struct ($T) {
+    key   : i32;
+    next  : i32;
+    value : T;
+}
+
+i32map_init :: proc (imap: ^I32Map($T), hash_count: i32 = 16) {
+    array_init(^imap.hashes, hash_count);
+    array_init(^imap.entries, 4);
+
+    for i: 0 .. hash_count do array_push(^imap.hashes, -1);
+}
+
+i32map_free :: proc (imap: ^I32Map($T)) {
+    array_free(^imap.hashes);
+    array_free(^imap.entries);
+}
+
+i32map_put :: proc (imap: ^I32Map($T), key: i32, value: T) {
+    lr := i32map_lookup(imap, key);
+
+    if lr.entry_index >= 0 {
+        imap.entries[lr.entry_index].value = value;
+        return;
+    }
+
+    entry : I32MapEntry(T);
+    entry.key = key;
+    entry.value = value;
+    entry.next = imap.hashes[lr.hash_index];
+
+    array_push(^imap.entries, entry);
+
+    imap.hashes[lr.hash_index] = imap.entries.count - 1;
+}
+
+i32map_has :: proc (imap: ^I32Map($T), key: i32) -> bool {
+    lr := i32map_lookup(imap, key);
+    return lr.entry_index >= 0;
+}
+
+i32map_get :: proc (imap: ^I32Map($T), key: i32) -> i32 {
+    lr := i32map_lookup(imap, key);
+    if lr.entry_index >= 0 do return imap.entries[lr.entry_index].value;
+
+    return null;
+}
+
+i32map_delete :: proc (imap: ^I32Map($T), key: i32) {
+    lr := i32map_lookup(imap, key);
+    if lr.entry_index < 0 do return;
+
+    if lr.entry_prev < 0   do imap.hashes[lr.hash_index] = imap.entries[lr.entry_index].next;
+    else                   do imap.hashes[lr.entry_prev] = imap.entries[lr.entry_index].next;
+
+    if lr.entry_index == imap.entries.count - 1 {
+        array_pop(^imap.entries);
+        return;
+    }
+
+    array_fast_delete(^imap.entries, lr.entry_index);
+    last := i32map_lookup(imap, imap.entries[lr.entry_index].key);
+    if last.entry_prev >= 0    do imap.entries[last.entry_prev].next = lr.entry_index;
+    else                       do imap.hashes[last.hash_index] = lr.entry_index;
+}
+
+i32map_clear :: proc (imap: ^I32Map($T)) {
+    for i: 0 .. imap.hashes.count do imap.hashes.data[i] = -1;
+    imap.entries.count = 0;
+}
+
+
+
+//
+// Private symbols
+//
+
+#private
+I32MapLookupResult :: struct {
+    hash_index  : i32 = -1;
+    entry_index : i32 = -1;
+    entry_prev  : i32 = -1;
+}
+
+#private
+i32map_lookup :: proc (imap: ^I32Map($T), key: i32) -> I32MapLookupResult {
+    lr := I32MapLookupResult.{};
+
+    hash := cast(u32) 0xcbf29ce7 ^ cast(u32) key;
+
+    lr.hash_index = hash % imap.hashes.count;
+    lr.entry_index = imap.hashes[lr.hash_index];
+
+    while lr.entry_index >= 0 {
+        if imap.entries[lr.entry_index].key == key do return lr;
+
+        lr.entry_prev = lr.entry_index;
+        lr.entry_index = imap.entries[lr.entry_index].next;
+    }
+
+    return lr;
+}
index 700e77b80e49dece6edd104885d354437e60178e..bf6482c67684dfc1c8b6ca3bea14565700d0d104 100644 (file)
@@ -25,7 +25,6 @@ ptrmap_free :: proc (use pmap: ^PtrMap) {
     array_free(^entries);
 }
 
-
 ptrmap_put :: proc (use pmap: ^PtrMap, key: rawptr, value: rawptr) {
     lr := ptrmap_lookup(pmap, key);
 
index 9df405cc8ed3bc88187365e59f5189a21b01e6fe..9506070c9c34e72b46aed3757bf9188ee14968ac 100644 (file)
@@ -4,6 +4,7 @@ package core
 
 #include_file "core/alloc"
 #include_file "core/array"
+#include_file "core/i32map"
 #include_file "core/intrinsics"
 #include_file "core/math"
 #include_file "core/memory"
index b9f6e40d527bf02dc797dd7b104aa1451e8c63ec..938de5fac6067a4e58621b3772b3a03c33a10a4c 100644 (file)
@@ -5,6 +5,7 @@ package core
 #include_file "core/alloc"
 #include_file "core/array"
 #include_file "core/file"
+#include_file "core/i32map"
 #include_file "core/intrinsics"
 #include_file "core/math"
 #include_file "core/memory"
index 6907ae2638340149b79a593525b918760df814c8..08067a3c6a1b023e958a788a302ebc49b8a77a75 100644 (file)
@@ -44,6 +44,8 @@ typedef struct AstDynArrType AstDynArrType;
 typedef struct AstVarArgType AstVarArgType;
 typedef struct AstStructType AstStructType;
 typedef struct AstStructMember AstStructMember;
+typedef struct AstPolyStructType AstPolyStructType;
+typedef struct AstPolyCallType AstPolyCallType;
 typedef struct AstEnumType AstEnumType;
 typedef struct AstEnumValue AstEnumValue;
 typedef struct AstTypeAlias AstTypeAlias;
@@ -106,6 +108,8 @@ typedef enum AstKind {
     Ast_Kind_DynArr_Type,
     Ast_Kind_VarArg_Type,
     Ast_Kind_Struct_Type,
+    Ast_Kind_Poly_Struct_Type,
+    Ast_Kind_Poly_Call_Type,
     Ast_Kind_Enum_Type,
     Ast_Kind_Type_Alias,
     Ast_Kind_Type_Raw_Alias,
@@ -533,6 +537,21 @@ struct AstStructMember {
     AstTyped_base;
     AstTyped* initial_value;
 };
+struct AstPolyStructType {
+    AstType_base;
+
+    Scope *scope;
+    bh_arr(OnyxToken *) poly_params;
+    bh_table(AstStructType *) concrete_structs;
+
+    AstStructType* base_struct;
+};
+struct AstPolyCallType {
+    AstType_base;
+
+    AstType* callee;
+    bh_arr(AstType *) params;
+};
 struct AstEnumType {
     AstType_base;
     Scope *scope;
@@ -749,6 +768,8 @@ typedef enum PolyProcLookupMethod {
 } PolyProcLookupMethod;
 AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxFilePos pos);
 
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params);
+
 // NOTE: Useful inlined functions
 static inline b32 is_lval(AstNode* node) {
     return (node->kind == Ast_Kind_Local)
index 145795c5ceac374d3cd73561e8b00820f2305564..d5af9acfb6c665aaaad4e2f4134695fbef501134 100644 (file)
@@ -34,6 +34,7 @@ extern SemState semstate;
 // NOTE: Resolving all symbols in the tree
 void onyx_resolve_symbols();
 
+AstType* symres_type(AstType* type);
 void symres_function(AstFunction* func);
 
 // NOTE: Inferring and checking types in the tree
index f84609ad9841cd1ac14fd4f571554638d5655e5a..4be207ea6af80d99c2a3551076c5289afab2cf92 100644 (file)
@@ -76,6 +76,7 @@ typedef struct StructMember {
         u16 alignment, mem_count;                                 \
         bh_table(StructMember) members;                           \
         bh_arr(StructMember *) memarr;                            \
+        bh_arr(Type *)         poly_args;                         \
     })                                                            \
     TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
     TYPE_KIND(Slice, struct { Type *ptr_to_data; })               \
diff --git a/onyx b/onyx
index f33dd0bb0f1c23c96560b2baf2008069106b3f98..c0f66a7c0b4feffa46812bda08fafa01c1626186 100755 (executable)
Binary files a/onyx and b/onyx differ
diff --git a/progs/poly_struct.onyx b/progs/poly_struct.onyx
new file mode 100644 (file)
index 0000000..9e32c69
--- /dev/null
@@ -0,0 +1,19 @@
+package main
+
+#include_file "core/std/wasi"
+
+use package core
+
+
+main :: proc (args: [] cstring) {
+
+    imap : I32Map(string);
+    i32map_init(^imap);
+
+    i32map_put(^imap, 50, "Hello ");
+    i32map_put(^imap, 1234, "World!");
+
+    print(i32map_get(^imap, 50));
+    print(i32map_get(^imap, 1234));
+    print("\n");
+}
index 40608c76a82b26039536c34bd293793b347a14c6..89bc81b81303d942b93168dfc011ad2c1ee5129a 100644 (file)
@@ -279,7 +279,8 @@ static void merge_parse_results(CompilerState* compiler_state, ParseResults* res
             }
 
             case Ast_Kind_Type_Alias:
-            case Ast_Kind_Struct_Type: {
+            case Ast_Kind_Struct_Type:
+            case Ast_Kind_Poly_Struct_Type: {
                 ent.type = Entity_Type_Type_Alias;
                 ent.type_alias = (AstType *) node;
                 bh_arr_push(compiler_state->prog_info.entities, ent);
index 3a984c7013bb3e55eb7ffbe80797b3f4938f40f2..30d08d169888aea502590b5889d058aa94c631db 100644 (file)
@@ -795,6 +795,8 @@ b32 check_struct_literal(AstStructLiteral* sl) {
         type_lookup_member_by_idx(sl->type, i, &smem);
         Type* formal = smem.type;
 
+        // NOTE: We may want to report a different error if the struct member was named,
+        // since those names may not be in the correct order. - brendanfh   2020/09/12
         if (!types_are_compatible(formal, (*actual)->type)) {
             onyx_report_error(sl->token->pos,
                     "Mismatched types for %d%s member, expected '%s', got '%s'.",
@@ -918,6 +920,7 @@ b32 check_array_access(AstArrayAccess* aa) {
 b32 check_field_access(AstFieldAccess** pfield) {
     AstFieldAccess* field = *pfield;
     if (check_expression(&field->expr)) return 1;
+    if (field->expr->type == NULL) return 1;
 
     if (!type_is_structlike(field->expr->type)) {
         onyx_report_error(field->token->pos,
index 87a437924c7ae6d192fa589c970126f3739c84d3..ab4068bcd736ba53b5fd8fbe0c4276585e8491d0 100644 (file)
@@ -12,7 +12,6 @@ static inline b32 should_clone(AstNode* node) {
                case Ast_Kind_NumLit:
                case Ast_Kind_StrLit:
                case Ast_Kind_Package:
-               case Ast_Kind_Struct_Type:
                case Ast_Kind_Enum_Type:
                case Ast_Kind_Enum_Value:
                case Ast_Kind_Overloaded_Function:
@@ -54,6 +53,8 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
         case Ast_Kind_VarArg_Type: return sizeof(AstVarArgType);
         case Ast_Kind_Struct_Type: return sizeof(AstStructType);
+        case Ast_Kind_Poly_Struct_Type: return sizeof(AstPolyStructType);
+        case Ast_Kind_Poly_Call_Type: return sizeof(AstPolyCallType);
         case Ast_Kind_Enum_Type: return sizeof(AstEnumType);
         case Ast_Kind_Type_Alias: return sizeof(AstTypeAlias);
         case Ast_Kind_Type_Raw_Alias: return sizeof(AstTypeRawAlias);
@@ -273,6 +274,25 @@ AstNode* ast_clone(bh_allocator a, void* n) {
                        ((AstTypeAlias *) nn)->to = (AstType *) ast_clone(a, ((AstTypeAlias *) node)->to);
                        break;
 
+        case Ast_Kind_Struct_Type: {
+            AstStructType* ds = (AstStructType *) nn;
+            AstStructType* ss = (AstStructType *) node;
+
+            ds->members = NULL;
+            bh_arr_new(global_heap_allocator, ds->members, bh_arr_length(ss->members));
+
+            bh_arr_each(AstStructMember *, smem, ss->members) {
+                bh_arr_push(ds->members, (AstStructMember *) ast_clone(a, *smem));
+            }
+
+            ds->stcache = NULL;
+            break;
+        }
+
+        case Ast_Kind_Struct_Member:
+            ((AstStructMember *) nn)->initial_value = (AstTyped *) ast_clone(a, ((AstStructMember *) node)->initial_value);
+            break;
+
                case Ast_Kind_Function_Type:
                        ((AstFunctionType *) nn)->return_type = (AstType *) ast_clone(a, ((AstFunctionType *) node)->return_type);
                        ((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
index 67a9c17a5b0b3a90c19b06baef2756afc44b7851..66ec3cb700a2bb8e4f60710ad15f176b3e03707d 100644 (file)
@@ -1297,6 +1297,30 @@ static AstType* parse_type(OnyxParser* parser) {
                 *next_insertion = (AstType *) symbol_node;
             }
 
+            if (parser->curr->type == '(') {
+                consume_token(parser);
+
+                bh_arr(AstType *) params = NULL;
+                bh_arr_new(global_heap_allocator, params, 2);
+
+                while (parser->curr->type != ')') {
+                    if (parser->hit_unexpected_token) break;
+
+                    AstType* t = parse_type(parser);
+                    bh_arr_push(params, t);
+
+                    if (parser->curr->type != ')')
+                        expect_token(parser, ',');
+                }
+                expect_token(parser, ')');
+
+                AstPolyCallType* pc_type = make_node(AstPolyCallType, Ast_Kind_Poly_Call_Type);
+                pc_type->callee = *next_insertion;
+                pc_type->params = params;
+
+                *next_insertion = (AstType *) pc_type;
+            }
+
             next_insertion = NULL;
         }
 
@@ -1319,8 +1343,37 @@ static AstType* parse_type(OnyxParser* parser) {
 }
 
 static AstStructType* parse_struct(OnyxParser* parser) {
-    AstStructType* s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
-    s_node->token = expect_token(parser, Token_Type_Keyword_Struct);
+    OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
+
+    AstStructType* s_node;
+    AstPolyStructType* poly_struct = NULL;
+
+    s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
+    s_node->token = s_token;
+
+    if (parser->curr->type == '(') {
+        consume_token(parser);
+
+        bh_arr(OnyxToken *) poly_params = NULL;
+        bh_arr_new(global_heap_allocator, poly_params, 1);
+
+        while (parser->curr->type == '$') {
+            consume_token(parser);
+            if (parser->hit_unexpected_token) return NULL;
+
+            OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+            bh_arr_push(poly_params, sym_token);
+
+            if (parser->curr->type != ')')
+                expect_token(parser, ',');
+        }
+        expect_token(parser, ')');
+
+        poly_struct = make_node(AstPolyStructType, Ast_Kind_Poly_Struct_Type);
+        poly_struct->token = s_token;
+        poly_struct->poly_params = poly_params;
+        poly_struct->base_struct = s_node;
+    }
 
     bh_arr_new(global_heap_allocator, s_node->members, 4);
 
@@ -1380,7 +1433,13 @@ static AstStructType* parse_struct(OnyxParser* parser) {
 
     expect_token(parser, '}');
 
-    return s_node;
+    if (poly_struct != NULL) {
+        // NOTE: Not a StructType
+        return (AstStructType *) poly_struct;
+
+    } else {
+        return s_node;
+    }
 }
 
 // e
@@ -1810,7 +1869,9 @@ static AstNode* parse_top_level_statement(OnyxParser* parser) {
                 } else if (node->kind != Ast_Kind_Overloaded_Function
                         && node->kind != Ast_Kind_StrLit) {
 
-                    if (node->kind == Ast_Kind_Struct_Type || node->kind == Ast_Kind_Enum_Type) {
+                    if (node->kind == Ast_Kind_Struct_Type
+                            || node->kind == Ast_Kind_Enum_Type
+                            || node->kind == Ast_Kind_Poly_Struct_Type) {
                         ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
                             "%b", symbol->text, symbol->length);
                     }
index 9394f9c2e7640713a88afe20c7ae6d00d8f2bc4d..1c650f5b44f6dc0a7671a608d201842828f58c7f 100644 (file)
@@ -8,7 +8,7 @@
 static void scope_enter(Scope* new_scope);
 static void scope_leave();
 
-static AstType* symres_type(AstType* type);
+AstType* symres_type(AstType* type);
 static void symres_local(AstLocal** local);
 static void symres_call(AstCall* call);
 static void symres_size_of(AstSizeOf* so);
@@ -40,7 +40,7 @@ static void scope_leave() {
     semstate.curr_scope = semstate.curr_scope->parent;
 }
 
-static AstType* symres_type(AstType* type) {
+AstType* symres_type(AstType* type) {
     if (type == NULL) return NULL;
 
     if (type->kind == Ast_Kind_Type_Alias) {
@@ -151,6 +151,25 @@ static AstType* symres_type(AstType* type) {
         return type;
     }
 
+    if (type->kind == Ast_Kind_Poly_Struct_Type) {
+        AstPolyStructType* pst_node = (AstPolyStructType *) type;
+        pst_node->scope = scope_create(semstate.node_allocator, semstate.curr_scope);
+
+        return type;
+    }
+
+    if (type->kind == Ast_Kind_Poly_Call_Type) {
+        AstPolyCallType* pc_node = (AstPolyCallType *) type;
+
+        pc_node->callee = symres_type(pc_node->callee);
+
+        bh_arr_each(AstType *, param, pc_node->params) {
+            *param = symres_type(*param);
+        }
+
+        return type;
+    }
+
     return type;
 }
 
index 7b0ff8211c5d9e7782927955e5cc21dfc43f1741..3698df8f549ef5d84439d5b4b88902302613d2e2 100644 (file)
@@ -423,6 +423,32 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
         case Ast_Kind_Type_Raw_Alias:
             return ((AstTypeRawAlias *) type_node)->to;
 
+        case Ast_Kind_Poly_Struct_Type:
+            // @Cleanup: Replace this with a proper onyx_report_error. - brendanfh 2020/09/14
+            assert(("Polymorphic struct used without instantiation.", 0));
+            break;
+
+        case Ast_Kind_Poly_Call_Type: {
+            AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
+
+            // @Cleanup: make this a proper error message.  -brendanfh 2020/09/14
+            assert(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type);
+
+            AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
+
+            bh_arr(Type *) param_types = NULL;
+            bh_arr_new(global_heap_allocator, param_types, bh_arr_length(pc_type->params));
+            bh_arr_each(AstType *, ptype, pc_type->params) {
+                bh_arr_push(param_types, type_build_from_ast(alloc, *ptype));
+            }
+
+            AstStructType* concrete = polymorphic_struct_lookup(ps_type, param_types);
+
+            bh_arr_free(param_types);
+
+            return type_build_from_ast(alloc, (AstType *) concrete);
+        }
+
         case Ast_Kind_Symbol:
             assert(("symbol node in type expression", 0));
             return NULL;
index 38cfc3009504999ae461f0268e7d0395c5ea7e67..a19babb009d927a59f7ca9c5f2883d528f369c57 100644 (file)
@@ -49,6 +49,8 @@ static const char* ast_node_names[] = {
     "SLICE TYPE",
     "DYNARR TYPE",
     "STRUCT TYPE",
+    "POLYMORPHIC STRUCT TYPE",
+    "POLYMORPHIC STRUCT CALL TYPE",
     "ENUM TYPE",
     "TYPE_ALIAS",
     "TYPE RAW ALIAS"
@@ -428,6 +430,22 @@ static Type* solve_poly_type(AstNode* target, AstType* type_expr, Type* actual)
                 break;
             }
 
+            case Ast_Kind_Poly_Call_Type: {
+                if (elem.actual->kind != Type_Kind_Struct) break;
+                if (bh_arr_length(elem.actual->Struct.poly_args) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
+
+                AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
+
+                fori (i, 0, bh_arr_length(pt->params)) {
+                    bh_arr_push(elem_queue, ((PolySolveElem) {
+                        .type_expr = pt->params[i],
+                        .actual = elem.actual->Struct.poly_args[i],
+                    }));
+                }
+
+                break;
+            }
+
             default: break;
         }
     }
@@ -534,6 +552,68 @@ no_errors:
     return func;
 }
 
+
+
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params) {
+    // @Cleanup
+    assert(bh_arr_length(ps_type->poly_params) == bh_arr_length(params));
+    assert(ps_type->scope != NULL);
+
+    if (ps_type->concrete_structs == NULL) {
+        bh_table_init(global_heap_allocator, ps_type->concrete_structs, 16);
+    }
+
+    scope_clear(ps_type->scope);
+
+    fori (i, 0, bh_arr_length(ps_type->poly_params)) {
+        if (params[i] == NULL) {
+            onyx_report_error((OnyxFilePos) { 0 }, "Type parameter is not a type.");
+            return NULL;
+        }
+
+        AstTypeRawAlias* raw = onyx_ast_node_new(semstate.node_allocator, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+        raw->to = params[i];
+
+        symbol_introduce(ps_type->scope, ps_type->poly_params[i], (AstNode *) raw);
+    }
+
+    static char key_buf[1024];
+    fori (i, 0, 1024) key_buf[i] = 0;
+    bh_table_each_start(AstNode *, ps_type->scope->symbols);
+        strncat(key_buf, key, 1023);
+        strncat(key_buf, "=", 1023);
+        strncat(key_buf, type_get_name(((AstTypeRawAlias *) value)->to), 1023);
+        strncat(key_buf, ";", 1023);
+    bh_table_each_end;
+
+    if (bh_table_has(AstStructType *, ps_type->concrete_structs, key_buf)) {
+        return bh_table_get(AstStructType *, ps_type->concrete_structs, key_buf);
+    }
+
+    AstStructType* concrete_struct = (AstStructType *) ast_clone(semstate.node_allocator, ps_type->base_struct);
+
+    semstate.curr_scope = ps_type->scope;
+    concrete_struct = (AstStructType *) symres_type((AstType *) concrete_struct);
+    if (onyx_has_errors()) goto has_error;
+    goto no_errors;
+
+has_error:
+    // onyx_report_error((OnyxFilePos) { 0 }, "Error in polymorphic struct generated from this call site.");
+    return NULL;
+
+no_errors:
+    bh_table_put(AstStructType *, ps_type->concrete_structs, key_buf, concrete_struct);
+
+    Type* cs_type = type_build_from_ast(semstate.node_allocator, (AstType *) concrete_struct);
+
+    cs_type->Struct.poly_args = NULL;
+    bh_arr_new(global_heap_allocator, cs_type->Struct.poly_args, bh_arr_length(params));
+
+    fori (i, 0, bh_arr_length(params)) bh_arr_push(cs_type->Struct.poly_args, params[i]);
+
+    return concrete_struct;
+}
+
 i32 sort_entities(const void* e1, const void* e2) {
     return ((Entity *)e1)->type - ((Entity *)e2)->type;
 }