From 14578c369ad789afbf9a874f50e7a5fd3d6a0bfd Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 22 May 2023 19:22:30 -0500 Subject: [PATCH] added: polymorphic unions --- compiler/include/astnodes.h | 11 ++++ compiler/include/types.h | 6 +- compiler/src/checker.c | 33 +++++++++- compiler/src/clone.c | 47 +++++++++++++++ compiler/src/entities.c | 1 + compiler/src/parser.c | 50 ++++++++++++--- compiler/src/polymorph.h | 107 +++++++++++++++++++++++++++++---- compiler/src/symres.c | 36 ++++++----- compiler/src/types.c | 34 +++++++++-- compiler/src/wasm_type_table.h | 42 +++++++++++++ core/runtime/info/helper.onyx | 6 ++ core/runtime/info/types.onyx | 8 +++ tests/tagged_unions.onyx | 24 +++++++- 13 files changed, 359 insertions(+), 46 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index f5aab0af..b96266a5 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -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); diff --git a/compiler/include/types.h b/compiler/include/types.h index 29ce2659..190435f3 100644 --- a/compiler/include/types.h +++ b/compiler/include/types.h @@ -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; \ + }) \ diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 568e315a..8c002ab4 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -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; diff --git a/compiler/src/clone.c b/compiler/src/clone.c index fdbe7a8c..3ba0fc54 100644 --- a/compiler/src/clone.c +++ b/compiler/src/clone.c @@ -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; diff --git a/compiler/src/entities.c b/compiler/src/entities.c index f3c877dc..dc5e3ef8 100644 --- a/compiler/src/entities.c +++ b/compiler/src/entities.c @@ -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; diff --git a/compiler/src/parser.c b/compiler/src/parser.c index ad037020..11bfd107 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -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; diff --git a/compiler/src/polymorph.h b/compiler/src/polymorph.h index 440688e9..0fc8716b 100644 --- a/compiler/src/polymorph.h +++ b/compiler/src/polymorph.h @@ -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; +} diff --git a/compiler/src/symres.c b/compiler/src/symres.c index b00a609a..527b9be1 100644 --- a/compiler/src/symres.c +++ b/compiler/src/symres.c @@ -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; } diff --git a/compiler/src/types.c b/compiler/src/types.c index d9a265ac..9b6c04fa 100644 --- a/compiler/src/types.c +++ b/compiler/src/types.c @@ -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; diff --git a/compiler/src/wasm_type_table.h b/compiler/src/wasm_type_table.h index 53e6e83e..b177cb3c 100644 --- a/compiler/src/wasm_type_table.h +++ b/compiler/src/wasm_type_table.h @@ -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; + } } } diff --git a/core/runtime/info/helper.onyx b/core/runtime/info/helper.onyx index 79f93f2c..b7c921de 100644 --- a/core/runtime/info/helper.onyx +++ b/core/runtime/info/helper.onyx @@ -101,6 +101,12 @@ write_type_name :: (writer: &io.Writer, t: type_expr) { else do io.write_str(writer, ""); } + 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, ""); + } + case .Compound { comp := cast(&Type_Info_Compound) info; io.write_str(writer, "("); diff --git a/core/runtime/info/types.onyx b/core/runtime/info/types.onyx index 2c6ef18e..56ea5a5a 100644 --- a/core/runtime/info/types.onyx +++ b/core/runtime/info/types.onyx @@ -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. diff --git a/tests/tagged_unions.onyx b/tests/tagged_unions.onyx index 6b3a17a8..a5f24699 100644 --- a/tests/tagged_unions.onyx +++ b/tests/tagged_unions.onyx @@ -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; -- 2.25.1