added: polymorphic unions
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 May 2023 00:22:30 +0000 (19:22 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 May 2023 00:22:30 +0000 (19:22 -0500)
13 files changed:
compiler/include/astnodes.h
compiler/include/types.h
compiler/src/checker.c
compiler/src/clone.c
compiler/src/entities.c
compiler/src/parser.c
compiler/src/polymorph.h
compiler/src/symres.c
compiler/src/types.c
compiler/src/wasm_type_table.h
core/runtime/info/helper.onyx
core/runtime/info/types.onyx
tests/tagged_unions.onyx

index f5aab0afc4ecb71473b185834618c4c45f5dc478..b96266a5de0ea916270534a3fdc01594b9dfc07d 100644 (file)
@@ -1060,6 +1060,16 @@ struct AstUnionVariant {
     AstTyped_base;
     bh_arr(AstTyped *) meta_tags;
 };
+struct AstPolyUnionType {
+    AstType_base;
+    char *name;
+
+    Scope *scope;
+    bh_arr(AstPolyStructParam) poly_params;
+    Table(AstUnionType *) concrete_unions;
+
+    AstUnionType* base_union;
+};
 struct AstEnumType {
     AstType_base;
     char *name;
@@ -2026,6 +2036,7 @@ void expand_macro(AstCall** pcall, AstFunction* template);
 AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed);
 
 Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
+Type* polymorphic_union_lookup(AstPolyUnionType* pu_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
 
 b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint);
 
index 29ce2659dc05779495159f0efea9e781bccab04b..190435f3e9cfb4c9ce46818717cb2c5444a6a739 100644 (file)
@@ -172,7 +172,11 @@ typedef struct UnionVariant {
         bh_arr(struct AstPolySolution) poly_sln;                  \
         struct AstType *constructed_from;                         \
         bh_arr(struct AstTyped *) meta_tags;                      \
-    })
+    })                                                            \
+    TYPE_KIND(PolyUnion, struct {                                 \
+        char* name;                                               \
+        bh_arr(struct AstTyped *) meta_tags;                      \
+    })                                                            \
 
 
 
index 568e315aef00ebf41a13433cffeb29be0483b3e7..8c002ab4c697d2ebafb5f4c96e7790b856ded63d 100644 (file)
@@ -713,7 +713,8 @@ CheckStatus check_call(AstCall** pcall) {
 
     if (call->kind == Ast_Kind_Call) {
         AstNode* callee = strip_aliases((AstNode *) call->callee);
-        if (callee->kind == Ast_Kind_Poly_Struct_Type) {
+        if (callee->kind == Ast_Kind_Poly_Struct_Type ||
+            callee->kind == Ast_Kind_Poly_Union_Type) {
             *pcall = (AstCall *) convert_call_to_polycall(call);
             CHECK(expression, (AstTyped **) pcall);
             return Check_Success;
@@ -2155,7 +2156,8 @@ CheckStatus check_expression(AstTyped** pexpr) {
         expr = *pexpr;
 
         // Don't try to construct a polystruct ahead of time because you can't.
-        if (expr->kind != Ast_Kind_Poly_Struct_Type) {
+        if (expr->kind != Ast_Kind_Poly_Struct_Type &&
+            expr->kind != Ast_Kind_Poly_Union_Type) {
             if (type_build_from_ast(context.ast_alloc, (AstType*) expr) == NULL) {
                 YIELD(expr->token->pos, "Trying to construct type.");
             }
@@ -2702,7 +2704,8 @@ CheckStatus check_overloaded_function(AstOverloadedFunction* ofunc) {
     if (ofunc->expected_return_node) {
         AstType *expected_return_node = (AstType *) strip_aliases((AstNode *) ofunc->expected_return_node);
 
-        if (expected_return_node->kind == Ast_Kind_Poly_Struct_Type) {
+        if (expected_return_node->kind == Ast_Kind_Poly_Struct_Type ||
+            expected_return_node->kind == Ast_Kind_Poly_Union_Type) {
             //
             // When you declare the expected return type of a #match'ed procedure to
             // be a polymorphic structure type, a special case has to happen. By trying
@@ -2902,6 +2905,30 @@ CheckStatus check_struct_defaults(AstStructType* s_node) {
 }
 
 CheckStatus check_union(AstUnionType *u_node) {
+    if (u_node->polymorphic_argument_types) {
+        assert(u_node->polymorphic_arguments);
+
+        fori (i, 0, (i64) bh_arr_length(u_node->polymorphic_argument_types)) {
+            Type *arg_type = type_build_from_ast(context.ast_alloc, u_node->polymorphic_argument_types[i]);
+            if (arg_type == NULL) YIELD(u_node->polymorphic_argument_types[i]->token->pos, "Waiting to build type for polymorph argument.");
+
+            //
+            // This check should always be false, but it handles
+            // the case where somewhere a type was expected, but
+            // not enough values were provided. This is checked
+            // elsewhere when instantiating a polymorphic sturucture.
+            if (i >= bh_arr_length(u_node->polymorphic_arguments)
+                || !u_node->polymorphic_arguments[i].value) continue;
+
+            
+            TYPE_CHECK(&u_node->polymorphic_arguments[i].value, arg_type) {
+                ERROR_(u_node->polymorphic_arguments[i].value->token->pos, "Expected value of type %s, got %s.",
+                    type_get_name(arg_type),
+                    type_get_name(u_node->polymorphic_arguments[i].value->type));
+            }
+        }
+    }
+
     if (u_node->constraints.constraints) {
         u_node->constraints.produce_errors = (u_node->flags & Ast_Flag_Header_Check_No_Error) == 0;
 
index fdbe7a8c53069c7a83f9c48e4e3da314d7c385ba..3ba0fc54f8839a4478025780e611653908d8dc04 100644 (file)
@@ -119,6 +119,8 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_Import: return sizeof(AstImport);
         case Ast_Kind_Capture_Block: return sizeof(AstCaptureBlock);
         case Ast_Kind_Capture_Local: return sizeof(AstCaptureLocal);
+        case Ast_Kind_Union_Type: return sizeof(AstUnionType);
+        case Ast_Kind_Union_Variant: return sizeof(AstUnionVariant);
         case Ast_Kind_Count: return 0;
     }
 
@@ -406,6 +408,51 @@ AstNode* ast_clone(bh_allocator a, void* n) {
             break;
         }
 
+        case Ast_Kind_Union_Type: {
+            AstUnionType* du = (AstUnionType *) nn;
+            AstUnionType* su = (AstUnionType *) node;
+
+            du->variants = NULL;
+            bh_arr_new(global_heap_allocator, du->variants, bh_arr_length(su->variants));
+
+            bh_arr_each(AstUnionVariant *, uv, su->variants) {
+                bh_arr_push(du->variants, (AstUnionVariant *) ast_clone(a, *uv));
+            }
+
+            du->meta_tags = NULL;
+            bh_arr_new(global_heap_allocator, du->meta_tags, bh_arr_length(su->meta_tags));
+            bh_arr_each(AstTyped *, tag, su->meta_tags) {
+                bh_arr_push(du->meta_tags, (AstTyped *) ast_clone(a, *tag));
+            }
+            
+            if (su->constraints.constraints) {
+                memset(&du->constraints, 0, sizeof(ConstraintContext));
+                bh_arr_new(global_heap_allocator, du->constraints.constraints, bh_arr_length(su->constraints.constraints));
+
+                bh_arr_each(AstConstraint *, constraint, su->constraints.constraints) {
+                    bh_arr_push(du->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+                }
+            }
+
+            du->utcache = NULL;
+            break;
+        }
+
+        case Ast_Kind_Union_Variant: {
+            C(AstUnionVariant, type_node);
+
+            AstUnionVariant *du = (AstUnionVariant *) nn;
+            AstUnionVariant *su = (AstUnionVariant *) node;
+
+            du->meta_tags = NULL;
+            bh_arr_new(global_heap_allocator, du->meta_tags, bh_arr_length(su->meta_tags));
+            bh_arr_each(AstTyped *, tag, su->meta_tags) {
+                bh_arr_push(du->meta_tags, (AstTyped *) ast_clone(a, *tag));
+            }
+
+            break;
+        }
+
         case Ast_Kind_Poly_Call_Type: {
             AstPolyCallType* pcd = (AstPolyCallType *) nn;
             AstPolyCallType* pcs = (AstPolyCallType *) node;
index f3c877dc2cd47c93b738a2c9f9a2c0279317642a..dc5e3ef82e16719a724eae7f3bd7b9cce7e7a937 100644 (file)
@@ -292,6 +292,7 @@ void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* s
         }
 
         case Ast_Kind_Union_Type:
+        case Ast_Kind_Poly_Union_Type:
         case Ast_Kind_Poly_Struct_Type:
         case Ast_Kind_Type_Alias: {
             ent.type = Entity_Type_Type_Alias;
index ad0370200c7faaefc4b18da7b04599fd032104d8..11bfd107ff43fb7408b5feb2c27ccdb8ff0e78eb 100644 (file)
@@ -2323,6 +2323,8 @@ static AstUnionType* parse_union(OnyxParser* parser) {
     OnyxToken* union_token = expect_token(parser, Token_Type_Keyword_Union);
 
     AstUnionType* u_node;
+    AstPolyUnionType* poly_union = NULL;
+
     u_node = make_node(AstUnionType, Ast_Kind_Union_Type);
     u_node->token = union_token;
 
@@ -2332,6 +2334,36 @@ static AstUnionType* parse_union(OnyxParser* parser) {
     Scope *scope_to_restore_parser_to = parser->current_scope;
     Scope *scope_symbols_in_unions_should_be_bound_to = u_node->scope;
 
+    if (consume_token_if_next(parser, '(')) {
+        bh_arr(AstPolyStructParam) poly_params = NULL;
+        bh_arr_new(global_heap_allocator, poly_params, 1);
+
+        while (!consume_token_if_next(parser, ')')) {
+            if (parser->hit_unexpected_token) return NULL;
+
+            OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+            expect_token(parser, ':');
+
+            AstType* param_type = parse_type(parser);
+
+            bh_arr_push(poly_params, ((AstPolyStructParam) {
+                .token = sym_token,
+                .type_node = param_type,
+                .type = NULL,
+            }));
+
+            if (parser->curr->type != ')')
+                expect_token(parser, ',');
+        }
+
+        poly_union = make_node(AstPolyUnionType, Ast_Kind_Poly_Union_Type);
+        poly_union->token = union_token;
+        poly_union->poly_params = poly_params;
+        poly_union->base_union = u_node;
+        poly_union->scope = u_node->scope;
+        u_node->scope = NULL;
+    }
+
     // Parse constraints clause
     if (parser->curr->type == Token_Type_Keyword_Where) {
         parse_constraints(parser, &u_node->constraints);
@@ -2380,17 +2412,14 @@ static AstUnionType* parse_union(OnyxParser* parser) {
 
     parser->current_scope = scope_to_restore_parser_to;
 
-    ENTITY_SUBMIT(u_node);
-    return u_node;
+    if (poly_union != NULL) {
+        // NOTE: Not a UnionType
+        return (AstUnionType *) poly_union;
 
-    //if (poly_struct != NULL) {
-    //    // NOTE: Not a StructType
-    //    return (AstStructType *) poly_struct;
-
-    //} else {
-    //    ENTITY_SUBMIT(s_node);
-    //    return s_node;
-    //}
+    } else {
+        ENTITY_SUBMIT(u_node);
+        return u_node;
+    }
 }
 
 static AstInterface* parse_interface(OnyxParser* parser) {
@@ -3334,6 +3363,7 @@ static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol
         case Ast_Kind_Enum_Type:
         case Ast_Kind_Distinct_Type:
         case Ast_Kind_Union_Type:
+        case Ast_Kind_Poly_Union_Type:
             ((AstStructType *) node)->name = generate_name_within_scope(parser, symbol);
             goto default_case;
 
index 440688e92128772d28e9075ffb565c189d6e48df..0fc8716b1d2fc03f123c1895172e583e21171da2 100644 (file)
@@ -386,24 +386,29 @@ static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type
             }
 
             case Ast_Kind_Poly_Call_Type: {
-                if (elem.actual->kind != Type_Kind_Struct) break;
-                if (bh_arr_length(elem.actual->Struct.poly_sln) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
+                if (elem.actual->kind != Type_Kind_Struct && elem.actual->kind != Type_Kind_Union) break;
+
+                bh_arr(AstPolySolution) slns;
+                if (elem.actual->kind == Type_Kind_Struct) slns = elem.actual->Struct.poly_sln;
+                if (elem.actual->kind == Type_Kind_Union)  slns = elem.actual->Union.poly_sln;
+
+                if (bh_arr_length(slns) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
 
                 AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
 
                 fori (i, 0, bh_arr_length(pt->params)) {
-                    PolySolutionKind kind = elem.actual->Struct.poly_sln[i].kind;
+                    PolySolutionKind kind = slns[i].kind;
                     if (kind == PSK_Type) {
                         bh_arr_push(elem_queue, ((PolySolveElem) {
                             .kind = kind,
                             .type_expr = (AstType *) pt->params[i],
-                            .actual = elem.actual->Struct.poly_sln[i].type,
+                            .actual = slns[i].type,
                         }));
                     } else {
                         bh_arr_push(elem_queue, ((PolySolveElem) {
                             .kind = kind,
                             .type_expr = (AstType *) pt->params[i],
-                            .value = elem.actual->Struct.poly_sln[i].value,
+                            .value = slns[i].value,
                         }));
                     }
                 }
@@ -1053,24 +1058,29 @@ b32 potentially_convert_function_to_polyproc(AstFunction *func) {
 // The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
 // structures now have a delay instantiation phase and are not forced to be completed immediately.
 
-char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
+char* build_poly_struct_name(char *name, Type* type) {
     char name_buf[256];
     fori (i, 0, 256) name_buf[i] = 0;
 
 
     // Special case for `? T`
-    if (cs_type->Struct.constructed_from == builtin_optional_type) {
+    if (type->kind == Type_Kind_Struct
+        && type->Struct.constructed_from == builtin_optional_type) {
         strncat(name_buf, "? ", 255);
-        strncat(name_buf, type_get_name(cs_type->Struct.poly_sln[0].type), 255);
+        strncat(name_buf, type_get_name(type->Struct.poly_sln[0].type), 255);
 
         return bh_aprintf(global_heap_allocator, "%s", name_buf);
     }
 
+    bh_arr(AstPolySolution) slns = NULL;
+    if (type->kind == Type_Kind_Struct) slns = type->Struct.poly_sln;
+    if (type->kind == Type_Kind_Union)  slns = type->Union.poly_sln;
+
 
-    strncat(name_buf, ps_type->name, 255);
+    strncat(name_buf, name, 255);
     strncat(name_buf, "(", 255);
-    bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
-        if (ptype != cs_type->Struct.poly_sln)
+    bh_arr_each(AstPolySolution, ptype, slns) {
+        if (ptype != slns)
             strncat(name_buf, ", ", 255);
 
         // This logic will have to be other places as well.
@@ -1151,7 +1161,7 @@ Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySoluti
         cs_type->Struct.constructed_from = (AstType *) ps_type;
         
         if (cs_type->Struct.poly_sln == NULL) cs_type->Struct.poly_sln = bh_arr_copy(global_heap_allocator, slns);
-        if (cs_type->Struct.name == NULL)     cs_type->Struct.name = build_poly_struct_name(ps_type, cs_type);
+        if (cs_type->Struct.name == NULL)     cs_type->Struct.name = build_poly_struct_name(ps_type->name, cs_type);
 
         return cs_type;
     }
@@ -1178,3 +1188,76 @@ Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySoluti
     add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
     return NULL;
 }
+
+Type* polymorphic_union_lookup(AstPolyUnionType* pu_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed) {
+    if (pu_type->scope == NULL) {
+        return NULL;
+    }
+
+    assert(!pu_type->base_union->scope);
+
+    if (pu_type->concrete_unions == NULL) {
+        sh_new_arena(pu_type->concrete_unions);
+    }
+
+    if (bh_arr_length(slns) != bh_arr_length(pu_type->poly_params)) {
+        onyx_report_error(pos, Error_Critical, "Wrong number of arguments for '%s'. Expected %d, got %d.",
+            pu_type->name,
+            bh_arr_length(pu_type->poly_params),
+            bh_arr_length(slns));
+
+        return NULL;
+    }
+
+    i32 i = 0;
+    bh_arr_each(AstPolySolution, sln, slns) {
+        sln->poly_sym = (AstNode *) &pu_type->poly_params[i];
+        i++;
+    }
+
+    char* unique_key = build_poly_slns_unique_key(slns);
+    i32 index = shgeti(pu_type->concrete_unions, unique_key);
+    if (index != -1) {
+        AstUnionType* concrete_union = pu_type->concrete_unions[index].value;
+
+        if (concrete_union->entity->state < Entity_State_Check_Types) {
+            return NULL;
+        }
+
+        if (concrete_union->entity->state == Entity_State_Failed) {
+            return (Type *) &node_that_signals_failure;
+        }
+
+        Type* cu_type = type_build_from_ast(context.ast_alloc, (AstType *) concrete_union);
+        if (!cu_type) return NULL;
+
+        cu_type->Union.constructed_from = (AstType *) pu_type;
+        
+        if (cu_type->Union.poly_sln == NULL) cu_type->Union.poly_sln = bh_arr_copy(global_heap_allocator, slns);
+        cu_type->Union.name = build_poly_struct_name(pu_type->name, cu_type);
+
+        return cu_type;
+    }
+
+    Scope* sln_scope = scope_create(context.ast_alloc, pu_type->scope, pu_type->token->pos);
+    insert_poly_slns_into_scope(sln_scope, slns);
+
+    AstUnionType* concrete_union = (AstUnionType *) ast_clone(context.ast_alloc, pu_type->base_union);
+    concrete_union->scope = scope_create(context.ast_alloc, sln_scope, pu_type->token->pos);
+    concrete_union->polymorphic_error_loc = pos;
+    BH_MASK_SET(concrete_union->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
+
+    i64 arg_count = bh_arr_length(pu_type->poly_params);
+    bh_arr_new(global_heap_allocator, concrete_union->polymorphic_argument_types, arg_count);
+    bh_arr_set_length(concrete_union->polymorphic_argument_types, arg_count);
+    concrete_union->polymorphic_arguments = bh_arr_copy(global_heap_allocator, slns);
+    concrete_union->name = pu_type->name;
+
+    fori (i, 0, (i64) bh_arr_length(pu_type->poly_params)) {
+        concrete_union->polymorphic_argument_types[i] = (AstType *) ast_clone(context.ast_alloc, pu_type->poly_params[i].type_node);
+    }
+
+    shput(pu_type->concrete_unions, unique_key, concrete_union);
+    add_entities_for_node(NULL, (AstNode *) concrete_union, sln_scope, NULL);
+    return NULL;
+}
index b00a609a64f042026d501f3f79f2a94fb80d71bf..527b9be1472e5c6a3afad4c7437fc7a590c21342 100644 (file)
@@ -172,20 +172,20 @@ static SymresStatus symres_union_type(AstUnionType* u_node) {
     assert(u_node->scope);
     scope_enter(u_node->scope);
     
-    // if (u_node->polymorphic_argument_types) {
-    //     assert(u_node->polymorphic_arguments);
+    if (u_node->polymorphic_argument_types) {
+        assert(u_node->polymorphic_arguments);
 
-    //     SymresStatus ss = Symres_Success, result;
-    //     fori (i, 0, (i64) bh_arr_length(u_node->polymorphic_argument_types)) {
-    //         result = symres_type(&u_node->polymorphic_argument_types[i]);
-    //         if (result > ss) ss = result;
+        SymresStatus ss = Symres_Success, result;
+        fori (i, 0, (i64) bh_arr_length(u_node->polymorphic_argument_types)) {
+            result = symres_type(&u_node->polymorphic_argument_types[i]);
+            if (result > ss) ss = result;
 
-    //         if (u_node->polymorphic_arguments[i].value) {
-    //             result = symres_expression(&u_node->polymorphic_arguments[i].value);
-    //             if (result > ss) ss = result;
-    //         }
-    //     }
-    // }
+            if (u_node->polymorphic_arguments[i].value) {
+                result = symres_expression(&u_node->polymorphic_arguments[i].value);
+                if (result > ss) ss = result;
+            }
+        }
+    }
 
     if (u_node->constraints.constraints) {
         bh_arr_each(AstConstraint *, constraint, u_node->constraints.constraints) {
@@ -260,6 +260,12 @@ static SymresStatus symres_type(AstType** type) {
             break;
         }
 
+        case Ast_Kind_Poly_Union_Type: {
+            AstPolyUnionType* put_node = (AstPolyUnionType *) *type;
+            assert(put_node->scope);
+            break;
+        }
+
         case Ast_Kind_Poly_Call_Type: {
             AstPolyCallType* pc_node = (AstPolyCallType *) *type;
 
@@ -330,7 +336,8 @@ static SymresStatus symres_call(AstCall** pcall) {
     SYMRES(arguments, &call->args);
 
     AstNode* callee = strip_aliases((AstNode *) call->callee);
-    if (callee->kind == Ast_Kind_Poly_Struct_Type) {
+    if (callee->kind == Ast_Kind_Poly_Struct_Type ||
+        callee->kind == Ast_Kind_Poly_Union_Type) {
         *pcall = (AstCall *) convert_call_to_polycall(call);
         SYMRES(type, (AstType **) pcall);
         return Symres_Success;
@@ -366,7 +373,8 @@ static SymresStatus symres_field_access(AstFieldAccess** fa) {
         expr->kind == Ast_Kind_Poly_Struct_Type ||
         expr->kind == Ast_Kind_Enum_Type ||
         expr->kind == Ast_Kind_Type_Raw_Alias ||
-        expr->kind == Ast_Kind_Union_Type) {
+        expr->kind == Ast_Kind_Union_Type ||
+        expr->kind == Ast_Kind_Poly_Union_Type) {
 
         force_a_lookup = 1;
     }
index d9a265ac964cb9534fffd7e184d78728561253f9..9b6c04fa35682e292ac8f7c1e54f378df268c7c3 100644 (file)
@@ -567,11 +567,27 @@ static Type* type_build_from_ast_inner(bh_allocator alloc, AstType* type_node, b
             return NULL;
         }
 
+        case Ast_Kind_Poly_Union_Type: {
+            if (type_node->type_id != 0) return NULL;
+
+            Type* p_type = type_create(Type_Kind_PolyUnion, alloc, 0);
+            p_type->ast_type = type_node;
+            p_type->PolyUnion.name = ((AstPolyUnionType *) type_node)->name;
+            p_type->PolyUnion.meta_tags = ((AstPolyUnionType *) type_node)->base_union->meta_tags;
+
+            type_register(p_type);
+            return NULL;
+        }
+
         case Ast_Kind_Poly_Call_Type: {
             AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
             pc_type->callee = (AstType *) strip_aliases((AstNode *) pc_type->callee);
 
-            if (!(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type)) {
+            if (!(pc_type->callee && (
+                    pc_type->callee->kind == Ast_Kind_Poly_Struct_Type ||
+                    pc_type->callee->kind == Ast_Kind_Poly_Union_Type
+                ))) {
+
                 // If it is an unresolved field access or symbol, just return because an error will be printed elsewhere.
                 if (pc_type->callee->kind == Ast_Kind_Field_Access || pc_type->callee->kind == Ast_Kind_Symbol) return NULL;
 
@@ -580,8 +596,6 @@ static Type* type_build_from_ast_inner(bh_allocator alloc, AstType* type_node, b
                 return NULL;
             }
 
-            AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
-
             bh_arr(AstPolySolution) slns = NULL;
             bh_arr_new(global_heap_allocator, slns, bh_arr_length(pc_type->params));
             bh_arr_each(AstNode *, given, pc_type->params) {
@@ -603,7 +617,15 @@ static Type* type_build_from_ast_inner(bh_allocator alloc, AstType* type_node, b
                 }
             }
 
-            Type* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
+            Type* concrete = NULL;
+            if (pc_type->callee->kind == Ast_Kind_Poly_Struct_Type) {
+                AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
+                concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
+            }
+            else if (pc_type->callee->kind == Ast_Kind_Poly_Union_Type) {
+                AstPolyUnionType* pu_type = (AstPolyUnionType *) pc_type->callee;
+                concrete = polymorphic_union_lookup(pu_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
+            }
 
             // This should be copied in the previous function.
             // CLEANUP: Maybe don't copy it and just use this one since it is allocated on the heap?
@@ -611,7 +633,6 @@ static Type* type_build_from_ast_inner(bh_allocator alloc, AstType* type_node, b
 
             if (!concrete) return NULL;
             if (concrete == (Type *) &node_that_signals_failure) return concrete;
-            concrete->Struct.constructed_from = (AstType *) ps_type;
             pc_type->resolved_type = concrete;
             return concrete;
         }
@@ -1267,6 +1288,9 @@ const char* type_get_name(Type* type) {
         case Type_Kind_PolyStruct:
             return type->PolyStruct.name;
 
+        case Type_Kind_PolyUnion:
+            return type->PolyUnion.name;
+
         case Type_Kind_Struct:
             if (type->Struct.name)
                 return type->Struct.name;
index 53e6e83e83ef0d09f03861fddc88850bdd0adeda..b177cb3c3b6bc0be89bc163d23e0c9a4abe0c9a8 100644 (file)
@@ -731,6 +731,48 @@ static u64 build_type_table(OnyxWasmModule* module) {
                 bh_arr_free(method_data);
                 break;
             }
+
+            case Type_Kind_PolyUnion: {
+                u32* tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(type->PolyUnion.meta_tags));
+                memset(tag_locations, 0, sizeof(u32) * bh_arr_length(type->PolyUnion.meta_tags));
+
+                u32 name_base = table_buffer.length;
+                u32 name_length = strlen(type->PolyUnion.name);
+                bh_buffer_append(&table_buffer, type->PolyUnion.name, name_length);
+
+                u32 tags_count = bh_arr_length(type->PolyUnion.meta_tags);
+                i32 i = 0;
+                bh_arr_each(AstTyped *, tag, type->PolyUnion.meta_tags) {
+                    AstTyped* value = *tag;                        
+
+                    tag_locations[i] = build_constexpr(value, &table_buffer, &constexpr_ctx);
+                    if (tag_locations[i] == 0) {
+                        // Polymorphic structs are weird in this case, because the tag might not be constructed generically for
+                        // the polymorphic structure so it should only be constructed for actual solidified structures.
+                        // See core/containers/map.onyx with Custom_Format for an example.
+                        tags_count--;
+                    } else {
+                        i++;
+                    }
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 tags_base = table_buffer.length;
+
+                fori (i, 0, tags_count) {
+                    WRITE_SLICE(tag_locations[i], type->PolyUnion.meta_tags[i]->type->id);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, 0);
+                bh_buffer_write_u32(&table_buffer, 0);
+                WRITE_SLICE(name_base, name_length);
+                WRITE_SLICE(tags_base, tags_count);
+
+                break;
+            }
         }
     }
 
index 79f93f2c584e7b5e7ab3265da658d0b1c56b59ee..b7c921de9b98ea11d0f6ab5b24f27088a44abba2 100644 (file)
@@ -101,6 +101,12 @@ write_type_name :: (writer: &io.Writer, t: type_expr) {
             else                do io.write_str(writer, "<anonymous union>");
         }
 
+        case .Polymorphic_Union {
+            u := cast(&Type_Info_Polymorphic_Union) info;
+            if u.name.count > 0 do io.write_str(writer, u.name);
+            else                do io.write_str(writer, "<anonymous polymorphic union>");
+        }
+
         case .Compound {
             comp := cast(&Type_Info_Compound) info;
             io.write_str(writer, "(");
index 2c6ef18ee4c536f42b696a9a762309754e833ec1..56ea5a5a8de723480c8b2ad078f7915ccde315d1 100644 (file)
@@ -23,6 +23,7 @@ Type_Info :: struct {
         Enum               :: 0x0C;
         Distinct           :: 0x0D;
         Union              :: 0x0E;
+        Polymorphic_Union  :: 0x0F;
     }
 
     kind := Kind.Invalid;
@@ -214,6 +215,13 @@ Type_Info_Union :: struct {
     methods: [] Method;
 }
 
+Type_Info_Polymorphic_Union :: struct {
+    use base : Type_Info;
+
+    name: str;
+    tags: [] any;
+}
+
 get_type_info :: (t: type_expr) -> &Type_Info {
     // Grossness to get around the fact that type_exprs are not technically comparable, because in most
     // cases you should not compare them as the number assigned to them is arbitrary.
index 6b3a17a82ad2083c6982eb37a3150060fc47a080..a5f24699f469cad4b5850dadf1629e819f0f1016 100644 (file)
@@ -1,6 +1,28 @@
 
 use core {*}
 
+NewOptional :: union (T: type_expr) {
+    None: void;
+    Some: T;
+}
+
+unwrap_optional :: (o: NewOptional($T)) -> T {
+    switch o {
+        case .Some => v do return v;
+        case .None do return .{};
+    }
+}
+
+new_optional_test :: () {
+    v := NewOptional(i32).{ Some = 123 };
+    v2 := NewOptional(str).{ None = .{} };
+    println(v);
+    println(v2);
+
+    v = .{ None = .{} };
+    println(unwrap_optional(v));
+}
+
 union_is :: macro (u: $U, $variant: U.tag_enum) -> bool {
     return cast(U.tag_enum, u) == variant;
 }
@@ -74,7 +96,7 @@ simple_test :: () {
     println(u);
 }
 
-main :: () {simple_test(); link_test();}
+main :: () {simple_test(); link_test(); new_optional_test();}
 
 Link :: union {
     End: void;